首页
/ Angular Commit Message 格式规范详解:从提交信息结构到 Changelog 自动生成

Angular Commit Message 格式规范详解:从提交信息结构到 Changelog 自动生成

2026-09-07 17:53:32作者:明树来

Angular 官方仓库(angular/angular)对每一条 Git 提交信息都有着非常严格的格式约束,这套被称作 Angular Commit Message Format 的约定不仅让整个仓库的历史更易读、更易回溯,还使得提交历史可以被程序化解析,从而自动生成每个版本的 CHANGELOG。本文以仓库内规范文档 contributing-docs/commit-message-guidelines.md 为骨架,结合仓库内的实际提交样例、Husky 钩子脚本与 @angular/ng-dev 校验配置,完整讲解 header / body / footer 三段的写法、typescopesummary 的取值规则,以及 BREAKING CHANGEDEPRECATED、revert 提交的正确写法。读完本文,你将能写出符合 Angular 仓库标准、可直接通过 CI 校验并被 changelog 工具正确归类的提交信息,这套规范同样适用于任何采用 Conventional Commits 风格的多包仓库。

为什么需要"精确到几乎严苛"的提交格式

规范文档开篇即强调:Angular 对 Git 提交信息的格式有着**非常精确(very precise)**的规则,其收益是双重的:

  1. 更易读的提交历史(easier to read commit history):统一的 <type>(<scope>): <summary> 结构让每次改动在 log 中一目了然,是修 bug、加功能、还是重构一目可辨。
  2. 可分析的 changelog 生成(analyzable for changelog generation):结构化信息能够被脚本解析,直接驱动自动化 changelog 输出。

第二点在仓库中有直接实证:查看根目录的 CHANGELOG.md,每一个版本号下都按 scope(对应 npm 包)分组,以 "Commit | Type | Description" 的表格呈现,例如 22.2.0-next.4 版本下 commoncompilercompiler-clicoreformslanguage-servicemigrationsplatform-server 各占一节。表格中的 type 与描述,正是从各提交的 header 部分解析出来的:

| Commit | Type | Description |
| -- | -- | -- |
| 862a0c8ab3 | fix | avoid prototype member collisions |
| 58b0cb4735 | fix | scope animations declared in minified nested rules |

也就是说,commit message 中的每个字段都会直接反映在面向用户与下游开发者的版本发布说明里——格式一旦不统一,changelog 的归类与可读性就会被破坏,这正是 Angular 对提交格式如此严格的根本原因。

提交信息的整体三段式结构

Angular 的每条 commit message 由 header(头部)body(正文)footer(页脚) 三段组成,彼此之间以空行(BLANK LINE)分隔:

<header>
<空行>
<body>
<空行>
<footer>
  • header 是强制项,必须符合下述 "Commit Message Header" 格式;
  • body 是强制项,唯一例外是 typedocs 的提交;当 body 存在时,其长度必须至少 20 个字符,且需符合 "Commit Message Body" 规范;
  • footer 为可选项,用于承载 breaking changes、deprecation 信息,以及关联的 GitHub issue / PR 引用。

上述三段式模板在仓库的 .gitmessage 文件中被配置为 Git 的默认提交消息模板,开发者执行 git commit 时编辑器会自动填充 <type>(<scope>): <summary>、提示撰写动机并给出 100 字符参考刻度线。因此,理解该文件的内容就是理解这套规范的最直接入口。

Commit Message Header:<type>(<scope>): <short summary>

header 是提交的"门面",格式如下:

<type>(<scope>): <short summary>
  │       │             │
  │       │             └─⫸ Summary 用一般现在时,首字母不大写,句末不加句号
  │       │
  │       └─⫸ Commit Scope:包名(见 scope 章节)
  │
  └─⫸ Commit Type:build|ci|docs|feat|fix|perf|refactor|test

其中 <type><short summary>必填字段,(<scope>) 可选。注意 typescope 之间、scopesummary 之间没有空格,冒号后有一个空格。

Type:必须从这 8 个枚举值中取值

type 描述改动的性质,只能是下表中的一个:

Type 含义
build 影响构建系统或外部依赖的改动(示例 scope:gulp、broccoli、npm)
ci CI 配置文件与脚本的改动(示例:GitHub Actions 工作流)
docs 仅涉及文档的改动
feat 新功能
fix 缺陷修复
perf 提升性能的代码改动
refactor 既不修 bug 也不加功能的代码重构
test 补充缺失的测试或修正既有测试

仓库最近的提交历史是这些 type 最直观的样例。例如当前分支最新提交 ci: move packages/private under fw-general(无 scope 的 ci 提交,因改动跨包),而 CHANGELOG.mdfixfeat 在几乎每个包下都大量出现。

注意:header 中出现的 type/scope 只是消息文本层面的枚举约束,真正的强制执行在 CI 与本地钩子层完成,见下文"提交校验是如何落地的"一节。

Scope:以受影响 npm 包为准

scope 应当是受影响的 npm 包名(从阅读 changelog 的读者视角来感知)。支持的全部 scope 为:

  • animations
  • benchpress
  • common
  • compiler
  • compiler-cli
  • core
  • dev-infra
  • devtools
  • docs-infra
  • elements
  • forms
  • http
  • language-service
  • language-server
  • localize
  • migrations
  • platform-browser
  • platform-browser-dynamic
  • platform-server
  • router
  • service-worker
  • upgrade
  • vscode-extension
  • zone.js

上述 24 个 scope 并非任意命名,而是与仓库 .ng-dev/commit-message.mjs 中配置的 scopes 数组一一对应

export const commitMessage = {
  maxLineLength: Infinity,
  minBodyLength: 20,
  minBodyLengthTypeExcludes: ['docs'],
  scopes: ['animations', 'benchpress', 'common', /* ... */ 'zone.js'],
};

配置中还保留了一条醒目的同步约定注释:"If you update this, also update the docs."——即修改此处 scope 列表的同时必须同步更新本文档的 scope 清单,防止文档与校验规则脱节。对照可见文档 header 示意图中出现的 bazelpackagingchangelogngccve 等名称属于历史演进中被移除或合并的旧 scope,当前以文档 scope 清单与 .ng-dev/commit-message.mjs 配置为准

文档同时给出了"以包名为准"这一通用规则的少数例外:

  • dev-infra:用于 /scripts/tools 目录内与 dev-infra 相关的改动;
  • docs-infra:用于 adev 目录(angular.dev 文档应用)内的基础设施改动,如应用代码、工具链或配置;但若要修改文档内容本身(例如编辑 .md 文件),应使用不带 scope 的 docs:,而不是 docs-infra
  • migrations:用于 ng update 迁移逻辑的改动;
  • devtools:用于浏览器扩展 devtools/README.md 的改动;
  • 空字符串 / 不写 scope:适合跨所有包统一进行的 testrefactor 改动(如 test: add missing unit tests),以及不与任何特定包相关的 docs 改动(如 docs: fix typo in tutorial)。

Summary:一句话说清"做了什么"

summary 是 header 的收尾部分,写作时要遵守三条硬性规则:

  • 使用命令式、一般现在时:写 change,不要写 changedchanges
  • 首字母不要大写
  • 句末不要加句号(.

从仓库真实提交可以看出这些规则的落地形态:

fix(vscode-extension): handle escaped delimiters in inline template and styles highlighting
feat(core): add utility for testing directives
refactor(core): remove old `deferredImports` structure
docs: highlight the search tutorial @for block as Angular

Commit Message Body:解释"为什么"而非"做什么"

与 summary 相同,body 也使用命令式、一般现在时(fix 而非 fixed/fixes)。body 的核心作用是解释做出该改动的动机(why)——规范原文要求 "This commit message should explain why you are making the change",并推荐通过对比旧行为与新行为来呈现改动的影响面。

例如仓库最近一条 fix(forms) 提交的 body,先说明假设前提、再阐述推理、最后澄清边界,结构非常适合作为样板:

Signal forms can safely assume {readOnlyHint: false, untrustedContentHint: false}
given their context. A form _must_ alter page DOM ... We also assume returned
content is trusted ...

In the future, we might want to consider cases where application errors are
explicitly untrusted ... For now, that's out of scope ...

docs 类型的提交是唯一可以省略 body 的类型(对应 .ng-dev/commit-message.mjs 中的 minBodyLengthTypeExcludes: ['docs'])。而对于其他所有类型,body 一旦存在就不得短于 20 个字符——这个下限并非文档的软性建议,而是 minBodyLength: 20 这一真实校验阈值。

仓库为开发者提供了一个极佳的书写辅助:.gitmessage 中包含多个完整示例,包括简单重构(refactor(core): rename refreshDynamicEmbeddedViews to refreshEmbeddedViews)、文档改动、bug 修复与 breaking change 提交,并提示 body 每行应在 100 字符内换行。开发者写提交时可直接参考这些"标准答案"调整自己的措辞。

Commit Message Footer:breaking changes、deprecations 与 issue 关联

footer 是可选段,承载三类信息:breaking changes(破坏性变更)deprecations(废弃声明),以及本提交 close / 关联的 GitHub issue 与 PR。footer 中的注释引用通常以 Fixes #<issue number>Closes #<pr number> 的形式书写——仓库大量提交正文末尾都带 Fixes #65493Fixes #54164. 这样的引用。

BREAKING CHANGE 区块

标准结构如下:

BREAKING CHANGE: <breaking change 摘要>
<空行>
<破坏性变更的详细说明 + 迁移指引>
<空行>
<空行>
Fixes #<issue number>

写作要点:BREAKING CHANGE: 前缀之后先跟一行简短摘要,空一行后给出包含迁移指引(migration instructions)的详细说明。在 .gitmessage 中有一个教科书式的 feat(bazel): simplify ng_package by dropping esm5 and fesm5 完整示例——摘要一句点明 "esm5 and fesm5 format is no longer distributed",随后分场景(Angular CLI 用户无需操作、自行构建的用户需自行降级)撰写迁移指引,最后以 Fixes #1234 结尾。由于此类提交会影响所有下游使用者,Angular 仓库对 BREAKING CHANGE 提交的评审也格外严格。

DEPRECATED 区块

结构与 BREAKING CHANGE 对称:

DEPRECATED: <被废弃的内容>
<空行>
<废弃说明 + 推荐的替代升级路径>
<空行>
<空行>
Closes #<pr number>

要点:DEPRECATED: 后接一句简短的"什么被废弃了",空行后详细说明废弃原因,并明确给出推荐的更新路径(recommended update path),让使用者在版本升级前就能知晓替代方案。

Revert 提交:撤销类提交的特殊规则

如果某次提交用于撤销前一次提交,则:

  • header 必须以 revert: 开头,后跟被撤销提交的 header
  • body 中必须包含被撤销提交的 SHA,格式为 This reverts commit <SHA>
  • 同时清楚说明撤销该提交的理由。

这种"保留原始 header + 附 SHA"的做法,使撤销与被撤销之间可双向追溯,自动化工具与人工 review 都能快速定位一组"提交—回滚"对。

提交校验如何落地:Husky 钩子与 ng-dev

理解规范只是第一步,Angular 仓库用工具将这套规范"固化"为日常不可绕过的约束:

  1. 依赖安装时启用钩子:根目录 package.json 中声明了 "prepare": "husky"husky devDependency,clone 仓库并安装依赖后 Git hooks 自动就位。
  2. 提交信息校验钩子.husky/commit-msg 中调用 pnpm --silent ng-dev commit-message pre-commit-validate --file $1,在提交前根据 .ng-dev/commit-message.mjs 的配置(type 枚举、24 个 scope、minBodyLength: 20、docs 豁免)校验待提交的消息。
  3. 草稿恢复钩子.husky/prepare-commit-msg 调用 ng-dev commit-message restore-commit-message-draft,用于在提交前从暂存区恢复上次未写完的提交信息草稿,减少格式化负担。
  4. CI 层的兜底:上述钩子在校验工具异常时会输出 WARNING 而非直接 fail(set +e 包裹),真正的强制性校验主要由 CI / pull request 阶段完成,确保任何绕过本地钩子的提交也无法蒙混过关。

常见错误速查与实践建议

结合规范与仓库样例,编写 commit message 时最容易踩的坑包括:

  • summary 大写或带句号Fix: update readme. 是错误的,应为 fix: update readme
  • type 超出枚举chorestyleimprovement 等不在 8 个合法 type 内(.gitmessage 中的历史注释含 style,但当前规范与校验配置只接受 build|ci|docs|feat|fix|perf|refactor|test);
  • scope 与包名不符:改动 packages/core 却写 fix(common): ... 会误导 changelog 分组,scope 应使用 .ng-dev/commit-message.mjs 中列出的受支持包名;
  • body 不足 20 字符:非 docs 类型提交若带 body,过短将无法通过校验;
  • breaking change 无迁移指引BREAKING CHANGE: 之后必须给出可执行的迁移说明,否则下游使用者无从升级;
  • revert 不带 SHA:务必在 body 中写 This reverts commit <SHA>

对任何希望长期维护的开源仓库,这套"枚举 type + 包名 scope + 强制 body + 结构化 footer"的 Angular Commit Message 规范都值得直接复用:它让 git log 变得可扫读,让 changelog 可以机械生成,也让每一次版本发布背后的改动边界清晰可见。仓库中 CHANGELOG.md 那洋洋万行的版本记录,正是这套规范长期运转的最好证明。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.81 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.79 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
390