本文介绍Git库的基础数据结构,这是Git得以运转的基础。Git库的基础数据结构根据作用主要可分为“支持存放数据信息的”,“支持存放配置信息的”和“支持存放索引信息的”这3大类。详细描述见表1。
表1
基础数据结构作用 | 基础数据结构名称 | 基础数据结构存放路径 |
---|---|---|
支持存放数据信息的 | Object Store和Pack File | .git/objects目录 |
支持存放配置信息的 | 配置文件 | .git/config文件 |
支持存放索引信息的 | 索引文件 | .git/index文件 |
一、Object Store
1.1、概念
Object Store内存储有Git库内所有数据文件和所有修改操作记录等数据,根据这些数据可获得Git库任意版本的快照。Object Store内所有数据文件可分为“Blob”,“Tree”,“Commit”和“Tag”这4种类型,文件名由40个十六进制字符构成,具体是对文件内容应用SHA1算法而获得。
Object Store内数据文件具体存放路径为“.git/objects目录”,取文件名前两个十六进制字符创建子目录,防止直接平铺文件数量过多。
1.1.1、Blob
Blob类型文件存储Git库内数据文件的具体内容,Git库内不同数据文件和数据文件的不同版本对应于不同的Blob类型文件(一个数据文件的两个版本即便只有一点修改,仍会以“两个独立的Blob类型文件”形式存在,而不是“一个独立的Blob类型文件,另外一个差异内容文件”形式)。
Blob类型文件不能引用其他类型文件,只能被Tree类型文件引用。
1.1.2、Tree
Tree类型文件内有0到多行记录,每行记录或指向一个Blob类型文件,或指向另外一个Tree类型文件。选取某个Tree类型文件为树的根节点,向下遍历,可得到文件系统内相应的一棵“目录-文件层次树”。
Tree类型文件能够引用Tree类型或者Blob类型文件,且能够被Tree类型或者Commit类型文件引用。
1.1.3、Commit
Commit类型文件保存修改操作记录,每次提交修改操作记录时都会创建一个新的Commit类型文件。Commit类型文件(除了初始Commit类型文件)会以上一次提交修改操作记录时创建的Commit类型文件为父Commit类型文件,同时会引用一个Tree类型文件。以该Tree类型文件为树的根节点进行遍历,得到的相应的“目录-文件层次树”即为Git库某个版本的完整快照。
Commit类型文件能够以Commit类型文件为父Commit类型文件,能够引用Tree类型文件,且能够被Tag类型文件引用。
1.1.4、Tag
Tag类型文件保存相关联的其他类型文件的描述信息,比如说“版本号”,这个其他类型文件一般为“Commit类型文件”。
Tag类型文件要么不存在,如果存在,则引用一个Commit类型文件。
1.2、实例
1.2.1、增加操作
执行如下命令:
1 | # Commit类型文件包含提交时间,因此,Commit类型文件文件名很大可能跟图示不一致 |
读取Object Store内数据文件的内容,可得到如图1所示引用关系图。
图1
1.2.2、删除操作
执行如下命令:
1 | # Commit类型文件包含提交时间,因此,Commit类型文件文件名很大可能跟图示不一致 |
读取Object Store内数据文件的内容,可得到如图2所示引用关系图。
图2
1.2.3、修改操作
执行如下命令:
1 | # Commit类型文件包含提交时间,因此,Commit类型文件文件名很大可能跟图示不一致 |
读取Object Store内数据文件的内容,可得到如图3所示引用关系图。
图3
二、Pack File
为了节省空间,Git会将Object Store内数据文件的内容使用zlib库进行压缩(也因此,不能直接查看这些数据文件的内容)。而为了更加压缩存储空间,Git又提供了Pack File机制,该机制会将Object Store内数据文件重新整合起来,以特定格式进行组织,达到再次压缩的目的。
Pack File机制产出文件的具体存放路径为“.git/objects/pack目录”和“.git/objects/info目录”。
触发Pack File机制的途径有:Object Store内含有过多数据文件,push
本地Git库到远端Git库,执行git gc
命令。
接下来进行举例说明,在上面的描述中,同一个文件的不同版本即便只有一点修改,也会以“两个独立的Blob类型文件”的形式保存,有些时候,这会显得浪费空间。在这种情形下,触发Pack File机制,可以大大减少空间的浪费,以下是具体的实验步骤。
1、执行以下命令,将原“repo.rb”和新“repo.rb”文件提交到Git库:
1 | git init |
此时Object Store内容如下:
1 | .git/objects |
经过查找发现原“repo.rb”和新“repo.rb”文件对应的Blob类型文件文件名分别为“033b4468fa6b2a9547a70d88d1bbe8bf3f9ed0d5”和“587438a7ab34b34ca57e2da92ecf7b79c8bb4fb0”,分别执行git cat-file -s 033b4468fa6b2a9547a70d88d1bbe8bf3f9ed0d5
和git cat-file -s 587438a7ab34b34ca57e2da92ecf7b79c8bb4fb0
命令,可知“033b4468fa6b2a9547a70d88d1bbe8bf3f9ed0d5”和“587438a7ab34b34ca57e2da92ecf7b79c8bb4fb0”文件大小分别为“22044”和“22056”字节
2、执行/usr/bin/du --block-size=1 -s .git/objects
命令,可知此时Object Store内数据文件总大小为“69632”字节
3、执行git gc
命令触发Pack File机制,此时Object Store内容如下:
1 | .git/objects |
4、执行git verify-pack -v .git/objects/pack/pack-72421cdfd34c79982edc4882bd4febc308e5ae11.idx
命令,可知“587438a7ab34b34ca57e2da92ecf7b79c8bb4fb0”文件大小为“22056”字节,“033b4468fa6b2a9547a70d88d1bbe8bf3f9ed0d5”文件引用“587438a7ab34b34ca57e2da92ecf7b79c8bb4fb0”,而其自身大小为“9”字节
5、执行/usr/bin/du --block-size=1 -s .git/objects
命令,可知此时Object Store内数据文件总大小为“28672”字节
三、配置文件
Git库的本地配置文件为“.git/config文件”,但是,参数配置不单只有该途径。参数配置按照优先级从低到高顺序为:“/etc/gitconfig文件”,“~/.gitconfig文件”,“.git/config文件”,“环境变量”和“命令行参数配置”。
四、索引文件
对Git库的操作(增加,删除,修改等)暂时被保存在索引文件中,只在git commit
命令执行后,才被真正提交到Git库。
参考文献: [1]https://git-scm.com/book/be/v2/Git-Internals-Packfiles