[控场AI]
· 7 分钟阅读· 3,601 字

Git提交信息写作指南:让Commit成为可读文档

Git提交信息写作指南:让Commit成为可读文档

用三段式提交信息回答 what/why/impact,让 git blame 不再考古。

文章指出,糟糕的提交信息如“fix stuff”会在数月后导致额外排查时间,而结构化信息能回答 90% 的 git blame 疑问。作者提出三段式结构:标题用祈使句概括改了什么,正文依次说明改了什么、为什么改、影响与意外之处。通过真实前后对比展示好信息如何避免误解。为让好格式成为默认,建议使用提交模板、避免用 -m 处理非琐碎改动、合并前 squash,并谨慎使用 AI 起草(why 必须亲手写)。单人项目尤其需要好提交信息,因为 git 历史是唯一记录推理的地方。

为什么你的 commit message 需要重写

六个月后,你对一行奇怪的代码运行 git blame,发现提交信息只有三个字:"fix stuff"。没错,是你自己写的。你完全不记得修了什么、为什么修。于是你不得不翻找可能早已不存在的 PR 和聊天记录。

这不是个别现象。据开发者社区分析,一条糟糕的提交信息(比如 "fix bug")在数月后可能让排查多花 20 分钟;而一条结构化的提交信息能回答 90% 的 git blame 疑问。提交信息是唯一紧挨着代码的文档,但大多数人都在浪费它。

提交信息示意

未来你会问的三个问题

当你运行 git blame 时,你从不问"这段代码在做什么"——代码本身就能告诉你。你真正想问的是:它为什么存在。

这三个问题永远相同:改了什么(what)、为什么改(why)、影响了什么(impact)。一条提交信息如果按顺序回答了这三点,你就能立刻关掉终端继续工作;如果答不上来,你就得开始考古。

作者举了一个真实的坏例子:update handler。后来他发现这次提交给某个 API 调用加了超时,diff 能告诉他"做了什么",却无法告诉他"为什么"——这个超时是拍脑袋定的吗?是响应了真实卡顿吗?因为不知道,他甚至不敢改动那行代码。

对比之下,如果信息写明"上游支付 API 在高负载下会挂起 30 秒,5 秒超时加一次重试对用户更快",那么读者立刻知道这个数字不是随机的,也清楚删除它会破坏什么。

"为什么"是最先消亡的部分。 代码自带"做了什么",diff 永久保留它;但你脑子里的推理会迅速蒸发。生产事故、慢接口、客户碰到的边界情况——趁记忆新鲜写下来,因为代码本身物理上无法存储这些。

"影响"是最常被跳过、也最后悔漏掉的一行。例如:"这同时改变了缓存键的结构,部署后旧缓存条目会失效。"这一句话能省掉别人一个困惑的下午。

git blame 是 Git 版本控制系统中用于追溯文件每一行最后修改者的命令。它逐行显示该行所属的提交哈希、作者和提交时间,通常配合 -L 参数限定行范围。git blame 的价值在于定位“责任”与“上下文”,但它只能指向一条提交信息;如果那条信息贫瘠,追溯就到此为止。因此,提交信息实际上是 git blame 的“元数据层”,它决定了你能否在不离开终端的情况下理解代码的历史意图。

我实际使用的提交结构

一条好的提交由标题行和正文组成,中间空一行。Git 对第一行有特殊处理,所以它分量很重。

标题控制在 50 字符以内,用祈使句:写 "Add timeout to payment handler",而不是 "Added" 或 "Adding"。它应该能补全句子:"如果应用,这次提交将……"

空一行之后是正文,每行约 72 字符换行,真正的工作在这里。作者的正文分三段,不加标签,按顺序写:

  1. 用平实语言说明改了什么,不是复述 diff,而是人能读懂的摘要。
  2. 为什么:这是证明变更存在合理性的段落。
  3. 影响与意外之处:副作用、前置条件、任何让人惊讶的细节。

大多数提交的正文只有四到六行。有些只有一行,因为改动微不足道且不言自明(比如"修正错误字符串里的拼写")。这个结构可以向下伸缩。

一个要避免的做法:把工单号当成全部信息。"JIRA-4471"对未来读者毫无意义,还需要登录和有效链接。工单系统会迁移、归档甚至关停,而 git 历史比它们活得都久。工单号可以放在末尾引用,但信息本身必须独立成立。

祈使句标题是 Git 社区广泛遵循的约定,源于 Git 自身生成的提交信息(如“Merge branch…”)。它要求标题以动词原形开头,隐含主语“This commit will…”。这样做的好处是:git log --oneline 读起来像一份动作清单,便于扫描和生成变更日志。同时,50 字符限制是为了适配 git log 默认终端宽度,避免截断。正文每行 72 字符换行则源于传统终端和邮件列表的显示习惯,确保在 git log 和邮件补丁中都不会折行。

真实提交的前后对比

数据库迁移。改前:migrate db。改后:标题 "Add nullable discount_code to orders",正文说明该列可为空是为了在现有 8 万行数据上执行迁移时无需默认值回填,且应用代码把 null 视为"未应用折扣码"。改前的版本是颗地雷——半年后有人会疑惑为什么这列是可空的而不是默认空字符串。模式决策正是那种你会忘记、然后反复质疑的东西。

性能优化。改前:perf。改后:"Switch catalog lookup to binary search",正文注明列表是预排序的、共 5 万行,线性扫描在性能分析中暴露出来。现在任何读者都知道前置条件(列表必须保持有序),不会不小心破坏它。

日志修复。改前:fix logging——毫无用处,因为日志改动往往微妙且承重。改后:"Add request ID to structured log context",正文说明请求内每行日志现在携带同一个 ID,可以 grep 完整链路,并注明这依赖中间件先运行。

规律很清楚:改前的信息都只有两个词,全都毫无价值;改后的信息大概多花 40 秒,却能在问题被提出之前就回答它。这 40 秒感觉像交税,其实是给未来的自己放了一笔高息贷款——避免一次 20 分钟的排查,就够偿还 30 次认真的提交。

让好格式成为默认

靠自律会失败。作者不信任自己每次都有意志力写好信息,所以让环境推着他走。

第一,提交模板。 Git 允许通过配置指向一个文本文件。模板里用注释行提醒 what / why / impact 的顺序。运行不带 -m 的 git commit 时,编辑器会带着这些提示打开,填完后删掉注释即可。结构就摆在眼前,摩擦消失了。

第二,禁止用 -m 处理非琐碎改动。 -m 标志是坏信息的温床——在局促的 shell 里打字,没有思考空间。打开编辑器,给自己空间。单行标志里的笔误,最终会变成 "fix stuff" 这样的提交。

第三,合并前 squash。 进行中的提交写 "wip"、"oops forgot import" 都无所谓,因为它们永远到不了主分支。把整个分支压缩成一条干净的提交,用一条好信息描述完成后的变更。别人读到的历史应该是"发布了什么"的故事,而不是"我怎么走到这里"的混乱。

作者现在也用 AI 起草初稿:粘贴 diff,要求生成 what/why/impact 格式的信息,然后亲手修正 "why",因为模型只能猜测他的推理。模型几乎总能写对 "what",而那本就是无聊的部分。

这里有个警告:不要让工具替你写 "why" 然后不读就提交。模型从 diff 推断意图,有时会错得听起来很自信。永久历史里一个错误的 "why" 比空白更糟。

Git 提交模板通过 git config commit.template <path> 配置一个文本文件。当运行不带 -m 的 git commit 时,Git 会将该文件内容预填到编辑器中,其中以 # 开头的行是注释,不会被记录到最终提交信息里。模板可以包含提示语、检查清单或固定结构,帮助开发者在每次提交时自然遵循规范。它不强制,但通过减少认知负担,让好格式成为默认路径。

单人项目更需要这样做

如果你在做单人项目,觉得没人会读所以无所谓——你恰恰是受益最大的人。没有队友可以问,git 历史是你唯一的记录。过去的你是一个陌生人,你只能通过提交信息和他对话。

从今天开始做一个改变:任何比拼写错误大的改动,都别再用 git commit -m。打开编辑器,写三段短文回答 what、why、impact,一个月后当 git blame 落在它上面时,感受一下。

想更进一步,设置提交模板,让三个提示每次迎接你。这一行配置对作者提交历史的帮助,超过任何"努力记住"。

回报不会在写信息当天显现,而会在之后出现:当一行令人困惑的代码旁边附着清晰解释,你十秒钟关掉终端,而不是挖二十分钟。好的提交是写给一个已经忘光一切的人的信息——那个人就是你自己。

#git commit message#提交信息规范#git blame#代码文档#提交模板#开发效率
分享:

相关推荐