Git Internals
Git 提交图与历史表达
理解为什么 Git 历史本质上是提交图,而不是简单时间线,以及 merge、rebase 如何重新表达这张图。
一句话理解
Git 保存的是提交图。线性日志只是这张图的一种阅读方式,不是 Git 底层真正的唯一结构。
1. 为什么“历史图”比“提交列表”更准确
如果把 Git 历史只理解成列表,你会很难解释:
- merge commit 为什么会有两个父节点
- rebase 为什么会生成一组新的提交 ID
- 为什么同一段改动能以不同图形结构被表达
把它看成图之后,这些现象就自然很多。
2. 普通提交如何形成一条线
大多数提交只有一个父提交,因此从阅读体验上会像一条线:
A -> B -> C -> D
这只是“图在这个局部看起来像线”,并不代表 Git 只有线性结构。
3. merge commit 表达了什么
merge commit 往往有两个父节点。它告诉 Git:
- 这里有两条历史线
- 它们在这个提交处汇合
所以 merge 的价值不只是“把代码拼起来”,还在于把“曾经并行开发过”这个事实保留下来。
4. rebase 为什么看起来更线性
rebase 的做法不是修改旧提交,而是:
- 取出一组提交表达的改动
- 换到新的基底上
- 重新生成一组提交对象
结果就是:
- 图结构被重新表达
- 提交 ID 会变化
- 阅读上通常更线性
5. merge 和 rebase 的真正差异
它们都可以完成“把两边改动整合起来”这件事,但历史表达不同。
merge
- 保留分叉与汇合
- 历史图更忠实于协作过程
- 已共享历史通常更安全
rebase
- 让一组提交接到新的基底后面
- 历史看起来更直
- 适合整理尚未共享的个人分支
6. 为什么 commit ID 会跟着历史结构变化
因为 commit 对象里不仅保存内容快照,还保存父提交关系。只要父节点不同,commit 对象就不是同一个对象,ID 也会跟着变化。
这就是 rebase 会“改写历史”的底层原因。
7. 为什么看图能帮助你判断风险
当你知道历史是图,就更容易判断:
- 某个 merge commit 是不是一个真实的汇合点
- 当前分支是不是只比主线多出一小段未共享提交
- reset 或 force push 会不会让别人依赖的路径突然消失
8. 用图来重新理解常见命令
git log --graph
它不是在“制造图”,而是在把已经存在的提交父子关系可视化。
git merge
它常常是在图里新增一个有多个父节点的提交。
git rebase
它会把旧改动重新表达成另一段提交链。
git cherry-pick
它会把某个提交表达的改动单独复制成一条新的提交节点。
常见误区
rebase 只是把提交“挪一挪”
不准确。更准确的说法是:rebase 基于新的父链重新生成提交对象。
merge 比 rebase 更“脏”
这不是底层结论,而是阅读偏好和协作策略差异。merge 更强调保留真实分叉结构,rebase 更强调线性表达。
一个实用判断原则
如果你关心“团队真实怎样并行协作过”,merge 的图更直观;如果你关心“合并前把自己的一段提交整理清楚”,rebase 更合适。