← 博客 · · ConvertCaseHQ 团队 · 阅读约需 7 分钟

Git 提交信息规范:主题行、大小写与 Conventional Commits

用英文写的规范 Git 提交信息,有一行简短的主题行(控制在 50 个字符左右),用祈使语气书写,写 “Add” 而不是 “Added”,末尾不加句号。然后空一行,再写一段在 72 个字符处换行、说明为什么要做这次修改的正文。至于大小写,选定一种惯例并坚持下去:带有 feat: 或 docs: 这类前缀时,前缀后的单词通常小写;没有前缀时,传统的建议是把第一个单词的首字母大写。

这些要求 Git 本身一条都不会强制执行,提交历史因此会慢慢走样。下面讲这些规则、每条规则的出处,以及如何让工具来检查,这样你就不必靠记忆。

一条好提交信息的结构

fix(editor): keep cursor position after paste

Pasting into an empty textarea moved the cursor to the start, because
the change handler replaced the value before restoring the selection.
Restore the selection after updating the value instead.

Fixes #142

它由三部分组成:说明这次提交做了什么的主题行,说明为什么这样做的正文,以及用来放引用和元数据的可选页脚(footer)。Git 自己的文档建议开头写 “a single short (no more than 50 characters) line summarizing the change, followed by a blank line and then a more thorough description”(一行简短的概述,不超过 50 个字符,然后空一行,再写更详细的说明)。第一个空行之前的所有内容都被当作提交的标题,git log --oneline、git shortlog 和 git format-patch 显示的就是它。

主题行:简短、祈使、不加句号

50 个字符是一个软性上限。Git 项目自己的 SubmittingPatches 指南就是这样称呼它的,并要求贡献者 “skip the full stop”(省略句号)。如果怎么也压不到 50 个字符以内,往往说明这次提交做了两件事。

同一份指南要求你 “describe your changes in imperative mood”(用祈使语气描述你的修改),就像在对代码库下命令:写 “make xyzzy do frotz”,而不是 “makes xyzzy do frotz” 或 “changed xyzzy to do frotz”。Chris Beams 的提交信息七条规则里有个好用的检验方法:好的主题行,能接在 “If applied, this commit will …”(如果应用,这次提交将……)后面,让这句话成立。

  • “If applied, this commit will add CSV export” 通顺。
  • “If applied, this commit will added CSV export” 不通顺。
  • “If applied, this commit will bug fixes” 同样不通顺,而且什么信息也没告诉读者。

祈使语气也和 Git 替你生成的信息一致,比如 Merge branch 'main' 和 Revert "…",这样你自己的提交在日志里读起来风格相同。

主题行的大小写:两派做法

对英文主题行来说,各种建议在这里确实互相冲突,正确答案取决于你的主题行是否以前缀开头。

惯例示例谁推荐
无前缀,首字母大写Add CSV exportTim Pope 2008 年写的提交信息笔记;Chris Beams 的七条规则
模块前缀,前缀后小写doc: clarify sign-off rulesGit 项目的 SubmittingPatches
类型前缀,描述小写feat: add CSV exportAngular 的提交指南;commitlint 的 conventional 配置

Git 项目的做法里有个细节值得借鉴。前缀之后只有第一个单词不再大写,而且只是因为它是第一个单词:本来就总是大写的词保持大写。它自己的例子是 refs: HEAD is also treated as a ref,因为 HEAD 总是写成大写。按同样的逻辑,就得到 fix: handle iOS paste 和 docs: update README badges。

我们的看法是:有前缀时,第一个单词小写,除非它是名称或缩写;没有前缀时,首字母大写。混用两种风格才是真正的错误,因为 git log --oneline 会把你的主题行变成一份列表,而每隔几行就切换一次大小写的列表看上去很草率。同样的道理用在文档上,请看我们的编写风格一致的文档一文。

Conventional Commits 规范到底怎么说

Conventional Commits 1.0.0 规范定义了这样的结构:

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

要点简述如下:

  • feat(新功能)和 fix(缺陷修复)是规范定义的两种类型,分别对应语义化版本(Semantic Versioning)中的 MINOR 和 PATCH 发布,破坏性变更则对应 MAJOR。其他类型也允许;规范提到了 build、chore、ci、docs、style、refactor、perf 和 test,这些是 commitlint 的 conventional 配置所推荐的,该配置基于 Angular 的约定。
  • 破坏性变更用冒号前的 ! 标记(feat!: drop Node 16 support),或者用 BREAKING CHANGE: 页脚标记,而且这个页脚必须是大写。
  • 读取该格式的工具不得将其视为区分大小写,BREAKING CHANGE 除外。关于类型应该小写还是大写,规范的 FAQ 说两种都可以,“but it's best to be consistent”(但最好保持一致)。

规范没有规定主题行的长度,也不要求使用祈使语气。这些来自 Git 的惯例。

commitlint 比规范更严格的地方

强制执行这种格式的常见办法是 commitlint 的 @commitlint/config-conventional,它的默认设置比规范走得更远。我们用 21 版检验了几条示例主题行:

主题行结果
feat: add CSV export通过
feat: Add CSV export未通过:主题行不得为句首大写(sentence case)
Feat: add CSV export未通过:类型必须小写
fix: handle empty textarea.未通过:不允许以句号结尾
fix: README typo未通过:同样被判定为句首大写
fix: typo in README通过

需要留意的是 README 这一行。以缩写或类名开头的主题行,比如 fix: README typo 或 feat: URLSearchParams support,会被拒绝,尽管 Git 项目会接受与之等价的写法。你可以改写,让缩写不在开头,或者在配置里放宽 subject-case 规则。默认的标题行(header)长度上限是 100 个字符,所以 commitlint 也不会要求你控制在 50 个字符以内。

正文:72 个字符处换行,并解释原因

Git 从不替你重新换行。Tim Pope 在 2008 年的解释至今成立:git log 会把信息缩进四个空格,所以在 80 列的终端里,为了右侧对称再留四个空格,就剩下 72 列;而且用 git format-patch 通过邮件发送提交时,已经换好行的文字也依然好读。如果你的编辑器不会自动换行,我们的按 N 字符换行工具可以按单词边界把段落在 72 个字符处换行。

正文用来写 diff 显示不出来的内容:问题是什么,为什么这样修,以及你试过但没有成功的做法。在命令行里,每个 -m 会成为独立的一段,所以 git commit -m "fix: keep cursor after paste" -m "Pasting into…" 会生成一行主题行、一个空行和一段正文。

让工具替你检查

  • VS Code:设置 "git.inputValidation": true。之后,当主题行超过 50 个字符(git.inputValidationSubjectLength)或其他行超过 72 个字符(git.inputValidationLength)时,提交框就会发出警告。
  • Vim:它自带的提交信息设置会在 72 个字符处给正文换行(textwidth=72),并且主题行超过 50 个字符的部分不再高亮。
  • 提交模板:git config commit.template ~/.gitmessage 会让每次提交在编辑器里一开始就显示你预先写好的提醒。
  • commit-msg 钩子(hook),无需任何依赖就能做检查。把下面的内容保存为 .git/hooks/commit-msg,并设为可执行:
#!/bin/sh
# Checks the subject line of every commit message.
subject=$(head -n 1 "$1")
length=${#subject}   # counts bytes in some shells; fine for ASCII subjects

if [ "$length" -gt 72 ]; then
  echo "commit-msg: subject is $length characters (limit 72, aim for 50)" >&2
  exit 1
fi
if [ "$length" -gt 50 ]; then
  echo "commit-msg: note, subject is $length characters (aim for 50)" >&2
fi
case "$subject" in
  *.) echo "commit-msg: drop the full stop at the end of the subject" >&2
      exit 1 ;;
esac
if [ -n "$(sed -n 2p "$1")" ]; then
  echo "commit-msg: leave line 2 blank, between subject and body" >&2
  exit 1
fi

它会在超过 50 个字符时给出警告,拒绝超过 72 个字符的主题行、末尾的句号和缺失的空行;真有必要时,git commit --no-verify 可以跳过它。提交前想快速数一下字数,可以把主题行粘贴到我们的字符计数器里。

清单就是这些:祈使语气的主题行、50 个字符左右、不加句号、整个仓库统一一条大小写规则、一个空行、在 72 个字符处换行且解释原因的正文,再加上钩子或 linter,让这些规则在忙碌的一周里也不会失守。

© 2026 ConvertCaseHQ. 保留所有权利。