commit 对象里的 parent、author、committer 到底各管什么?这篇造了根提交、合并、章鱼合并,又让 Alice、Bob、Carol 分别提交、amend、cherry-pick,看哪些字段变、哪些不变,再看时区是怎么存的。
$ git cat-file -p HEAD | grep -c "^parent" || true 0 $ git cat-file -p HEAD | grep -c "^parent" 1 合并一个分支后: $ git cat-file -p HEAD | grep "^parent" parent d0bbaeede32a7a0f6e2aab6597c8cda424352165 parent dd3c813d3c610dc68f0630adcb19ee73c84fcbbf 一次合并两个分支(章鱼合并)后: $ git cat-file -p HEAD | grep "^parent" parent bae945ca0c02ef2c3012ad0b3f8acd5ef3d735fa parent e3e50e471721a9b873dda83c6316fe98ecf0fe57 parent c72d0d88ecaa1999399a720ea9dc1c8b46b4725f $ git log --graph --oneline *-. 32c113b 章鱼合并 |\ \ | | * c72d0d8 x3 | * | e3e50e4 x2 | |/ * | bae945c 合并 |\ \ | |/ |/| | * dd3c813 x1 |/ * d0bbaee 普通提交 * 7586fcf 根提交
commit 对象里有几行 parent 就有几个父提交:根提交没有,普通提交一个,合并提交两个或更多。所谓“合并”并没有特殊的对象类型,只是多了 parent 行。
Alice 在 10 分钟时提交: $ git log -1 --format='作者 %an %ad | 提交者 %cn %cd' --date=format:%H:%M 作者 Alice 10:10 | 提交者 Alice 10:10 Bob 在 30 分钟后 amend 之后: $ git log -1 --format='作者 %an %ad | 提交者 %cn %cd' --date=format:%H:%M 作者 Alice 10:10 | 提交者 Bob 10:30 Carol 在 50 分钟后 cherry-pick 之后: $ git log -1 --format='作者 %an %ad | 提交者 %cn %cd' --date=format:%H:%M 作者 Alice 10:10 | 提交者 Carol 10:50
author 是改动的原作者与时间,committer 是创建这个 commit 对象的人与时间。amend、cherry-pick、rebase 都会生成新的 commit 对象,committer 随之变成执行操作的人,author 保持不变。给别人的补丁打上去时,这正是作者署名不丢的原因。
$ git log -1 --format='作者日期 %ad%n提交日期 %cd' --date=iso
作者日期 2026-01-01 10:10:00 +0000
提交日期 2026-01-01 10:50:00 +0000
$ git show -s --format=fuller | head -8
commit e75469276c9ab2600fcaa61f8bf72352169aade1
Author: Alice <alice@example.com>
AuthorDate: Thu Jan 1 10:10:00 2026 +0000
Commit: Carol <carol@example.com>
CommitDate: Thu Jan 1 10:50:00 2026 +0000
功能两个时间戳互相独立。git log 默认显示作者日期;--format=fuller 或 %cd 才看到提交日期。统计“某天提交了多少”时要弄清用的是哪一个。
$ git cat-file -p HEAD | sed 's/^tree .*/tree …/' tree … author 张三 <alice@example.com> 1772326800 +0800 committer Dave <alice@example.com> 1772326800 -0500 时区不同 $ git log -1 --format='%ad | %cd' --date=iso 2026-03-01 09:00:00 +0800 | 2026-02-28 20:00:00 -0500 $ git log -1 --format='%at %ct' 1772326800 1772326800 两个时间戳相减 = 0 秒(同一时刻,只是写成了不同的时区)
头部里存的是 Unix 时间戳和时区偏移。偏移只影响显示,相同的时间戳在不同时区下是同一时刻,作者和提交者可以各写各的时区。
$ git log --oneline --first-parent 32c113b 章鱼合并 bae945c 合并 d0bbaee 普通提交 7586fcf 根提交 $ git log --oneline -1 HEAD^1; git log --oneline -1 HEAD^2; git log --oneline -1 HEAD^3 bae945c 合并 e3e50e4 x2 c72d0d8 x3
parent 行的顺序有意义:第一个是合并时你所在的分支,后面是被合并进来的。HEAD^1、HEAD^2 与 --first-parent 都依赖这个顺序;git revert -m 的编号也是它(后面有专门一篇)。