Git 的快照哲学:不是增量,是内容寻址
很多人把 Git 当"高级保存"用。但 Git 的设计哲学和 SVN 完全不同——它不是记录「改了什么」,而是记录「这一刻所有文件的完整状态」。
问题从哪里来
传统版本控制(CVS、SVN)是增量模型:只存 diff,版本是一条链。
这导致:
- 回滚到旧版本,要反向应用所有 diff——慢
- 分支操作,要算「从哪分出来的」——复杂
- 合并冲突,要三方 diff——容易错
Git 想清楚了:与其算 diff,不如直接存快照。
快照模型是什么
每次 git commit,Git 做的事:
关键:Git 不是「版本控制系统」,是**「内容寻址文件系统」**——
- 文件内容 → 哈希(地址)
- 哈希(地址)→ 文件内容
这就是 .git/objects/ 里那些怪文件名的来源——它们就是哈希值。
这个设计带来的能力
| 能力 | 为什么快照模型能做到 | 增量模型做不到 |
|---|---|---|
| 分支切换秒级 | 只是把 HEAD 指向另一个快照 | 要算 diff、应用补丁 |
| 离线工作 | 所有历史都在本地快照里 | 得连服务器才能看历史 |
| Cherry-pick | 直接取那个快照里的文件状态 | 要算「这个 commit 改了什么」 |
| 合并 | 三个快照比对,不是算 diff | 增量合并容易漏 |
认知升华
回头看,Git 教给我们的是:
不要用「增量思维」解决所有问题。
有时候,存「完整状态」比存「变化」更高效——因为状态是确定的,变化是相对的。
这和函数式编程的「不可变数据」是一个道理:存值,不存改。
验证你有没有想清楚
两个问题:
- Git 的「快照」为什么不会让仓库变得巨大?
(git gc之前和之后,存储方式有什么不同?) - 为什么 Git 的「内容寻址」让「去重」天然高效?换一个文件的位置(比如从
a/b.js移到src/b.js),Git 怎么处理?
(答案同样不写在这里——去想清楚,想清楚了才是你的认知。)