先说结论,再展开分析。
git的文件分为四种类型,分别是:blob,tree,commit,tag。
blob:blob类型文件,用于存储用户的文件内容。比如有个a.txt文件内容是“aaa”,那么git把内容快照进.git隐藏文件夹中,生成blob文件,内容也是“aaa”。
tree:tree类型的文件,用于记录文件夹下的信息,假设文件夹下有blob文件,那么就记录blob文件名字。假设还有子文件夹,也记录子文件夹。
commit:commit类型文件,记录的是提交信息。比如提交者是谁,关联tree,上一次提交是哪个commit文件。通过此commit文件能找到下面的tree及blob。
总的来说,blob类型记录用户文件内容,tree类型记录用户文件夹,commit类型作为Merkle树的头节点。

以上是一次提交所产生的文件,每次提交就会产生一个Merkle树,树中记录文件夹及文件内容,而提交与提交之间的关系,是链表。现在再往git提交两次新的代码,流程如下:

分支指向最新的提交,通过最新的提交,能找到已提交过的所有文件夹和文件。以下,我将在linux环境使用git操作一次。
1.创建一个测试用的文件夹testgit。

2.进入文件夹并创建一个仓库。

3.创建AAA文件夹及子文件A.txt,写入内容AContent.

4.添加AAA文件夹

5.添加文件夹后,我们去看看git生成了什么东西。查看隐藏.git文件夹。并进入.git查看git所有文件结构。


我们发现objects文件夹下有一个fc6b...文件。
6.查看fc6b...文件的类型及内容。(不能直接打开,因为是二进制文件,需要使用git cat-file)

他是blob类型文件,内容是AContent,正是刚刚我们提交的A.txt内容。也就是说,当我们git add时候,git产生一个快照,把文件内容复制进来这里了。
7.嗯,我们继续把AAA文件夹提交吧。看看.git会产生什么文件。

8.回到.git文件夹,显示所有文件。

9.我们已经知道fc6b存储是的A.txt的内容AContent。那么新产生的三个文件分别是什么呢,我们一个个打开看过究竟。




请按照四张图的顺序往下看,注意观察文件所保存的内容:
1.commit文件47e7存储tree文件2027.
2.tree文件2027存储tree文件cce8.
3.tree文件eec8存储blob文件fc6b.
4.blob文件fc6b是A.txt内容。
和我们创建的目录很相似吧。testgit -> AAA -> A.txt -> txt内容AContent。
10.我们再来看以下当前的master指向谁。

指向的47e7,正是上面的commit类型文件。验证了此图:

11.最后,如果你想验证每次提交的链表。那么你根据上面所述再提交一次,并查看最新提交commit类型文件,你会发现他有一个parent项,记录的是上一个commit文件。此时,master记录的也是最新的commit类型文件。
总结:关于拉分支,分支之间的合并,回滚,Head分离等等操作,同样是居于这个链的。比如分支之间合并吧,每条分支有一条链,将两条链上的节点合并(文件合并),产生新的节点,再把新节点提交就完成分支合并了。题外话,为什么几种类型的文件名字很长呢?其实用了Merkle tree,会把子文件求hash码作为文件名字,如下图:

Author : SunnyDecember
Date : 2020.1.12
原文