松散对象适合少量写入;历史一长,.git/objects/
下成千上万个小文件会带来 inode 与目录压力(参见 小文件问题)。packfile
把多个对象串进单个 .pack,用
.idx 按 SHA 查偏移。传输
fetch/push 时传的也是 pack(第 14
篇)。
本文依据 gitformat-pack
说明 pack/idx 布局,并用 git verify-pack -v
解读实测输出。
一、文件对
| 文件 | 作用 |
|---|---|
objects/pack/pack-<hash>.pack |
连续存放的对象数据 + pack 尾部校验 |
objects/pack/pack-<hash>.idx |
按对象 SHA 排序的索引,指向 pack 内偏移 |
objects/info/packs |
列出仓库使用的 pack 文件 |
*.rev(可选) |
reverse index,加速 reachability(较新版本) |
pack 文件名中的 hash 为 pack 文件内容的 SHA-1(不含 idx)。
二、pack 文件结构(概念)
- 头部:签名
PACK、版本(通常为 2)、对象个数。 - 对象序列:每个对象带类型与长度编码,后跟 zlib 压缩数据或 delta 指令。
- 尾部:整个 pack 的 SHA-1 校验和。
对象类型(3 bit 分类,见官方文档):
| 类型 | 含义 |
|---|---|
| commit / tree / blob / tag | 与松散对象相同语义 |
| OFS_DELTA | 以 pack 内负偏移引用基对象 |
| REF_DELTA | 以 20 字节 SHA 引用基对象 |
三、verify-pack 实测
对仅含一次提交的小仓库执行 git repack -ad
后:
git verify-pack -v .git/objects/pack/*.pack输出(以下经删减):
2b24b26a117ef5cfdbf88040e282ec86085e6b1f commit 228 145 12
ce013625030ba8dba906f756967f9e9ca394464a blob 6 15 157
aaa96ced2d9a1c8e72c56b253a0e2fe78393feb7 tree 37 48 172
non delta: 3 objects
列含义:SHA type
uncompressed-size compressed-size
offset-in-pack。三者均为 non
delta——小仓库无相似对象可链。
git count-objects -v 同期:
in-pack: 3
packs: 1
size-pack: 1
松散对象计数为 0 表示已全部进 pack(若未 prune 可能仍有冗余松散文件,见 第 10 篇)。
四、idx v2 概要
idx 使 git cat-file 能在 pack 内 O(log N)
定位对象:
- fanout 表:256 个桶,按 SHA 首字节累计计数。
- SHA 列表:排序的 20 字节 ID。
- CRC32:每个对象的校验。
- offset 表:对应 pack 内字节偏移。
读取对象时:SHA → idx 查 offset → pack 解压。REF_DELTA 需再解析基对象(可能在同一 pack 内更早偏移)。
五、与松散对象的语义等价
pack 是物理编码;对象 ID
仍是对「头+载荷」的哈希,与松散存储一致。git cat-file -p <sha>
无需关心对象来自松散还是 pack。
六、本文边界
参考资料
- Git documentation, gitformat-pack
- 实验环境:Git 2.54.0
系列索引:Git 内部结构 · 上篇:index · 下篇:pack-objects 与 delta
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【Git 内部】Git 内部结构:对象库与磁盘文件格式
从 .git 目录布局、松散对象与 packfile 格式,到 refs、index、reflog、gc/fsck 与 fetch/push 落地——用官方 format 文档与本地实测 dump 系统讲清 Git 把版本历史存在哪、每个命令改写了哪些文件。全 16 篇。
【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 验证。
【Git 内部】对象图:tree、commit、tag 的链式结构
一次提交在对象库里如何连成链?tree 条目 mode/name/hash、commit 的 tree/parent 字段、annotated tag,用 git cat-file -p 展开并画对象图。