bigywater.tech
Git · 8

commit 对象:parent 个数、author 与 committer 的区别

commit 对象里的 parent、author、committer 到底各管什么?这篇造了根提交、合并、章鱼合并,又让 Alice、Bob、Carol 分别提交、amend、cherry-pick,看哪些字段变、哪些不变,再看时区是怎么存的。

3 个
一次合并两个分支的章鱼合并,commit 里有 3 行 parent
作者不变
amend 和 cherry-pick 之后 author 仍是 Alice,只有 committer 变成 Bob / Carol
0 秒
+0800 和 -0500 两个时区写的 author 与 committer 时间戳相减,同一时刻

parent 行

实测 · 本机执行输出
  $ 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 行。

author 与 committer

实测 · 本机执行输出
  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 时间戳和时区偏移。偏移只影响显示,相同的时间戳在不同时区下是同一时刻,作者和提交者可以各写各的时区。

parent 的顺序

实测 · 本机执行输出
  $ 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 的编号也是它(后面有专门一篇)。

分步演示

parent 与 author
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / git 2.54.0(Apple Git-157);每个实验在临时目录里新建仓库,HOME 和全局配置指向实验专用的空目录(不读用户自己的 git 配置),提交的作者与时间固定以便复现;需要网络协议的实验用本机的 file:// 或 git daemon 回环端口(9800 以上),不碰任何远程仓库;计时用 time.perf_counter,数字每次运行会有小幅抖动。作者、提交者与时间由环境变量固定;时区一节的 张三 / Dave 两个名字是实验里设的,邮箱沿用默认值
    • 没有验证:带 GPG / SSH 签名的 commit 多出来的 gpgsig 头(后面有 SSH 签名一篇);commit 里的 encoding 头(非 UTF-8 提交说明);mergetag 头(合并签名的标签时才有);作者与提交者相同但时区不同时 git log 的排序细节
    京ICP备2026067502号-1京公网安备11010502063487号