编程与开发 · 第 5 期

自己写个 Git,对象模型长什么样

内容寻址存储 · content-addressable store

Git 不是数据库,是一个对象存储——blob 存文件内容、tree 存目录结构、commit 存快照,用 SHA 哈希串成一张 DAG。理解这个对象模型,你就懂了 Git 的分支、合并、历史为什么这么设计,而不是像 SVN 那样存 diff。
⏱ 约 11 分钟 🎯 用 Git 但不懂它内部的人 📦 源:build-your-own-x §6

01一个反共识:Git 存快照不存 diff

很多人以为 Git 像 SVN 一样存"每次改了什么"(diff)。其实 Git 存的是每次提交的完整快照。

每次 commit,Git 把当时的每个文件存成一个 blob(按内容算 SHA),目录结构存成 tree,再包一层 commit 指向根 tree。没改的文件,新 commit 的 tree 直接指向旧 blob——靠 SHA 复用,不靠 diff 存储。所以 Git 切分支是 O(1)(只加一个指针),合并能自动找共同祖先。

commit → tree → blob / tree → ... (SHA 串联)
Git 存快照不存 diff,
靠 SHA 复用。
灏天文库 · 编程与开发 P.15

02对象树可视化:点对象看内容

下面是一个小仓库的对象树(commit → tree → blob/tree)。点任意对象,看它的类型、SHA 和内容。

🌳 git 对象树可视化
点任意节点看它的类型、哈希和内容。
commit tree blob

← 点上方对象

03packfile:怎么把对象压成包

每个对象单独存一个文件,小仓库没问题,大仓库几百万对象就慢了。Git 的解法是 packfile:

这就是为什么 git gc 后仓库变小、clone 变快——它把松散对象压成 packfile。理解对象模型 + packfile,你就懂了 Git 的存储层:日常是松散对象(快),gc 后是 packfile(小),两者自动转换。

松散对象求快,
packfile 求小。
灏天文库 · 编程与开发 P.16

04带走这套清单

✅ 理解 Git 6 条可执行规则

  1. 记住三类对象:blob=内容、tree=目录、commit=快照,SHA 串联。
  2. Git 存快照不存 diff:没改的文件靠 SHA 复用,不是存增量。
  3. 分支是指针不是副本:切分支 O(1),因为只加一个 commit 指针。
  4. 合并靠共同祖先:Git 自动找两个分支的最近共同 commit 做三方合并。
  5. gc 把松散压成 packfile:仓库变小、clone 变快,delta 压缩是关键。
  6. 从零写一遍:blob/tree/commit 各写个存取函数,比看 10 篇博客都懂。
懂了对象模型,
Git 的设计就不奇怪了。
灏天文库 · 编程与开发 P.17