- 教程
- 版本控制
【免费下载链接】commit-messages-guide
A guide to understand the importance of commit messages and how to write them well
本文基于开源文档仓库 commit-messages-guide(越南语版 README_vi-VN.md)编写,系统讲解 Git Commit 的本质、Commit Message 为何重要、编写高质量提交信息的九大最佳实践,以及如何通过rebase、squash、cherry-pick和补丁级操作来规划与重构一份清晰的提交历史。读完本文,你将掌握一套可直接落地到日常开发与代码评审中的 Commit Message 编写规范,并能熟练运用交互式变基等命令管理自己的提交历史。
什么是 Commit?——快照而非差异
很多人误以为 Git 只保存文件版本之间的差异(diff),实际上并非如此。用最简单的话说,一个 Commit 就是你本地仓库中文件的快照(snapshot)。
Git 存储的是所有文件的完整版本,而不是文件之间的差异。对于两次提交之间没有发生变化的文件,Git 并不会重复保存一份新文件,而是保存一个指向先前已存储的相同文件的链接。因此:
- 每次提交都是一份完整可恢复的文件状态,而不只是"改了什么";
- 未变更的文件通过链接复用存储,不会造成无谓的磁盘浪费;
- 每个提交因此都是独立的、可被单独检出的版本节点。
下图展示了 Git 随时间推移的存储方式,其中每一个"Version"就是一个 Commit(图为原文档引用示意图,读者可在原文档中查看):
Version 1 ──▶ Version 2 ──▶ Version 3 ──▶ Version 4 │ │ │ │ 快照A 快照A(链接) 快照A(链接) 快照A(链接) 快照B 快照B(链接) 快照B(链接) 快照C 快照C(链接) 快照D理解了"提交即快照"这一底层事实,就自然明白:每个提交都值得配上一条能说清楚它是什么、为什么存在的消息。
为什么 Commit Message 如此重要?
Commit Message 的价值远不止"给提交起个名字",它直接关系到协作效率与长期可维护性。原文档总结了四个核心原因:
- 加快并简化代码评审(code review):评审者不需要逐行揣测每个提交的意图,一条清晰的消息能让评审过程更快速、更高效;
- 帮助他人理解变更:消息让团队其他成员在不看 diff 的情况下也能明白这次改动的含义;
- 解释"为什么":很多决策原因无法仅靠代码表达,消息是承载这些背景信息的载体;
- 帮助未来的维护者:多年后维护者可以通过消息弄清"为什么改、怎么改的",让故障排查(troubleshooting)与调试(debugging)变得容易得多。
值得注意的是,本仓库本身就在践行这一理念:仓库的 CONTRIBUTING.md 明确要求贡献者"使用英文撰写 commit message、issue 和 PR",并且"遵循指南中描述的最佳实践"。也就是说,你正在阅读的这份指南,正是用自己倡导的规范写出来的。
编写 Commit Message 的九大最佳实践
以下实践来自原文档作者的实际经验、互联网文章及其他指南,属于"建议而非铁律"——正如 CONTRIBUTING.md 所强调的:Best practices in this guide should aim to help, not to set rule-following blindly standards(这些实践是为了帮助你更清晰、更简洁地沟通,而不是制造盲从的教条)。
实践一:使用祈使句(imperative form)
提交消息应该描述"这次变更实际做了什么、产生了什么效果",而不是"已经做了什么"。
# Good Use InventoryBackendPool to retrieve inventory backend# Bad Used InventoryBackendPool to retrieve inventory backend为什么要用祈使句?因为一条提交消息是在陈述这次提交带来的行为——"让代码去做 X",而不是回顾"X 已经被做了"。这与你给同事下达指令的口吻一致:Fix bug、Add feature、Refactor module,而非Fixed bug、Added feature。用祈使句写作,消息读起来更像一句清晰的指令,也更贴近 Git 本身"Apply this patch"的心智模型。
实践二:首字母大写
# Good Add `use` method to Credit model# Bad add `use` method to Credit model理由是遵循英文语法中"句子开头字母大写"的规则。当然,这条实践会因人、因团队、因语言而异——是否大写并不重要,重要的是选定一个统一标准并始终遵守,不要在两条提交之间随意切换。
实践三:不查看源码也能理解变更内容
好的消息应当让人只看文字就明白改动的意图,而不必打开代码。
# Good Add `use` method to Credit model# Bad Add `use` method再看一组对比:
# Good Increase left padding between textbox and layout frame# Bad Adjust cssAdd \use` method没有说明"给谁加、加在哪";Adjust css更是完全无法传达意图。这种"不看代码也能懂"的习惯在多提交、多改动、多重构的场景下尤为珍贵——它能让评审者理解提交者当时在想什么,也让git log` 成为一份可读的变更文档。
实践四:用正文解释"为什么(why)""做什么(for what)""怎么做(how)"及补充细节
主题行(subject)负责一句话概括,正文(body)则承载更深层的背景。原文档给出了三个正面示例:
# Good Fix method name of InventoryBackend child classes Classes derived from InventoryBackend were not respecting the base class interface. It worked because the cart was calling the backend implementation incorrectly.# Good Serialize and deserialize credits to json in Cart Convert the Credit instances to dict for two main reasons: - Pickle relies on file path for classes and we do not want to break up everything if a refactor is needed - Dict and built-in types are pickleable by default# Good Add `use` method to Credit Change from namedtuple to class because we need to setup a new attribute (in_use_amount) with a new value这三个示例都遵循同一结构:主题行概括改动 → 空行 → 正文解释动机与取舍。第二个示例甚至以列表形式并列了"为什么转成 dict"的两个理由;第三个示例解释了"为什么从 namedtuple 改为 class"——这类"为什么不选另一个方案"的信息,是代码本身永远无法表达的。
格式上请注意两点:
- 主题与正文之间必须用空行分隔,空行之后的额外空行都属于正文部分;
- 适当使用
-、*以及反引号(`)等字符可以让消息更易读(例如用反引号包裹代码标识符in_use_amount)。
实践五:避免无上下文、含义笼统的消息
以下这类消息对任何读者(包括未来的自己)都没有信息量:
# Bad Fix this Fix stuff It should work now Change stuff Adjust css"修好了""改了下""应该能跑了"——它们没有说明改了什么、为什么改、问题根因是什么。写消息时不妨问自己:如果半年后的我读到这条消息,能据此定位问题吗?
实践六:限制字符数
Pro Git 的提交规范建议:
- 主题行(subject)不超过 50 个字符;
- 正文(body)每行不超过 72 个字符。
限制主题长度能强迫你提炼核心信息;限制正文行宽则保证在终端、邮件、评审工具等场景下都能优雅换行显示,避免阅读体验被截断破坏。
实践七:保持语言一致性
- 对项目所有者:选定一种语言,用它写所有的提交消息;理想情况下,提交语言应与代码注释、默认翻译语言(对本地化项目而言)保持一致;
- 对贡献者:跟随仓库现有提交历史所使用的语言来写。
正例——统一使用英文:
# Good ababab Add `use` method to Credit model efefef Use InventoryBackendPool to retrieve inventory backend bebebe Fix method name of InventoryBackend child classes正例——统一使用葡萄牙语:
# Good (Portuguese example) ababab Adiciona o método `use` ao model Credit efefef Usa o InventoryBackendPool para recuperar o backend de estoque bebebe Corrige nome de método na classe InventoryBackend反例——中英混杂:
# Bad (mixes English and Portuguese) ababab Usa o InventoryBackendPool para recuperar o backend de estoque efefef Add `use` method to Credit model cdcdcd Agora vai杂糅语言会让检索(git log --grep)、自动化工具解析和跨团队协作都变得困难。本仓库的做法同样值得借鉴:CONTRIBUTING.md 规定仓库内提交、issue、PR 一律使用英文,同时为多语言文档维护了 18 个翻译版本(见 README.md 的 Available languages 列表,其中就包括本文依据的 README_vi-VN.md 与中文版 README_zh-CN.md)。
实践八:使用一份 Commit Message 模板
原文档提供的模板最初由 Tim Pope 撰写,并收录于《Pro Git》一书。模板原文如下:
Summarize changes in around 50 characters or less More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. In some contexts, the first line is treated as the subject of the commit and the rest of the text as the body. The blank line separating the summary from the body is critical (unless you omit the body entirely); various tools like `log`, `shortlog` and `rebase` can get confused if you run the two together. Explain the problem that this commit is solving. Focus on why you are making this change as opposed to how (the code explains that). Are there side effects or other unintuitive consequences of this change? Here's the place to explain them. Further paragraphs come after blank lines. - Bullet points are okay, too - Typically a hyphen or asterisk is used for the bullet, preceded by a single space, with blank lines in between, but conventions vary here If you use an issue tracker, put references to them at the bottom, like this: Resolves: #123 See also: #456, #789中文翻译如下(越南语版 README_vi-VN.md 还提供了完整的越南语翻译版本,便于非英语读者对照理解):
用 50 个或更少的字符概括变更 如有必要,补充更详细的说明文字,每行控制在 72 个字符左右。 在某些场景下,第一行被视为提交的主题(subject),其余文本作为 正文(body)。主题与正文之间的空行至关重要(除非你完全省略正文); 如果两者连在一起,`log`、`shortlog`、`rebase` 等工具可能会被混淆。 解释这个提交要解决的问题。重点说明"为什么"要做这个改动,而不是 "怎么做"(怎么做由代码来说明)。这次改动是否有副作用或其他不易 察觉的后果?请在这里解释清楚。 后续段落之间用空行分隔。 - 也支持使用列表(bullet points) - 通常用连字符或星号作为列表标记,前面加一个空格,条目之间 留空行,不过具体约定因项目而异。 如果你使用问题跟踪系统(issue tracker),请在末尾引用相关编号: Resolves: #123 See also: #456, #789这份模板把"主题 + 正文 + 空行 + 列表 + issue 引用"的完整结构固化下来。你可以在仓库中通过git commit --template或git config commit.template将其配置为默认模板,让每次提交都按此结构书写。
实践九(补充):避免"this PR / this commit / this patch"式指代
英文原版 README.md 还补充了两条值得一并采纳的实践。其一:不要用"这个提交""这个 PR"这类自我指代——读者当然知道这就是一个提交或 PR,没必要重复声明:
# Bad This commit does x and y... This PR does x and y... This Patch x and y... # Good X and y are done...实践十(补充):避免人称化表达
其二:像学术写作一样避免使用人称代词,让消息保持客观、聚焦于变更本身:
# Bad I fixed the problem. # Good The problem has been fixed by doing x and y...Rebase 与 Merge:如何构建清晰的提交历史
本节是 Atlassian 教程《Merging vs. Rebasing》的精简版(TL;DR)。越南语版文档特意保留rebase与merge两个英文术语不翻译,以免产生歧义——这两个概念在 Git 语境中含义精确,建议读者同样直接使用英文关键词检索学习。
Rebase 是什么
TL;DR:把你分支上的提交逐个应用到基础分支之上,生成一棵新的提交树。
rebase 会将当前分支的每一个提交"重放"到目标分支的最新状态上,得到一条线性的历史。
Merge 是什么
TL;DR:创建一个新的提交——恰当地称之为merge commit(合并提交)——它承载两个分支之间的全部差异。
merge 保留了两条分支各自的演进轨迹,用一个额外的合并节点把它们汇合起来。
为什么很多人偏好 rebase 而非 merge?
原文档作者明确表示自己"特别偏爱 rebase",理由如下:
- 产生一条**"干净"的历史**,没有多余的 merge commit;
- 所见即所得:在代码评审中,所有变更都能对应到某个具体、有标题的提交,避免改动被"藏"在合并提交里;
- 更多的合并冲突由提交者本人解决,且每一次由合并产生的变更都归属于带有恰当消息的提交;
- 人们通常不会深入审查 merge commit,因此尽量避免它们,可以确保所有改动都有一个"属于它的提交"。
什么时候应该 squash?
Squashing是把一串提交压缩成一个提交的过程。典型适用场景:
- 减少几乎没有上下文的提交(如修正拼写、格式调整、遗漏的内容);
- 把分开的、但合并在一起才更有意义的变更合并为一条提交;
- 重写"工作进行中(work in progress)"的提交。
什么时候应该避免 rebase 或 squash?
在公共提交(public commits)或多人共用的共享分支上,避免 rebase 和 squash。原因很直接:rebase 和 squash 会改写历史、覆盖已有提交。如果对共享分支上的提交(例如已经推送到远程仓库的提交,或来自其他分支的提交)执行这些操作,会造成分叉(divergent trees)与冲突,导致协作者丢失他们本地或远程的更改,引发混乱。
一句话总结:历史只在你自己的私有分支上"重写";一旦提交被推送到公共分支,就以 merge 方式演进。
实用的 Git 命令
理解了理念,再看工具。以下是原文档给出的四组高频命令,配合真实示例逐一说明。
git rebase -i:交互式改写历史
rebase -i用于 squash 提交、修改消息、重写/删除/重排提交等。执行后会打开编辑器,展示如下形式的命令清单:
pick 002a7cc Improve description and update document title pick 897f66d Add contributing section pick e9549cf Add a section of Available languages pick ec003aa Add "What is a commit" section" pick bbe5361 Add source referencing as a point of help wanted pick b71115e Add a section explaining the importance of commit messages pick 669bf2b Add "Good practices" section pick d8340d7 Add capitalization of first letter practice pick 925f42b Add a practice to encourage good descriptions pick be05171 Add a section showing good uses of message body pick d115bb8 Add generic messages and column limit sections pick 1693840 Add a section about language consistency pick 80c5f47 Add commit message template pick 8827962 Fix triple "m" typo pick 9b81c72 Add "Rebase vs Merge" section # Rebase 9e6dc75..9b81c72 onto 9e6dc75 (15 commands) # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into the previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # d, drop = remove commit # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out交互式变基中的命令释义:
| 命令 | 含义 |
|---|---|
p/pick | 保留该提交 |
r/reword | 保留提交,但允许修改提交消息 |
e/edit | 保留提交,但停下来供你修改(amend) |
s/squash | 保留提交,但将其并入上一个提交 |
f/fixup | 类似 squash,但丢弃该提交自己的消息 |
x/exec | 用 shell 执行该行剩余的命令 |
d/drop | 移除该提交 |
注意清单中的警告:行可以重排,命令按从上到下的顺序执行;删除一行意味着该提交将永久丢失;如果删除全部内容,rebase 会被中止。空提交默认会被注释掉。
值得一提的细节是:上面示例中的提交主题(Improve description、Add contributing section、Add "Good practices" section、Add commit message template、Add "Rebase vs Merge" section……)恰好对应本指南自身各章节的演进过程——这份示例本身就是"用规范提交逐步构建文档"的真实写照。
fixup:轻松清理提交
fixup用于在不需要复杂 rebase的情况下快速清理提交:把它并入前一个提交,同时丢弃自身冗余的消息。典型工作流:先用git commit --fixup=<sha>创建一条"修补"提交,再通过git rebase -i --autosquash让 Git 自动把 fixup 提交排到目标提交之后并完成合并。对于"漏改一处""补个空格"这类微小修正,它比手动 squash 更省心。
git cherry-pick:把提交搬到正确的分支
当你把提交误放到了错误的分支时,不必重新编码,用cherry-pick把那条提交"摘"到目标分支即可:
$ git cherry-pick 790ab21 [master 094d820] Fix English grammar in Contributing Date: Sun Feb 25 23:14:23 2018 -0300 1 file changed, 1 insertion(+), 1 deletion(-)输出清晰地展示了新提交(094d820)在目标分支(master)上落地的结果、作者时间与文件变更统计。
git add/checkout/reset -p:按补丁(hunk)精细控制
-p(--patch)系列命令允许你只暂存(add)想要的补丁片段,而不改动已写好的代码,非常适合把一个大改动拆分成多个逻辑提交,或单独 reset/checkout 某处修改。
假设我们面对如下 diff(示例为给 README 增加 Template 章节的改动):
diff --git a/README.md b/README.md index 7b45277..6b1993c 100644 --- a/README.md +++ b/README.md @@ -186,10 +186,13 @@ bebebe Corrige nome de método na classe InventoryBackend `` # Bad (mixes English and Portuguese) ababab Usa o InventoryBackendPool para recuperar o backend de estoque -efefef Add `use` method to Credit model cdcdcd Agora vai `` +### Template + +This is a template, [written originally by Tim Pope](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), which appears in the [_Pro Git Book_](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project). + ## Contributing Any kind of help would be appreciated. Example of topics that you can help me with: @@ -202,3 +205,4 @@ Any kind of help would be appreciated. Example of topics that you can help me wi - [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) - [Pro Git Book - Commit guidelines](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines) +- [A Note About Git Commit Messages](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)执行git add -p后,Git 会逐个 hunk 询问你的处理方式:
Stage this hunk [y,n,q,a,d,/,j,J,g,s,e,?]? s Split into 2 hunks.输入s可将当前 hunk 进一步拆分为更小的 hunk,从而精细化控制。上述示例被拆分成了三个 hunk:
hunk 1
@@ -186,7 +186,6 @@ `` # Bad (mixes English and Portuguese) ababab Usa o InventoryBackendPool para recuperar o backend de estoque -efefef Add `use` method to Credit model cdcdcd Agora vai `` Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?hunk 2
@@ -190,6 +189,10 @@ `` cdcdcd Agora vai `` +### Template + +This is a template, [written originally by Tim Pope](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), which appears in the [_Pro Git Book_](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project). + ## Contributing Any kind of help would be appreciated. Example of topics that you can help me with: Stage this hunk [y,n,q,a,d,/,K,j,J,g,e,?]?hunk 3
@@ -202,3 +205,4 @@ Any kind of help would be appreciated. Example of topics that you can help me wi - [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) - [Pro Git Book - Commit guidelines](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines) +- [A Note About Git Commit Messages](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)交互提示中的按键含义(按?可随时查看完整帮助):y暂存该 hunk、n跳过、q退出、a暂存该文件剩余全部 hunk、d跳过该文件剩余全部 hunk、/按正则搜索 hunk、j/J跳到下一个/下一批 hunk、g跳转、s拆分、e手动编辑、?显示帮助。同理,git checkout -p与git reset -p允许你对工作区或暂存区做同等级的精细回退操作。
延伸阅读与仓库内资料
本文主体内容完整继承了 README_vi-VN.md,读者可以继续在仓库中查阅:
- README.md:英文原版,比越南语版多出"PR 消息与 cover letter""commit 签名与 DCO""避免 this PR/this commit 指代""避免人称化"等章节,是最完整的版本;
- README_zh-CN.md:简体中文版,适合中文读者对照阅读;
- README_zh-TW.md 及其他 15 个语言的翻译版本,完整列表见 README.md 的 Available languages 一节;
- CONTRIBUTING.md:仓库贡献指南,包含跨语言一致性原则、新翻译检查清单(如"仅在 README.md 中更新语言列表""翻译版本不要包含 Available languages 章节")、PR 评审流程(至少一位母语者评审、讨论解决后才可合并)等实操约定;
- CODEOWNERS:代码所有者配置,标明各文件的主要维护者;
- LICENSE:本仓库采用 Creative Commons Attribution 4.0 International(CC BY 4.0)许可。
本指南的灵感与参考来源包括:Chris Beams 的《How to Write a Git Commit Message》、《Pro Git》一书的提交规范章节、Tim Pope 的《A Note About Git Commit Messages》、Atlassian 的《Merging vs. Rebasing》教程、《Pro Git》的 Rewriting History(改写历史)章节,以及 Linux 内核补丁哲学与"完美补丁"(The perfect patch)等经典资料,均可按名称检索获取。此外,whatthecommit.com 与 gitmoji 这类趣味工具也能为提交写作提供灵感。
把上面这些实践串起来:用祈使句 + 首字母大写写主题、50/72 字符限制、正文讲清楚"为什么"、保持语言一致、私有分支上放心用rebase -i与fixup打磨历史、公共分支上以 merge 演进、误提交用cherry-pick补救、大改动用-p精细拆分——你就拥有了一套完整、可落地的提交信息方法论。
- 教程
- 版本控制
【免费下载链接】commit-messages-guide
A guide to understand the importance of commit messages and how to write them well
相关推荐
Git Commit Message 撰写实战指南:commit-messages-guide 项目的最佳实践与历史整理手册
Git Commit Message 撰写实战指南:commit messages guide 项目的最佳实践与历史整理手册 commit messages g
教程版本控制Armbian 有线网不通?Magicsee N5 Max 以太网故障 30 分钟修复指南
Armbian 有线网不通?Magicsee N5 Max 以太网故障 30 分钟修复指南 闲置的 Magicsee N5 Max 刷完 Armbian,想把它
嵌入式开发工具构建工具操作系统commit-messages-guide:Git 提交信息编写与提交历史整理的完整实战指南
commit messages guide:Git 提交信息编写与提交历史整理的完整实战指南 本指南以开源仓库 commit messages guide 的葡
教程版本控制
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考