一文了解git底层原理

一、git相关区域

工作区(Working Directory): 工作区是平时编写文本文件的地方

暂存区(Stage/Index): 暂存区是提交文本文件到本地仓库的来源地,只有把工作区的文件添加至暂存区,才可以被提交至本地仓库。 (git add)

本地仓库(Repository): 本地仓库是保存每次文件更新的记录,包括提交人,提交时间,提交的内容等详细信息,方便追溯历史版本。(git commit)

远程仓库(Remote Repository):远程仓库算是本地仓库的一个副本,主要是方便合作伙伴之间的仓库文件同步。

二、git各类对象

blob:

     blob对象只跟文本文件的内容有关,和文本文件的名称及目录无关,只要是相同的文本文件,会指向同一个blob。

tree:

     tree对象记录文本文件内容和名称、目录等信息,每次提交都会生成一个顶层tree对象,它可以指向其引用的tree或blob。

commit:

     commit对象记录本次提交的所有信息,包括提交人、提交时间,本次提交包含的tree及blob。

tag:

     标签引用,它指向某一个commit。

三、验证git中三大对象commit、tree和blog之间的关系

1. 本地初始化一个git仓库

$ git init gittest

执行后 ls -a 可查看到.git目录。

2. 在.git同级目录下创建doc/readme.txt文件,内容为 hello world 

   执行 ls -a 查看到文件已新增

$ ls -a.git        doc/readme.txt

3. 在还没有进行add的前提下,使用命令 git status 查看当前仓库的状态,可以看到创建的文件夹和文件还没有添加到暂存区,所以接着使用命令:find .git/objects -type f 看不到有什么结果。

$ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    doc/
$ find .git/objects -type f

4. git add 后,使用上述命令结果如下:

$ git status 
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

    new file:   doc/readme.txt
$ find .git/objects -type f
.git/objects/3b/18e512dba79e4c8300dd$ git cat-file -t 3b18e512dba79e4c8300blob$ git cat-file -p 3b18e512dba79e4c8300hello world
 

使用 git cat-file -t ID号  查看object的类型,由上可知该object为blog,说明新的东西加入到暂存区,git会主动把暂存区的东西创建为blog。使用命令:git cat-file -p ID号  查看该blog的内容,可以得到内容就是readme中的内容。

5. 执行如下命令commit该新增文件

$ git commit -m "add readme"

再通过 find .git/objects -type f 可查看到四个object对象

$ find .git/objects -type f
.git/objects/3b/18e512dba79e4c8300dd
.git/objects/e7/a75e980c7adc0d5ab1c4
.git/objects/2e/9e6cd9fef232a6f0ecab
.git/objects/7a/802cad5138b242c5ee6b

$ git cat-file -t 3b18e512dba79e4c8300
blob

$ git cat-file -p 3b18e512dba79e4c8300
hello world

$ git cat-file -t e7a75e980c7adc0d5ab1
tree

$ git cat-file -p e7a75e980c7adc0d5ab1
100644 blob 3b18e512dba79e4c8300dd    readme.txt

$ git cat-file -t 2e9e6cd9fef232a6f0ec
tree

$ git cat-file -p 2e9e6cd9fef232a6f0ec
040000 tree e7a75e980c7adc0d5ab1    doc

$ git cat-file -t 7a802cad5138b242c5ee
commit

$ git cat-file -p 7a802cad5138b242c5ee
tree 2e9e6cd9fef232a6f0ecab
author test <> 1591054691 +0800
committer test <> 1591054691 +0800

由上可以得出,一个commit里面含有一个tree(这次commit时期包含的文件内容)。一个tree下面确实包含有tree和blog。 

进入到某一具体的blog进行查看,验证是否一个blog代表一个文件内容(进入到具体的blog下,有的blog能直接解析打开,如html,txt等,有的不能,如图片等)。如下可以直接看到该blog所对应的readme.txt的文件内容。

6. 在.git 同级目录下新建second.txt文件,commit后得到3个新object如下:

$ git commit -m "second commit"
[master b0da7df] second commit
 1 file changed, 1 insertion(+)
 create mode 100644 second.txt

$ find .git/objects -type f
.git/objects/0c/e52f0e38439ed9833307
.git/objects/3b/18e512dba79e4c8300dd
.git/objects/b0/da7dfb5c1f09e5c0b618
.git/objects/e7/a75e980c7adc0d5ab1c4
.git/objects/e0/19be006cf33489e2d017
.git/objects/2e/9e6cd9fef232a6f0ecab
.git/objects/7a/802cad5138b242c5ee6b

$ git cat-file -t 0ce52f0e38439ed983330d7
tree

$ git cat-file -p 0ce52f0e38439ed983330d7
040000 tree e7a75e980c7adc0d5ab1    doc
100644 blob e019be006cf33489e2d0    second.txt

$ git cat-file -t b0da7dfb5c1f09e5c0b618 
commit

$ git cat-file -p b0da7dfb5c1f09e5c0b618
tree 0ce52f0e38439ed98333077
parent 7a802cad5138b242c5ee
author test <> 1591016659 +0800
committer test <> 1591016659 +0800

second commit

$ git cat-file -t e019be006cf33489e2d0
blob

$ git cat-file -p e019be006cf33489e2d0
second

当执行 git commit -m "second commit" 的时候就会在.git/objects/下新生成了1个tree对象和一个commit对象的文件夹(前两位为文件夹名称 -- 后38位为文本内容的哈希值)。对应上述的0ce52f0e38439ed98333077(tree对象), b0da7dfb5c1f09e5c0b61835e0ecc87877d0e853(commit对象)。

只生成一个与commit对象一一对应的顶层tree对象。由于本次提交doc目录下的readme.txt内容没有变化,所以上图的0ce52f0e38439ed98333077(tree对象)还会指向e7a75e980c7adc0d5ab1(tree对象)。这个时候HEAD游标指向的是当前master分支的second commit(HEAD索引向前移动),second commit 会指向上一次的提交即 parent指向first commit。

整个原理图如下:

一文了解git底层原理

注意: blob对象只对文件的内容有关,和文件名称无关,如果不同的文件名称,内容相同只会有一个blob对象,生成的新tree对象会指向该blob对象。例如third commit和four commit的内容一样,所以不会生成新的blob对象,新的tree对象只会指向同一个blob。

 

 

相关推荐