Git Internals

提交对象、父提交与提交说明教程

说明提交对象如何把 tree、父提交和说明文本连接成历史图。

适合谁看
  • 想建立稳定 Git 心智模型的学习者
  • 经常遇到历史、引用、恢复问题的开发者
前置知识
  • 会看基础命令输出
  • 知道提交、分支、HEAD 这些名词
常见风险
  • 只背底层术语却不连接到实际命令
  • 把对象、引用、工作区混成一层理解

一句话理解

commit 对象不是“写一句提交说明”那么简单,它真正做的是把一次项目快照、父子关系和元数据固定成历史节点。

1. 提交对象里至少有什么

提交如何通过父提交链接成历史每个 commit 对象包含指向 tree 的引用、一个或多个父提交、作者和提交者信息,以及提交说明。父提交是历史图的关键链接。
普通提交(一个父节点)
ABCD
HEAD 指向: main
Merge 提交(两个父节点)
ABCM
BEF
Rebase 后提交(新 ID 的提交链)
ABCE'F'
HEAD 指向: feature

一个 commit 对象通常会包含:

  • 指向当前快照根 tree 的信息
  • 一个或多个父提交
  • author / committer 信息
  • 提交说明

所以 commit 的价值是把“内容状态”和“历史位置”同时固定下来。

2. 为什么父提交很关键

如果没有 parent,Git 就无法表达历史图。

父提交关系决定了:

  • 当前提交是从哪里接出来的
  • merge commit 为什么会有多个父节点
  • rebase 为什么会生成新的提交对象

你看到的日志线条、分支汇合、祖先关系,本质上都依赖 parent 字段。

3. 提交说明为什么不是附属品

提交说明不仅是给人看的文字,它也是 commit 对象内容的一部分。

这意味着:

  • 只改提交说明,提交对象就变了
  • 对象一变,提交 ID 也会变

所以 amend message 不是“只改了备注”,而是生成了新的 commit 对象。

4. 为什么 commit ID 会变

因为 commit 对象里保存的不只是 tree,还包括 parent 和 message。

只要以下任一项变化:

  • 当前 tree 不同
  • parent 不同
  • message 不同
  • 作者信息不同

commit 对象就不再是同一个对象,ID 也就跟着变化。

5. merge commit 和普通提交有何不同

普通提交大多只有一个父提交。
merge commit 往往有两个父提交。

这意味着 merge commit 不只是“有新的内容状态”,它还在历史结构上明确记录了“两条线在这里汇合”。

6. 为什么这能帮助你理解 rebase

rebase 之所以被称为“改写历史”,不是因为它偷偷改了旧提交,而是因为:

  • 新基底变了
  • parent 关系变了
  • 因而必须生成新的 commit 对象

这就是为什么 rebase 后同样内容的提交,ID 也会不一样。

7. 从 commit 对象角度重新看常见操作

git commit

新增一个 commit 对象,并让当前分支引用移动到它。

git commit --amend

不是给原提交“补充说明”,而是重新生成一个新的 commit 对象来替换当前位置。

git merge

通常会新增一个有多个父提交的 commit。

常见误区

commit 主要就是一段 message

不是。message 只是 commit 对象里最容易被人直接看到的一部分。

amend 不算改写历史

从对象模型上看,算。因为新的 commit 对象已经不是原来那个了。

merge commit 只是把代码合进来

还不止。它也把“两边历史在这里汇合”的结构写进了图里。

一个最值得记住的结论

commit 不是日志条目,而是一个把快照、父链和说明一起固定下来的历史节点。