Git 提交规范¶
怎么写 commit message 才清晰、可检索、方便协作。命令见 Git 命令集;本地校验见 Gitignore 与 Hooks。
相关文档:Git 命令集 · Gitignore 与 Hooks · 协作流程
阅读指引¶
- 概念:提交说明由哪几部分组成、为什么要约定
- 总览:常见格式、type 表、相关命令
- 场景:按实际写法与踩坑展开,含正反例
- 文末速查:现象 → 建议
提交说明是什么¶
每次 git commit 都会留下一条 提交说明(commit message),包含 标题(subject,通常第一行)和可选的 正文(body)、页脚(footer / trailer)。
一句话:「给这次改动贴标签,让未来的自己和队友能看懂 改了什么、为什么改。」
效果:说明随 commit 永久保存在 Git 历史里;git log、PR、changelog、自动化发版都会读到它。写得好不好,直接影响 code review 和排障效率。
总览¶
常见格式一览¶
| 格式 | 适用 | 示例首行 | 场景 |
|---|---|---|---|
| 祈使句单行 | 个人项目、小改动 | Fix login redirect loop | 最低要求 |
| Conventional Commits | 开源 / 团队 / 自动化 changelog | feat(auth): add OAuth callback | Conventional |
| 带正文多行 | 复杂改动、需要解释「为什么」 | 首行 + 空行 + 段落 | 多行正文 |
| Breaking change | 不兼容 API 变更 | feat!: drop Node 16 或 footer | 破坏性变更 |
| Trailer 页脚 | 署名、联合作者、Issue 关联 | Closes #42 | Trailer |
Conventional Commits 规范 被 Angular、Semantic Release 等广泛采用;团队可以裁剪(例如只要 type: 不要 scope),但结构清晰比死记标准更重要。
Conventional Commits:type 速查¶
| type | 含义 | 示例首行 |
|---|---|---|
feat | 新功能 | feat: add export to CSV |
fix | 修复 bug | fix: handle null user id |
docs | 仅文档 | docs: update install steps |
style | 格式(不影响逻辑) | style: format with prettier |
refactor | 重构(非新功能非修 bug) | refactor: extract auth middleware |
perf | 性能 | perf: cache config parse result |
test | 测试 | test: add login API cases |
build | 构建 / 依赖 | build: bump webpack to 5.94 |
ci | CI 配置 | ci: add matrix for Node 20/22 |
chore | 杂项维护 | chore: update gitignore |
revert | 回滚某 commit | revert: feat(auth): add OAuth |
可选 scope(括号内模块名):feat(api): ...、fix(ui): ...。
表示 不兼容变更 时在 type 后加 !:feat(api)!: remove v1 endpoints。
本篇相关 Git 命令¶
| 命令 | 干什么 | 场景 | 命令集 |
|---|---|---|---|
git commit -m '...' | 单行说明 | 单行 | git commit |
git commit(无 -m) | 打开编辑器写多行 | 多行正文 | git commit |
git commit --amend | 改最近一次说明 | amend | git commit |
git commit --amend --no-edit | 并入改动、说明不变 | amend | git commit |
git revert <hash> | 用新 commit 撤销历史 | 已 push | git revert |
git log --oneline | 看历史标题 | 写 revert 时找 hash | git log |
git commit --no-verify | 跳过 commit hook | 见 hooks 文档 | git commit |
写好标题的通用原则¶
| 原则 | 建议 |
|---|---|
| 语气 | 祈使句(英文:Add 不是 Added;中文可用「添加」「修复」) |
| 长度 | 首行约 50 字符内(软限制,超出可换行写 body) |
| 内容 | 说 做了什么,细节和动机放正文 |
| 标点 | 首行末尾 一般不加句号 |
| 粒度 | 一个 commit 一个完整意图;WIP 不要直接进 main |
场景:团队没定规范时的最低要求¶
常见场景:个人仓库、小团队尚未写 CONTRIBUTING。
一句话:「首行让人 5 秒内看懂这次改动目的。」
# 可以:动词开头、具体
git commit -m "Fix crash when profile avatar is missing"
# 可以:中文项目
git commit -m "修复头像为空时个人页崩溃"
# 避免:太泛
git commit -m "update"
git commit -m "fix bug"
git commit -m "WIP"
# 避免:把 issue 号当唯一说明
git commit -m "#123"
场景:Conventional Commits 怎么写¶
常见场景:开源项目、需要自动生成 changelog、PR 标题与 commit 对齐。
# 新功能 + scope
git commit -m "feat(billing): support annual subscription"
# 修 bug
git commit -m "fix: prevent double submit on checkout"
# 文档-only
git commit -m "docs: add API rate limit section"
# 依赖升级(别和功能混在一起)
git commit -m "build: upgrade eslint to 9.x"
效果:工具可按 feat / fix 分类发版;reviewer 从 git log 快速过滤类型。
Note
进仓库前 先看该项目 的 CONTRIBUTING.md、PR 模板或已有 git log --oneline -20,跟现有风格一致比照搬全网标准更重要。
场景:多行说明 — 标题 + 正文¶
常见场景:重构、行为变更、需要交代 为什么 而不是 改了哪些文件。
一句话:「首行摘要,空一行,正文写动机、取舍、风险。」
git commit -m "$(cat <<'EOF'
fix(auth): reject expired refresh tokens
Previously expired tokens still passed middleware when
clock skew was under 30s. Align with OAuth spec and
return 401 with clear error code.
Refs #8842
EOF
)"
或用编辑器(不设 -m):
git commit
# 编辑器中:
# fix(auth): reject expired refresh tokens
#
# Previously expired tokens still passed...
效果:git log 默认只显示首行;git log -p 或 git show 能看到正文。
场景:改最近一次说明(还没 push)¶
常见场景:commit 完发现 typo、漏 git add 某个文件。
# 只改说明
git commit --amend
# 说明写在一行
git commit --amend -m "feat: add password reset email"
# 补文件进上一次 commit,说明不变
git add forgotten.ts
git commit --amend --no-edit
一句话:「最后一笔账还没 push,可以涂改。」
效果:commit hash 会变;仅适用于尚未 push 或仅你使用的分支。
Warning
已经对 共享远程分支 push 过的 commit,不要用 amend 改历史(除非团队明确允许并配合 force push)。见 已 push 场景。
场景:已经 push 到远程了¶
常见场景:说明写错、功能要撤回,但 commit 已在 origin/main 上。
| 情况 | 建议 | 示例 |
|---|---|---|
| 仅说明有错、改动正确 | 新开 commit 修正描述,或团队允许时 revert 再重做 | docs: fix typo in previous commit message(空 commit 慎用) |
| 要撤销整次改动 | git revert(保留历史) | git revert abc1234 |
| 个人 feature 分支、无人基于它开发 | 可 rebase/amend + push --force-with-lease | 见 协作流程 |
# 推荐:用 revert 生成「反向」commit
git revert HEAD
git commit # 编辑器里默认有 revert 说明,可改
# 不推荐在 shared main 上:
# git reset --hard HEAD~1 && git push --force
一句话:「公共历史已发布,优先 追加 新 commit,而不是改旧账。」
场景:破坏性变更(Breaking Change)¶
常见场景:删 API、改配置项默认值、主版本号升级。
# 方式 1:type 后加 !
git commit -m "$(cat <<'EOF'
feat(api)!: remove legacy /v1 users endpoint
BREAKING CHANGE: clients must use /v2/users. v1 removed
after deprecation period noted in 2025-Q4 changelog.
EOF
)"
# 方式 2:footer 里写 BREAKING CHANGE:
git commit -m "feat: switch default TLS to 1.3
BREAKING CHANGE: TLS 1.0/1.1 no longer supported."
效果:发版工具可据此 bump 主版本号;使用者能在 log 里搜 BREAKING CHANGE。
场景:Trailer 与 Issue 关联¶
Trailer 是 message 末尾 的标准化行(Key: Value),常见于开源与 GitHub/GitLab 集成。
| Trailer | 用途 | 示例 |
|---|---|---|
Closes #123 / Fixes #123 | 合并后关 Issue | GitHub 自动识别 |
Refs #123 | 仅关联不关 | 部分完成 |
Co-authored-by: Name <email> | 联合作者 | 多人 pair / squash 前 |
Signed-off-by: Name <email> | DCO 签署 | Linux 基金会等项目 |
Reviewed-by: Name <email> | 记录 review | 部分企业规范 |
git commit -m "$(cat <<'EOF'
fix: validate upload MIME type
Co-authored-by: Alex Chen <alex@example.com>
Closes #456
EOF
)"
常见场景:
- PR Squash merge 时把多位作者的 Co-authored-by 写进最终 commit
- IDE / AI 工具 自动插入 trailer — 提交前看一眼,去掉不需要或违反项目政策的行
Note
是否允许某种 trailer 以 目标仓库 的 CONTRIBUTING / DCO 为准;本文只介绍常见写法,不代替项目政策。
场景:PR 合并后 commit 长什么样¶
常见场景:在 GitHub/GitLab 上 merge PR,本地 history 出现 merge commit 或 squash commit。
| 合并方式 | 典型 log 表现 | 说明怎么写 |
|---|---|---|
| Merge commit | Merge pull request #99 from ... | 保留分支上各 commit;PR 标题常作 merge 说明 |
| Squash merge | 一条 feat: ... (#99) | PR 标题 往往变成最终 commit 首行 — 开 PR 时就按规范写标题 |
| Rebase merge | 分支上多条线性 commit | 每个 commit 仍应保持原子、说明清晰 |
一句话:「在平台上 merge 时,PR 标题 ≈ 未来的 commit 标题(尤其 squash)。」
场景:与 commit-msg hook 配合¶
团队可在 commit-msg hook 里校验格式(例如必须 feat: 开头、首行非空)。示例与安装见 Gitignore 与 Hooks — commit-msg。
效果:不合规时在本地 commit 失败,比 push 后 CI 打回更早。
Warning
不要习惯性 git commit --no-verify 绕过校验;hook 报错应修 message 或按团队流程申请例外。
场景:好 vs 差 — 对照示例¶
| 差 | 好 | 原因 |
|---|---|---|
update code | fix(ui): prevent modal focus trap | 具体、可检索 |
fix bug and add feature | 拆成两个 commit | 一个 commit 一件事 |
Fixed the thing. | fix: handle timeout in payment webhook | 祈使句、无句号、有范围 |
| 正文粘贴 200 行 diff | 正文写 why 与 trade-off | diff 在 git show 里已有 |
feat: ... + 无关 chore: bump deps 混提交 | 依赖升级单独 build: commit | 便于 revert / bisect |
速查:现象 → 建议¶
| 现象 | 建议 |
|---|---|
| 不知道写什么 | 首行:做了什么 + 哪里;正文:为什么 |
| 改动跨很多模块 | 拆 commit,或 scope 写主模块 |
| commit 完发现漏文件 | 未 push → git add + commit --amend --no-edit |
| 说明 typo 已 push 到 main | 不要 amend main;必要时 revert 或 follow-up commit |
| 项目有 CONTRIBUTING | 以项目文档为准,本篇作通用参考 |
| hook 拒绝提交 | 读报错改格式;见 hooks 文档 |
| 要写联合作者 | 用 Co-authored-by trailer;merge 策略与平台文档一致 |
更多命令:Git 命令集 — git commit。