松散对象的载荷不是孤立的——commit
指向一棵 tree,tree 条目指向
blob 或子 tree;tag 再指向
commit(或其它对象)。分支名 refs/heads/master
只是指向某个 commit ID
的别名。理解这条链,才能把「一次提交」翻译成磁盘上 3
个(或更多)松散文件。
本文用最小仓库的 git cat-file -p
实测输出,说明 tree/commit/tag 的文本格式与对象图拓扑。blob
编码见 松散对象。
一、最小仓库的对象图
一次提交、单文件 hello.txt
时,对象库中有三个松散对象(尚未 repack):
commit 2b24b26a… → tree aaa96ced… → blob ce013625… (hello.txt)
二、commit 对象
git cat-file -p HEAD(以下输出经删减,作者信息随环境变化):
tree aaa96ced2d9a1c8e72c56b253a0e2fe78393feb7
author … <…> 1783189662 +0800
committer … <…> 1783189662 +0800
init
字段含义(gitformat-commit):
tree:根目录快照的 tree 对象 ID(必需)parent:父提交 ID(初始提交无此行)author/committer:姓名、邮箱、Unix 时间、时区- 空行后是提交说明
commit 对象本身也是 blob
式存储:commit <size>\0 + 上述文本,再
zlib、SHA-1。
三、tree 对象
git cat-file -p aaa96ced2d9a1c8e72c56b253a0e2fe78393feb7:
100644 blob ce013625030ba8dba906f756967f9e9ca394464a hello.txt
每行:模式、类型、对象
ID、文件名(显示时以制表符分隔;磁盘载荷在
NUL 前为二进制 SHA)。
常见模式:
| mode | 含义 |
|---|---|
100644 |
普通文件 |
100755 |
可执行文件 |
040000 |
子目录(条目类型为 tree) |
160000 |
gitlink(子模块,第 15 篇) |
120000 |
符号链接 |
子目录通过嵌套 tree 表达:父 tree 条目指向子 tree 的 SHA,子 tree 再列其条目。
四、多文件与多级目录
添加 src/a.txt 后,tree 可能变为:
100644 blob … hello.txt
040000 tree … src
子 tree src 内:
100644 blob … a.txt
一次 git commit
若修改多个路径,会生成新的 tree
链(可能复用未变子 tree 的既有对象 ID),再生成新
commit。未变 blob 不会重复存储——内容寻址去重。
五、tag 对象
轻量标签:refs/tags/v1.0
直接写 commit SHA,不创建 tag 对象。
附注标签(annotated tag):创建 tag
对象,载荷含
object、type、tag、tagger、说明文字;refs/tags/v1.0
指向 tag 对象 ID。git pack-refs 可在
packed-refs 用 ^ 标记 peeled
commit(第
04 篇)。
六、parent 链与合并
普通提交至多一个 parent 行。合并提交有两个
parent 行,分别指向合并前两个分支 tip。对象图从
DAG(有向无环图)变成「合并节点两入边」——仍无环,但拓扑更复杂。合并时
.git 临时文件见 第
13 篇。
七、本文边界
- 不讨论 merge 三方树如何生成(命令语义见 Pro Git)。
- 不展开 pack 内对象引用(第 07 篇)。
参考资料
- Git documentation, gitformat-commit
- Git documentation, gitformat-tag
- 实验环境:Git 2.54.0
系列索引:Git 内部结构 · 上篇:松散对象 · 下篇:refs 与 packed-refs
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【Git 内部】refs、HEAD 与 packed-refs
分支和标签在磁盘上只是一行 SHA?refs/heads、符号引用 HEAD、packed-refs 文件头与 peeled tag 的格式与生成时机。
【Git 内部】日常 porcelain 命令改写了哪些文件
add、commit、branch、checkout、reset 各触及 .git 的哪些路径?用分步 find 快照对照 index、objects、refs、logs 的变化。
【Git 内部】.git 目录全景:三棵树与仓库布局
git init 之后 .git 里每个路径干什么?对照 gitrepository-layout 与本地 find 清单,建立 bare/non-bare、三棵树、objects/refs/index 的磁盘级地图。
【Git 内部】松散对象:zlib 载荷与 SHA-1 路径
blob/tree/commit 在磁盘上如何编码?对象头 type size、zlib 压缩、objects/ab/cdef 路径命名,对照 git hash-object 与手工 SHA-1 验证。