Git底层原理

2017-12-21  本文已影响31人  伊凡的一天

    git本质上是一个内容寻址的文件系统,并在此基础上提供了一个版本控制系统的界面。

    一个已经初始化后的git目录结构如图:

git目录结构

其中我们需要重点关注以下四个文件:

1. HEAD:保存了当前被检出的分支

2. objects:存储所有的数据内容

3. index:存储暂存区信息

4. refs:存储指向commit对象的指针


1. Git对象

    objects目录下存储了git中所有的数据对象,其目录结构如下:

objects                                                                                                                                    0d                                                                             7f012a2181ab54c95b4078674da7b31b580bff                                                              1c                                                                                     c469fe42468071e2b1823650fb0a3206985281

    objects目录中存储的对象拥有一个40位的id,其中前2位作为文件夹名,后38位作为文件名,文件内容即为该对象的值。git中拥有四种对象类型:blob,tree,commit和tag。

1.1 blob对象

    blob对象存储了用户工作区域内的文件内容。当执行git add命令时,objects下会生成新的blob对象,保存了add命令的文件内容。

    使用git cat-file命令可以从指定的对象中取得数据,加上-p选项

    git构造一个对象时,首先添加一个header,header以对象类型开头。下面的例子时git构造了一个内容为字符串"blob",类型为blob的对象。header首先以blob开头,然后添加一个空格,随后是数据内容的长度,最后是一个空字节。因此一个blob对象包含一个blob header和原始数据内容,其形式如下:

>> header = "blob #{content.length}\0"

=> "blob 16\u0000"

    Git会将上述header和原始数据内容为这个blob对象计算出SHA-1校验和。此SHA-1校验和就是前文所说的40位长度的对象id。然后git根据此id值确定此对象的存储位置(前2位作为文件夹,后38位作为文件名),使用zlib压缩此blob对象然后存储到相应位置。

    commit对象,tree对象的文件header则以"commit"和"tree"开头。

1.2 tree对象

    tree对象,顾名思义是一棵树,它的每个节点被称为tree entry。每个tree entry有一个指向blob对象或子树对象的SHA-1指针,以及相应的模式,类型,文件名信息等。例如,某个tree对象的信息如下:

tree对象

    每一行代表此tree对象的一个节点,即tree entry。第一列代表文件模式,其中040000表示这是一个目录,而100644表示这时一个普通文件。第二列表示此节点的对象类型。第三类是此节点的对象SHA-1值,通过此SHA-1值来定位数据内容,可以通过git cat-file -p <SHA-1>来查看。第四列则代表了文件名。因此,此tree对象的树形结构图如下:

tree对象

tree对象同blob对象一样,拥有一个SHA-1值,并且通过SHA-1值来确定文件位置,并将添加了header的tree对象内容写入到objects目录下。

1.3 commit对象

    一个commit对象保存了一次commit的信息,包括作者,提交注释,并且指向父commit对象和一个tree对象。这个tree对象是当前项目快照。如图所示:

commit对象

2. 再谈git文件夹结构

2.1 index文件

    index文件实际上就代表了git中暂存区。它存储了一个tree对象,表示当前项目的快照。当运行git add命令时,首先在objects目录下生成一个新的blob对象,然后更新index文件中的tree对象。

    当运行git status命令时,git会对比工作区和暂存区的文件,具体流程如下:

1. 首先比较文件的时间戳和长度,如果相同则认为文件没有改变。

2. 如果发现时间戳不同,则比较文件内容,如果内容相同,则将工作区文件的时间戳更新到index文件(暂存区)中。

3. 如果工作区文件和暂存区文件内容不相同,则提示有changes

    当运行git commit命令时,首先使用index文件中tree对象在objects目录下创建一个tree对象,然后在objects目录下创建一个commit对象,commit对象指向此tree对象以及父commit对象,最后更新.git/refs/heads/master(commit时的分支名)文件内容为此commit的SHA-1值。

2.2 refs文件夹

    refs文件夹结构如下:

refs文件夹结构

    其中子文件夹heads目录为每一个分支创建了一个文件,文件中只保存了在该分支下最近一次提交的commit id。

refs/heads文件夹结构

    打开master文件,内容只是一个commit的SHA-1值。

master文件内容

2.3 HEAD文件

    HEAD文件指向当前活跃的分支。打开HEAD文件,文件内容如下:

HEAD文件内容

    可以看到文件内容是本地的某一个代表了feture2分支的文件,而该分支文件的内容是最近的一次commit id。因此,我们常用的命令诸如 git reset HEAD等命令中出现的HEAD,能够正确的定位到具体的commit。

上一篇下一篇

猜你喜欢

热点阅读