问题
Git 所说的“快照”到底是什么意思?为什么 Git 不像传统版本控制那样主要存差异?这样不会很浪费空间吗?
回答
核心结论
Git 的核心思路不是“记录某次改动长什么样”,而是:
- 记录 某一时刻整个目录树的状态
- 用对象复用、压缩和打包机制避免重复存储
所以“Git 存快照”不等于“每次都傻乎乎把所有文件完整复制一遍”。更准确地说:
Git 记录的是一次提交对应的目录树快照,而未变化的对象会被复用。
快照和差异的区别
| 方式 | 关注点 | 直观理解 |
|---|---|---|
| 差异模型 | 这次改了什么 | 第 2 行从 A 变成 B |
| 快照模型 | 当前整体是什么样 | 此刻项目目录树长什么样 |
传统版本控制更偏向“补丁思维”,Git 更偏向“状态思维”。
Git 为什么偏爱快照思维
主要因为它带来三个好处:
- 读取历史更直接:恢复某次提交时,不需要一层层回放补丁
- 分支和合并更自然:分支本质上只是指向某个提交的指针
- 对象模型更统一:文件内容、目录结构、提交记录都能抽象成对象图
为什么不会严重浪费空间
Git 主要靠三类机制控制空间占用:
1. 相同内容只存一份
相同文件内容会对应同一个 blob 对象。
提交 A ─┐
├─> blob(Hello)
提交 B ─┘
只要内容没变,Git 就不会重复创建新的内容对象。
2. 目录树按层复用
如果一个大型项目里只改了一个文件,那么:
- 变化的
blob会变 - 受影响路径上的
tree会变 - 其他未变化的对象会继续复用
这也是为什么“存快照”不等于“整仓全量拷贝”。
3. 长期存储会打包压缩
git gc 会把对象重新打包,形成 packfile。打包后 Git 内部会进一步做压缩,很多对象之间还会利用差异编码节省空间。
所以 Git 的策略更像:
- 日常写入:对象化、简单、快速
- 长期存储:打包、压缩、节省空间
Git 仓库里的常见对象
常见核心对象有四类:
| 对象 | 存什么 | 可以把它想成 |
|---|---|---|
blob |
文件内容 | 一张纸,只管内容,不管文件名 |
tree |
目录结构 | 一个文件夹目录表 |
commit |
提交元信息 | 一张记录“谁在什么时候提交了什么目录树”的卡片 |
tag |
标签对象(常见于 annotated tag) | 给某个对象贴上的正式标签 |
三个核心对象怎么串起来
commit
└─ 指向 tree
├─ 指向 blob(文件内容)
└─ 指向子 tree(子目录)
也就是说:
blob不知道自己叫什么名字- 文件名和目录层级由
tree管 - 某次提交最终指向的是一棵目录树
commit 历史为什么是 DAG
Git 提交历史通常被描述为 DAG(有向无环图)。
| 概念 | 含义 |
|---|---|
| 有向 | 提交之间有父子指向关系 |
| 无环 | 提交不会形成回路 |
| 图 | 整个历史可以看成节点和边组成的结构 |
这里的“有向”更准确地表示 父子关系方向,而不是单纯“时间箭头”。提交时间和提交关系常常相关,但不是完全等价的概念。
分支为什么这么轻
因为分支本质上只是“一个指向提交的名字”。
main -> C4
feature -> C3
创建分支通常不是复制整份代码,而是多了一个新的引用指向已有提交。
工作区、暂存区、提交之间的关系
理解 Git 最好别只背命令,还要建立这条链路:
工作区 -> 暂存区 -> commit -> 对象库
- 工作区:你眼前正在编辑的文件
- 暂存区:准备纳入下一次提交的快照内容
- commit:一次正式提交记录
- 对象库:Git 最终存储对象的地方
一个常见误区
很多人听到“Git 存快照”,就以为:
每次提交都把每个文件重新完整复制一次。
这是不准确的。Git 记录的是“目录树在这一刻长什么样”,然后通过对象复用和打包压缩,把成本控制在可接受范围内。
一句话总结
Git 的“快照”本质上是对目录树状态的记录,而不是对每次编辑动作的线性录像;它靠对象复用和打包压缩,兼顾了历史读取效率与存储空间。
相关问题
- SHA-1 是什么? → 是内容哈希,Git 用它给对象做标识与完整性校验;现代版本也在逐步支持更强哈希算法。
- 为什么文件改名后有时 Git 还能识别历史? → 因为 Git 更关注内容相似度,而不是只看文件名。
- PR 和 MR 有区别吗? → 名称不同,本质上都是“请求把一个分支合并到另一个分支”。
技术拓展
Git Flow 和 Trunk Based 怎么看
分支工作流本质上是在回答一个问题:
团队希望把“开发中的不稳定变化”与“可发布主线”隔离到什么程度?
- Git Flow:分支层次更多,发布节奏更明显
- Trunk Based:主干更短平快,更依赖持续集成和自动化测试
两者没有绝对优劣,关键看团队的协作习惯和工程能力。