Git 内部结构:对象库与磁盘文件格式
git commit 之后,工作区文件看似没变,但
.git
里已经多出了对象、引用和日志。中文资料多讲命令用法,少有一份把「磁盘上到底长了什么」按官方
format 规范讲透的系统性资料。本系列填补这个空白:以
gitrepository-layout
与 gitformat-* 文档为 A
级主来源,每章用本地仓库 + hexdump /
git cat-file / git verify-pack
等真实执行钉住结论。
系列定位:格式规范级拆解,不是 Git 命令教程。如果你只想学
rebase -i,Pro Git 前九章足够;如果你想搞懂 clone 之后.git里每个路径的职责、pack delta 在字节里怎么编码、git gc会删掉哪些松散对象——这是为你写的。
推荐入口
- .git
目录全景(第 01
篇):bare/non-bare、
HEAD/objects/refs/index职责划分与三棵树心智模型——读懂后续所有格式的入口。 - 松散对象格式(第
02 篇):
blob 6\0hello\n如何 zlib 压缩后落到objects/ab/cdef...。 - pack 与
idx 格式(第 07
篇):
.pack/.idx条目类型、OFS_DELTA/REF_DELTA 在git verify-pack里长什么样。 - 日常命令的文件副作用(第
12 篇):add/commit/branch/checkout/reset 各改写
.git的哪些路径。
一、这个系列要回答的五个问题
.git里每个目录/文件职责是什么?工作区、索引、HEAD 与对象库如何对应? 三棵树不是比喻——它们分别对应工作区文件、.git/index和HEAD指向的 commit tree。bare 仓库没有工作区和 index,但对象库与 refs 布局一致。 → 第 1、2、3 篇。分支、标签、HEAD、reflog 在磁盘上长什么样?
packed-refs何时出现? 分支是refs/heads/<name>里 40 字节(或 64 字节 SHA-256)的十六进制串;reflog 是logs/下的追加式二进制日志,与 reflog 命令读的是同一份文件。 → 第 4、5 篇。松散对象与 packfile 的格式边界在哪?delta 压缩如何体现在字节布局里? 松散对象 = zlib(ASCII 头 + 载荷);pack 把多个对象串进单个文件,相似 blob 可链成 REF_DELTA。
git repack后松散文件可被 prune。 → 第 2、7、8 篇。git gc/repack/fsck会改写或删除哪些路径?损坏通常表现为什么?gc触发 repack、写info/packs、可选 prune 悬空松散对象;fsck能检出坏 zlib、哈希不匹配与悬空对象,修复边界因损坏类型而异。 → 第 10、11 篇。commit/merge/fetch/push 各自触发了哪些文件变化?SHA-256 与 reftable 会改变什么? merge 冲突时 index 出现 stage 2/3 条目,
.git根下出现MERGE_HEAD;fetch 接收 pack 写入objects/pack/并更新refs/remotes/;SHA-256 仓库对象 ID 为 64 十六进制字符,路径仍为objects/xx/...。 → 第 12–14、16 篇。
二、篇目依赖关系与推荐阅读路径
全系列六部分、16 篇。版本锚定 Git 2.45+(本站实验环境 Git 2.54.0);SHA-256 / reftable 在第 16 篇单列并标注实验性边界。
强依赖图
flowchart TD
A["01 .git layout"] --> B["02 loose objects"]
B --> C["03 object graph"]
A --> D["04 refs / packed-refs"]
D --> E["05 reflog"]
A --> F["06 index"]
B --> G["07 pack + idx"]
G --> H["08 pack-objects delta"]
G --> I["09 commit-graph / bitmap"]
H --> J["10 gc / repack"]
B --> K["11 fsck"]
D --> L["12 porcelain effects"]
C --> M["13 merge / rebase files"]
G --> N["14 fetch / push"]
A --> O["15 worktree / submodule"]
B --> P["16 SHA-256 / reftable"]
推荐阅读路径
只想搞懂
.git里有什么01 → 02 → 04 → 06 → 07排查仓库损坏 / 空间异常
02 → 07 → 10 → 11,按需补05理解 fetch/clone 后多了什么文件
01 → 07 → 14 → 10写工具或读对象库(非教程用户)
02 → 03 → 06 → 07 → 08 → 09跟进 Git 存储演进
02 → 16(前置07)
三、目录与每篇一句话价值
第一部分:磁盘全景与对象基座
.git 目录全景:三棵树与仓库布局
git init后默认文件清单、bare 与 non-bare 差异、objects/refs/hooks/info各目录职责——建立后续逐字节读格式的地图。松散对象:zlib 载荷与 SHA-1 路径 对象头
type size\0、zlib 压缩、objects/ab/cdef...命名规则;git hash-object与手工计算 SHA-1 的对照。对象图:tree、commit、tag 的链式结构 tree 条目
mode name hash、commit 的tree parent author字段、annotated tag 如何指向 commit——从git cat-file -p展开到 Mermaid 对象图。
第二部分:引用与暂存区
refs、HEAD 与 packed-refs
refs/heads|tags|remotes文本格式、符号引用、git pack-refs后的packed-refs文件头与 peeled tag 行。reflog:logs/ 下的追加式历史 每条 reflog 记录的旧/新 SHA、提交者、时间戳、消息;
git reflog与hexdump -C logs/HEAD的一一对应。index 暂存区:dircache v2 与扩展节
DIRC魔数、条目 stat/SHA/stage、冲突时的 stage 2/3;TREE/REUC等扩展节的角色。
第三部分:Pack 与可达性加速
pack 与 idx 文件格式 pack 头
PACK、对象类型字节、OFS_DELTA/REF_DELTA 偏移;idx v2 扇区与git verify-pack -v输出字段含义。pack-objects 与 delta 压缩 相似 blob 如何链成长度为 1 的 delta 链;thin pack 与
git repack窗口启发式(格式视角,不拆 C 源码)。commit-graph 与 reachability bitmap
objects/info/commit-graph的 generation number;pack 旁路的.bitmap文件如何加速git rev-list(描述机制,不编造未实测倍率)。
第四部分:维护与完整性
gc、repack 与 prune
git gc触发的 repack、info/packs清单、--prune=now删除可达 pack 之外的松散对象;git count-objects -v各字段含义。fsck:校验规则与损坏形态 坏 zlib、哈希不匹配、悬空对象、
dangling commit与lost-found的修复边界——含故意截断松散对象的实测fsck输出。
第五部分:命令与协议的文件级落地
日常 porcelain 命令改写了哪些文件 add→index、commit→objects+refs+logs、checkout→HEAD/index、reset 三种模式各自触及的路径快照对比。
merge/rebase 的临时文件与 index stage
MERGE_HEAD、ORIG_HEAD、CHERRY_PICK_HEAD、MERGE_MSG;冲突时git ls-files -u的 stage 1/2/3。fetch/push 与 pack 传输落地 smart 协议边界上 pack 如何写入
objects/pack/;FETCH_HEAD、refs/remotes/、shallow文件与浅克隆的关系。
第六部分:扩展与演进
worktree、submodule 与 alternates 链接工作树的
gitdir:指针、.git/worktrees/清单、.git/modules/子模块对象库、info/alternates共享对象。SHA-256 对象格式与 reftable 演进
git init --object-format=sha256下 64 字符对象 ID;reftable 相对packed-refs的设计定位与当前默认边界。
四、与其他系列的关联
| 本站已有系列 | 与本系列的关系 |
|---|---|
| 存储工程 | 小文件问题、页缓存、fsync——解释松散对象膨胀与 pack 的工程动机;建议先读 小文件问题 |
| 开源之道 | 分布式协作与许可证语境;本系列补对象库磁盘语义 |
| 架构百科 IaC 篇 | Git 作状态存储时的文件级心智模型 |
五、承诺与不承诺
承诺:
- 关键格式结论锚定 Git 官方
gitformat-*/gitrepository-layout文档 - 命令输出与十六进制 dump 来自真实执行(Git 2.54.0,WSL2 Linux);删减处标注
- 每篇说明「本文不展开」的边界,避免与命令教程重复
不承诺:
- 不替代 Pro Git 或
git help作为命令参考 - 不覆盖 GitHub/GitLab 托管平台内部实现
- 不对 pack bitmap / commit-graph 给出未实测的性能倍率
- reftable 以设计文档与边界为主,不写「已全面替代 packed-refs」
延伸阅读
- Pro Git, Git Internals(B 级辅助)
- 全部系列索引
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【Git 内部】.git 目录全景:三棵树与仓库布局
git init 之后 .git 里每个路径干什么?对照 gitrepository-layout 与本地 find 清单,建立 bare/non-bare、三棵树、objects/refs/index 的磁盘级地图。
【Git 内部】refs、HEAD 与 packed-refs
分支和标签在磁盘上只是一行 SHA?refs/heads、符号引用 HEAD、packed-refs 文件头与 peeled tag 的格式与生成时机。
【Git 内部】reflog:logs/ 下的追加式历史
git reflog 读的是哪个文件?logs/HEAD 与 logs/refs 的二进制条目格式:旧 SHA、新 SHA、提交者、时间戳与消息。
【Git 内部】index 暂存区:dircache v2 与扩展节
git add 改的是 .git/index。dircache 魔数、条目 stat/SHA/stage、TREE/REUC 扩展节,对照 xxd 与 git ls-files -s。