想要了解如何存储,首先需要知道存储位置。
当我们通过 git init 创建 git 仓库时,会创建.git 目录,.git 的目录结构如下:
├─hooks
├─info
├─logs
│ └─refs
│ ├─heads
│ └─remotes
│ └─origin
├─objects
│ ├─07
│ ├─13
│ ├─2b
│ ├─2d
│ ├─3b
│ ├─5a
│ ├─5e
│ ├─7e
│ ├─94
│ ├─fa
└─refs
├─heads
├─remotes
│ └─origin
└─tags
其中 objects 目录中存储了所有的 git 对象,也是直接涉及数据文件存储的目录,其他目录在此不做讨论。
那么,想要了解 objects 目录中如何存储文件,就需要首先了解 git 的数据模型。
git 数据模型分为三种:
通过伪代码来认识这三个对象
type blob = array
type tree = map
type commit = struct {
parent: array
author: string
message: string
snapshot: tree
}
使用我本地的一次提交为例,分析一下这三种对象的效果。
分析过程主要使用到两条命令。
git 中提供了 git cat-file 用来查看 git 对象,分析时主要使用的参数有:
git 还有 git log 可以查看提交记录,快速找到 commit 对象。
git 在存储文件/目录之前,会首先根据文件/目录计算 40 位哈希值。其中:
git 存储信息时以该哈希值做索引,而不是文件名。
哈希值通过 SHA-1 计算得出。





通过上述过程,我们大体可以得出以下结论:
以下图为例,数据库代表 commit 对象,目录代表 tree 对象,文件代表blob 对象。该图代表的场景为:

从中可以看到,当第二次提交时, commit 对象(commit2)下创建一个新的 tree 对象(tree2)。对于第二次提交而言,tree1-1并未改动,因此 tree2 直接使用指针指向原有地址,blob1发生变动,则生成一个新的 blob 对象(blob2),并让 tree2 指向它。
完成上述操作后,commit2 对象包含了当前仓库的所有信息,这也就是当前时刻的 snapshot。
对三者做一个简易对比。