# 撰写良好的提交消息

* 原文链接：[Writing Good Commit Messages](https://freebsdfoundation.org/wp-content/uploads/2020/11/Writing-Commit-Messages.pdf)
* 作者：**ED MASTE**

## 为何提交信息很重要？

当你在 Git、Subversion 或其他版本控制系统（VCS）中提交更改时，你会被要求写一些描述提交的文本——提交信息（Commit Messages）。那么，提交信息有多重要呢？你是否应该花费相当多的精力去写它？如果你写的是“修复了一个 bug”，这真的很重要吗？

大多数项目有不止一个开发者，并且持续一段时间。提交信息是与当前和未来的其他开发者沟通的一个非常重要的方法。

FreeBSD 拥有数百名活跃的开发者和数十年的历史，涉及数十万次提交。在这段时间里，开发者社区已经学会了良好的提交信息有多么宝贵；有时这些是通过痛苦的教训学到的。

提交信息至少有三个作用：

* 与其他开发者沟通

FreeBSD 的提交会生成邮件，发送到各种邮件列表。这些邮件包括提交信息以及补丁本身。提交信息还可以通过像 `git log` 这样的命令查看。这些信息旨在让其他开发者了解正在进行的更改；其他开发者可能希望测试这些更改，可能对某个主题感兴趣，想要更详细地审查，或者可能有自己的项目在进行，并且从互动中受益。

* 让更改可发现

在一个有着悠久历史的大型项目中，在调查问题或行为变化时，可能很难找到相关的更改。冗长且详细的提交信息可以方便地进行搜索，找出可能相关的更改。例如，使用 `git log --since 1 year --grep USB timeout`。

* 提供历史文档

提交信息用于为未来的开发者记录更改，可能是在几年或几十年后。这些未来的开发者甚至可能是你，原始作者。今天看起来显而易见的更改，过一段时间后可能就不那么显而易见了。

`git blame` 命令会将每一行源文件标注上进行更改的提交（哈希值和主题行）。

在确立了提交信息的重要性后，以下是一个良好的 FreeBSD 提交信息的元素：

* 以主题行开始

提交信息应以一行简短的主题开始，简要概括更改内容。主题应该能够让读者快速判断该更改是否值得关注。

* 保持主题行简短

主题行应尽可能简短，同时保留所需的信息。这有助于提高浏览 `git log` 的效率，并使 `git log --oneline` 能够在 80 列的行内显示短哈希和主题行。一个好的经验法则是保持在 63 个字符以内，如果可能，目标应是 50 个字符及更少。

* 如果适用，主题行前缀加上组件名

如果更改涉及某个特定组件，主题行可以加上该组件名并以冒号（`:`）结尾。

```sh
✓ foo: Add -k option to keep temporary data
```

在上述建议的 63 个字符限制内包括前缀，以便避免 `git log --oneline` 换行。

* 主题首字母大写

主题本身的首字母要大写。如果有前缀，除非必要（如 `USB:`），否则无需大写。

* 主题行不要以标点符号结尾

不要以句号或其他标点符号结尾。在这方面，主题行就像新闻头条，或电子邮件中的主题标题。

* 用空行分隔主题和正文

用空行分隔主题和正文。

一些微不足道的提交不需要正文，只有主题行。

```sh
✓ ls: Fix typo in usage text
```

* 限制消息为 72 列

`git log` 和 `git format-patch` 会将提交信息缩进四个空格。72 列换行使右边缘有一致的对齐边距。将消息限制在 72 个字符以内还可确保提交信息格式化后的补丁在 RFC 2822 建议的电子邮件行长度限制（78 个字符）以下。这个限制与多种可能渲染提交信息的工具兼容；较长的行可能导致换行不一致。

* 使用现在时、命令语气

这有助于简短的主题行，并提供一致性，包括自动生成的提交信息（例如，`git revert` 生成的提交信息）。当阅读提交主题列表时，这一点很重要。可以将主题视为完成句子“when applied, this change will …”的部分。

```sh
✓ foo: Implement the -k (keep) option
✗ foo: Implemented the -k option
✗ This change implements the -k option in foo
✗ -k option added
```

* 关注“做了什么”和“为什么做”，而非“如何做”

解释更改的目标是什么，为什么要做这个更改，而不是怎么做。

不要假设读者已经了解问题。解释该更改的背景和动机。如果有基准数据，可以包含在内。

如果更改存在限制或不完整的方面，请在提交信息中描述这些内容。

* 考虑是否有部分提交信息应该改为代码注释

有时，在写提交信息时，你可能会写出一两句话，解释更改中一些棘手或令人困惑的部分。这时，可以考虑是否将这些解释作为代码注释放在代码中，这样会更有价值。

* 为未来的自己写提交信息

在写提交信息时，你掌握了所有背景信息——是什么促使了这个更改，考虑并拒绝的替代方案，更改的局限性等等。想象一下，几年后你回顾这个更改时，写出提交信息的方式应该能够提供这些必要的背景。

* 提交信息应该独立存在

你可以包括对邮件列表帖子、基准结果网站或代码审查链接的引用。然而，提交信息应该包含所有相关信息，以防这些引用在未来不再可用。

类似地，提交可能会引用先前的提交，例如修复 bug 或还原提交。在引用的提交（或其他合适的简短引用）中，除了提交标识符（修订号或哈希值）外，还应该包括引用提交的主题行。随着每次版本控制系统的迁移（从 CVS 到 Subversion 再到 Git），以前系统中的修订标识符可能变得难以跟踪。‘

* 在页脚中包含适当的元数据

提交信息的最后一段可能包含一个或多个标准元数据标签。FreeBSD 中使用的标准标签有：

| 标签                    | 描述                               |
| --------------------- | -------------------------------- |
| PR                    | FreeBSD 问题报告（Bugzilla）编号         |
| Submitted by          | 原作者的 ID（如果不是提交者）                 |
| Reported by           | 报告问题的第三方 ID                      |
| Reviewed by           | 审阅者的 ID                          |
| Tested by             | 测试此更改的人员的 ID                     |
| Approved by           | 批准此更改的导师或代码所有者                   |
| Obtained from         | 来自另一个项目的更改源                      |
| MFC after             | 从 Current 合并到 Stable 前的时间周期      |
| MFC with              | 与此更改一同合并的关联提交                    |
| MFH                   | 用于合并请求的 Ports 季度分支               |
| Relnotes              | 是否应包含此更改的发布说明（是/否）               |
| Security              | 与安全问题相关的外部参考，例如 CVE 编号           |
| Sponsored by          | 资助此更改的组织或事件                      |
| Differential Revision | FreeBSD Phabricator 实例中代码审查的完整链接 |

“ID”表示一个 FreeBSD 用户 ID 或一个名字和电子邮件地址。多个 ID 可以作为逗号分隔的列表呈现，或者通过在后续行中重复元数据标签来表示。

***

**ED MASTE** 管理着 FreeBSD 基金会的项目开发工作。他还是选举产生的 FreeBSD 核心团队的成员。除了 FreeBSD，他还为多个其他开源项目做出了贡献，包括 LLVM、ELF 工具链、QEMU 和 Open vSwitch。他与妻子 Anna 以及儿子 Pieter 和 Daniel 一起住在加拿大的基奇纳 - 滑铁卢。
