温故而知新(一)— 执行git操作时,git到底做了什么?

开发人员每天都在使用git,但git原理是什么?.git文件夹下到底记录了哪些内容?git命令执行后,git到底做了些什么?今天来一起梳理下吧。

一、Git是什么

聊聊Git,git我们在项目开发中,几乎每天都能接触到,它是一个分布式的版本控制系统,优点如下:

  1. 引入快照概念:当git提交信息时,会对当前提交的全部文件、包括目录结构创建快照,并保留快照的索引;而并非记录单个文件的更改信息。
  2. 基本所有操作都在本地执行:git提供的工作区,使开发者能够在本地对代码进行修改,而不需要依赖网络环境。
  3. 完整性:git通过SHA-1散列算法,对git的文件目录和文件统一计算hash值,在每次提交存储前,git都会对该hash值进行比对,如果文件有丢失,git很容易发现。
  4. git提供了工作区、暂存区、仓库的概念,通常从仓库拉取分支到本地工作区,工作区操作文件后,将文件提交到暂存区,暂存区的内容会批量性的提交到远程仓库。分别对应的状态为已修改、已暂存、已提交。

二、模拟git文件变化

2.1 文件变化时,.git文件里记录了什么

  • git init git init命令,在当前项目根目录下创建.git文件夹,用于保存版本信息,该文件夹下。文件夹的结构如下图所示
  • git hash-object -w 文件名

在项目中创建一个mainPage.vue的文件,同时对该文件使用hash-object命令,此时可查看,该文件内容已被压缩成为二进制文件,该压缩后的文件,保存在.git/objects下,称为Git对象 ;计算的hash值前两位,作为目录名称,其他作为文件名。

  • git cat-file -p 二进制编码

查看二进制文件内容,使用cat-file命令,其中文件名称是计算出的完整hash码。

当我们更改mainPage.vue的内容之后,再次对该文件执行git hash-object命令,可看到新的Git对象已生成,让我们查看.git/objects文件夹下的目录变化,可发现,新的文件目录和文件已生成。

2.2 变化的文件,如何在暂存区记录

  • git update-index --add mainPage.vue
  • git ls-files --stage

当前暂存区保存的是第二次变化的文件,可以根据该命令查找到要提交的Git对象文件。当前的mainPage.vue文件对应的二进制文件名称,可以在.git/objects/a2/文件夹下找到

2.3 暂存区文件,如何生成一个commit对象

  • git write-tree 保存当前文件的目录结构,即记录文件所在目录关系。这里会为当前文件所在的每个目录都生成一个Git对象,同样保存在.git/objects目录下。mainPage.vue保存在src/views文件下,执行完当前命令后,可查看.objects下生成了三个目录。分别为(1e,2a,93

查看每个文件夹对应的文件内容

到目前为止,git就为我们保存了一个目录结构,该目录结构上保存了当前文件夹地下的文件二进制对象,当前的目录结构和目录中的文件对象也就称作文件快照。这个目录结构在git中生成的对象是一个树结构对象

现在,我们需要将目录树对象写入到版本历史,在这一步,会生成一个commit对象。可以看到,目前在objects目录下,多生成了一个d5的文件夹,让我们来查看下d5下包含了哪些内容。

可以看到,当前目录中包含了上一次提交的tree对象信息,还有作者、提交者和提交说明信息。

好了,到目前为止,可以为我们前面这块工作做一个总结了。

  1. 当执行git add时,git主要完成两个步骤,1、保存对象,对变化的文件生成一个git对象。同时将它保存在objects目录下。2、将文件写入/更新暂存区,以通知git哪些文件需要提交,即执行git add时,objects目录下生成了当前文件的hashcode值。
  2. 当执行git commit时,git主要完成,1、为文件目录结构和文件生成一棵树,git将该树生成一个git对象,保存在objects中。2、生成一个commit对象,该commit对象记录了树对象的值,以及提交信息。

那么快照是什么:

个人理解,在git执行commit时,会对暂存区内的内容进行一次备份,该内容可以理解为生成的commit对象,该对象中包含了文件的目录结构、文件原始快照信息,以及文件的提交信息。对应上面的分析,应该就是.git目录下的d5文件夹下的二进制内容。可以看出,git在备份时,保留的是文件的完整信息,而非某个文件的差异信息,在提交时,暂存区的内容也不会随着提交而消失,因此可以使用git log命令,查看历史提交内容,暂存区的内容更像是保存了当前待提交文件的状态。

三、与远程仓库关联,发生了什么?

3.1 关联远程仓库

目前在本地创建的文件都保存在了暂存区,下一步需要推送到远程分支,推送到远程分支前,需要创建远程仓库,同时对将本地git与远程仓库进行管理。 使用命令

git remote add origin https://gitlab.**.**.cn/test3/monorop.git

3.2 同步远程仓库信息

关联之后,需要将远程分支生成的内容同步到本地,我们执行 git pull origin master操作,这步执行完后,可观察到我们的objects文件中,同时多了三个文件夹,分别记录了文件远程分支的文件blob信息、远程分支的目录结构、远程分支的commit对象信息。

查看各个文件夹目录下的内容,可看到其记录的都是远程分支上的信息。

3.3 提交文件到远程仓库

紧接着,将当前本地变更,提交到远程分支上。执行git commit操作,看下发生了什么。 可以看到objects中增加了两个文件夹,一个文件夹记录了本地与远程分支的tree对象信息,查看其记录数据,可分析出来,每次在更改代码时,git会为我们保存一次git object对象,以便记录最新的变更。

而另一个文件夹中,则记录的是当前指针指向的commit对象,该commit对象中除了记录当前的tree对象指针和提交作者人员信息之外,还记录了一个指向上次提交对象(父对象)的指针,该父对象指针,记录了完整的上一次快照信息,因此git通过这种模式,记录了完整的信息变化,而不仅仅是当前变化文件的信息,因此不会存在丢失数据问题。

当有信息变更时,commit对象上保存的什么:

当git分支上有内容变更时,会生成一个最新的commit对象,该commit对象中,除了保存本次修改的tree对象信息、提交信息之外,还记录了上一次的parent对象信息。这个parent对象信息,记录了完整的上一次操作,因此git生成的commit对象,通常都是完整的历史记录信息。

四、git分支

我们在项目中,经常会让创建新的分支,那分分支的具体是什么呢?分支其实指的就是指向commit对象的指针,分支名称也叫做当前的指针名。比如上面示例中的master初始分支,其实代表的就是指向最新commit对象的指针名为master。

4.1 创建新分支

我们基于某个分支创建新分支时,如基于master分支创建了feature-xyl分支,可在本地使用命令git branch feature-xyl。执行完成,git会为当前所在的commit对象创建一个叫做feature-xyl的指针,指向当前commit对象。

4.2 记录分支

当创建的分支越来越多时,git如何知道当前在哪个分支上呢,git提供了一个名叫HEAD 的指针,它总是指向当前所在的本地分支 ,比如我们创建了feature-xyl分支,但没切换到这个分支上时,此时head指向的还是master分支。 使用命令git log --oneline --decorate可查看。下图有两层含义,第一表明了head目前指向的当前所在本地分支。第二表明master、origin/master、feature-xyl都指向同一个commit对象。

4.3 修改分支内容,查看HEAD指针情况

当我们在本地分支上,比如feature-xyl分支上修改了内容,提交之后,查看当前的HEAD指针指向,可以看到当前HEAD指针,指向的feature-xyl上最新的commit对象上。根据回溯,可以发现当前的提交内容,正好是修改内容。

切换回master分支,可以看到HEAD指向的是master分支上最新的commit对象。

HEAD指向:

HEAD是git提供的一个特殊指针,总是指向当前本地分支的最新commit对象,并会随着分支的切换,自动变化。其保存在.git/refs/heads/目录下。由于每个分支指向的最新commit对象不同,因此我们在切换分支时对应的内容通常也是不一样的。

这篇文章主要记录了git中,文件blob对象、tree对象、commit对象的生成方式及在.git文件夹下的表示形式,同时通过和远程仓库关联,演示了分支不同变化下,上述三个对象分别有哪些变化,最后介绍了HEAD指针的指向形式,相信通过这篇文章,会给大家提供一个很清晰的视角去理解git命令后的内容。下篇文章,会继续围绕这篇内容,讲解其他命令后的故事。

相关推荐
Ten peaches22 分钟前
Selenium-Java版(环境安装)
java·前端·selenium·自动化
Enti7c24 分钟前
BOM知识点
javascript
心.c34 分钟前
vue3大事件项目
前端·javascript·vue.js
姜 萌@cnblogs43 分钟前
【实战】深入浅出 Rust 并发:RwLock 与 Mutex 在 Tauri 项目中的实践
前端·ai·rust·tauri
蓝天白云下遛狗1 小时前
google-Chrome常用插件
前端·chrome
兔子坨坨1 小时前
pycharm连接github(详细步骤)
windows·git·学习·pycharm·github
多多*1 小时前
Spring之Bean的初始化 Bean的生命周期 全站式解析
java·开发语言·前端·数据库·后端·spring·servlet
linweidong2 小时前
在企业级应用中,你如何构建一个全面的前端测试策略,包括单元测试、集成测试、端到端测试
前端·selenium·单元测试·集成测试·前端面试·mocha·前端面经
满怀10152 小时前
【HTML 全栈进阶】从语义化到现代 Web 开发实战
前端·html
繁依Fanyi2 小时前
用 UniApp 构建习惯打卡 App —— HabitLoop 开发记
javascript·uni-app·codebuddy首席试玩官