0 快照
相信了解Git的朋友,在阅读Git相关资料的时候,一定会读到:Git记录文件的快照(snapshot),而不是差异的部分。
到底什么是快照?
《Git Pro》中英文原文如下:
Every time you commit, or save the state of your project in Git, it basically takes a picture of what all your files look like at that moment and stores a reference to that snapshot.
每当读者在Git中提交或保存你的工程的状态的时候,总的来说,Git为那一刻,所有你的文件的状态的做了一份“快照”并存储了一个引用到那份“快照”。
读到这里,请读者先在脑海中有一个模糊的观念:快照就是一段程序的输出结果;在提交或保存文件状态时,Git输入当前文件的状态,输出一个快照。
引用《Git Pro》中的图片,我们先来宏观体验一下。
比如我当前处于Version 1时刻,此时修改了文件A、C,修改后的文件分别记为A1、C1,我们进入到了Version 2时刻。
对于Git来说,只要两个文件的SHA-1不同,则这两个文件就是两个文件。因此,Git发现A1与A的SHA-1不同,B的相同,C的不同。
当我们提交或保存工作工程状态的时候,Git便把此时A、B、C三个文件复制到.git的隐藏文件夹中的某个目录,以Git的方式编码存储起来,并记录好引用到A1,B,C1的相关信息等,以便日后找到。
此时Git的版本库中,A有修改,因此有A和A1两个文件;B并没有被修改,因此B存储的只有一份;C有修改,因此有C和C1两个文件;共5个工程文件。
上述,便是Git记录文件快照的过程;上述提到的相关信息便是广义上的快照。
1、实验
1.1 实验过程
让我们做个实验体验一下。
实验环境:Windows 10,Git版本2.18.0
我们在目录下面新建一个test.txt的文件,里面输入内容“This is a test.”。
然后初始化目录为git仓库。
我们发现,出现了一个隐藏文件夹.git。我们点开来,发现如下结构:
这里我们主要关心objects文件夹,点开来看,现在有两个文件夹,info和pack。
我们再开一个窗口,然后把刚刚建的test.txt提交到暂存区,看看objects下面发生了什么。
objects文件夹下面多了一个文件夹叫27,打开来,里面有以一串以字母和数字为名字的文件。
紧接着,我们提交test.txt,执行
$ git commit --all -m "first commit"
我们发现,又多了两个文件夹b7和ba。打开来看,依旧是有一个以一串字母和数字为名字的文件。
至此,一个文件,被git成功拍了一张快照,记录在册。
1.2 实验解析
objects下面的文件夹的名字有何意义?为什么一次提交会新建三个文件夹?
我参考《Git Pro》中的讲解,为大家解析。
Git每次提交,会生成三类东西:
- 一个提交,commit
- 一个树,tree
- 一些文件的快照,blob
他们三者相关关系如下图
上图只是示意图,只列出了Git存储文档时关键的内容。
关于blob
我们发现,当我们执行$ git add <file>…时,生成了一个27文件夹,里面只有一个文件,这便是Git进行快照的方式——Git使用blob类型的数据结构存储这些快照,并计算SHA-1值,SHA-1值的前两个字符作为文件夹名称,后续作为文件名称。
关于tree
当我们提交这次修改的时候,Git生成了一个tree数据结构,用来管理本次提交涉及到的所有文件。因为实验只涉及到一个文件,因此tree中的内容只有一个。Git把tree保存成文件,保存成文件的方法和保存文件快照的方法一样——生成SHA-1值,前两个字符作为文件夹名称,后续作为文件名称,便生成了本次实验的b7文件夹。
关于commit
然后才是Git针对本次的提交记录,一个commit数据结构。commit记录了一些本次提交的重要内容,如提交者、作者、附注等,最重要的就是指向tree的指针tree和指向父提交的指针parent。指向tree的指针,保证本次提交的所有快照可以被找到;指向父提交的指针,保证了分支功能。同样的,将这些内容保存成文件,生成SHA-1值,前两个字符作为文件夹名称,后续作为文件名称,便生成了ba文件夹。
可以利用$ git log 查看
log中显示的SHA-1正好是文件夹ba与里面文件e9176e…连接起来的内容。
当我们不关注tree和blob,只关注提交时,将每次的提交,以结点网络图的形式连接起来,便变成下面的样子。
在进一步,抽象成结点。
这看起来,就像一条分支了。
1.3 结论
至此,我们可以得出如下结论:
- 广义上来讲,一个snapshot指的就是用当前文件的状态形成的一些相关信息。
- 微观上讲,一个snapshot指的是一次提交中的tree结构,tree中记录了每个文件的blob。
以上,便是Git存储文档的方式。