Conventional Commits:结构化提交信息规范
fix bug 修好了登录页的问题 update code
这些信息对他人(甚至对未来的自己)毫无帮助。而 Conventional Commits 通过强制类型与描述结构,带来四大好处:
1. **可自动生成变更日志**:工具能根据提交类型甄别功能、修复、破坏性变更,自动整理出语义化版本号对应的 CHANGELOG。
2. **语义化版本一目了然**:`fix:` 说明是补丁发布,`feat:` 说明是次版本发布,`BREAKING CHANGE` 代表主版本变更。
3. **让团队沟通更清晰**:任何人都能从历史记录中快速理解变更的性质和范围。
4. **触发自动化流程**:CI/CD 可依据提交信息决定是否进行预发布、构建通知、测试策略切换等。
## 基本格式与结构
一条符合 Conventional Commits 的提交信息结构如下:
[optional scope]:
[optional body]
[optional footer(s)]
### 必需部分:类型、冒号、描述
- **`type`**:表明本次提交的**性质**。必须是预定义的关键词,如 `feat`、`fix`、`docs` 等。
- **`scope`**(可选):用括号包裹,表示本次变更**影响的模块或范围**,例如 `(auth)`、`(parser)`。
- **`!`**(可选):若在 `type/scope` 后紧跟 `!`,表示这是一个**破坏性变更**,可以直接替代 `BREAKING CHANGE` 页脚。例如 `feat!:` 或 `feat(api)!:`。
- **`description`**:对变更的简短描述,全部小写,句尾不加标点,使用祈使语气。
**完整示例:**
feat(auth): add login with Google OAuth
拆解:
- 类型:`feat`(新功能)
- 范围:`auth`
- 描述:`add login with Google OAuth`
### 可选部分:正文和页脚
- **Body(正文)**:在标题后空一行开始,可以详细说明变更原因、上下文、与之前行为的对比等。同样使用祈使语气。
- **Footer(页脚)**:常用于引用议题、标记破坏性变更或关联工单。每条页脚独占一行,多时用一行空行分隔。格式通常是 `token: value` 或 `BREAKING CHANGE: description`。
**完整示例:**
fix(parser): resolve incorrect markdown link parsing
The previous regex did not handle parentheses inside link text, causing crash on specific edge cases. Now the parser first extracts raw link before decoding entities.
Closes #452
BREAKING CHANGE: the output node structure now includes a
separate raw field for link data.
## 常用提交类型详解
Conventional Commits 规范鼓励使用一套标准类型,但在团队内可根据需要扩展。以下为最核心的类型:
| 类型 | 含义 | 版本影响 |
|------------|--------------------------------------------------------------|----------|
| `feat` | 引入新功能 | 次版本号 |
| `fix` | 修复一个 Bug | 补丁号 |
| `docs` | 仅修改了文档(README、注释、API docs 等) | 无 |
| `style` | 不影响代码含义的改动(空格、格式化、分号补全等) | 无 |
| `refactor` | 既非新功能也非 Bug 修复的代码重构 | 无 |
| `perf` | 提高性能的代码变更 | 无/补丁 |
| `test` | 增加或修改测试 | 无 |
| `chore` | 构建过程或辅助工具的变动(依赖更新、脚本修改等) | 无 |
| `ci` | 持续集成配置或脚本的变动 | 无 |
| `build` | 影响构建系统或外部依赖的变更 | 无 |
> **注意**:`feat` 和 `fix` 类型直接影响语义化版本号的自动提升。`perf` 如果包含性能修复可能视为补丁,其他类型通常不触发版本变更。
## 破坏性变更(BREAKING CHANGE)
破坏性变更标志着当前版本不再向后兼容。有两种标记方式:
1. **在类型或 scope 后加 `!`**:例如 `feat!:` 或 `refactor(api)!:`。这种方式更简洁,适合标题直接表达。
2. **在页脚中显式声明**:在页脚部分添加 `BREAKING CHANGE: <描述>`。即使使用了 `!`,页脚也常用于详细说明迁移指南。
**示例一:使用 `!`**
feat!: drop support for Node 10
The library now requires Node >= 14. Please check your runtime before upgrading.
**示例二:页脚声明**
refactor(auth): move token refresh logic to middleware
BREAKING CHANGE: The refreshToken function has been removed from
the public API. Use session.refresh() instead.
## 范围(Scope)的最佳实践
Scope 的粒度需要团队协商,常见策略:
- **按模块**:`feat(cart): add discount coupon`、`fix(login): resolve session timeout`
- **按层级**:`fix(ui): correct button alignment`、`refactor(db): change connection pool logic`
- **无范围**:当变更影响全局或范围难以界定时,可以省略 scope,例如 `chore: update eslint rules`
不必为所有提交强行添加 scope,清晰的目的远比追求形式重要。
## 与语义化版本(SemVer)的对应关系
Conventional Commits 能与 SEMVER 完美配合,实现自动版本决策:
| 提交类型在历史记录中 | SEMVER 变化 | 举例 |
|---------------------|-------------|------|
| 含有 `BREAKING CHANGE` 或带有 `!` 的类型 | MAJOR 版本增加 | `1.2.3` → `2.0.0` |
| 至少一个 `feat` 类型,无破坏性变更 | MINOR 版本增加 | `1.2.3` → `1.3.0` |
| 仅有 `fix` 等补丁类型,无新功能 | PATCH 版本增加 | `1.2.3` → `1.2.4` |
工具如 [standard-version](https://github.com/conventional-changelog/standard-version)、[semantic-release](https://github.com/semantic-release/semantic-release) 可读取提交历史自动执行上述逻辑,并生成 CHANGELOG。
## 团队落地实操
### 1. 建立提交约定文档
将类型定义、scope 列表、破坏性变更声明方式、是否允许 `WIP` 等写入 `CONTRIBUTING.md` 或项目 Wiki,确保新成员快速对齐。
### 2. 使用交互式提交工具
- **[commitizen](https://github.com/commitizen/cz-cli)**:通过 `cz` 命令引导填写,避免格式错误。
- **VS Code 插件**:如 “Conventional Commits” 插件,提供可视化选择界面。
### 3. 引入提交信息检查
通过 Git Hooks 或 CI 自动校验提交格式:
- **[commitlint](https://github.com/conventional-changelog/commitlint)**:配置 `@commitlint/config-conventional` 即可在本地拦截不合规提交。
- **GitHub Actions / GitLab CI**:在 PR 阶段执行格式检查,阻止不规范合并。
**commitlint 配置示例(`commitlint.config.js`):**
```javascript
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', [
'feat', 'fix', 'docs', 'style', 'refactor',
'perf', 'test', 'chore', 'ci', 'build'
]],
'scope-empty': [0, 'never'],
},
};
4. 自动生成 CHANGELOG 并发布
- 结合 standard-version 在
package.json中配置脚本:"release": "standard-version",一键完成版本号更新、CHANGELOG 生成、Git 标签创建。 - 使用 semantic-release 在 CI 环境中全自动发布,无需人工干预版本号。
常见误区与注意事项
- 描述不用祈使语气:应使用
add而非added,fix而非fixed,因为提交本身应体现“应用这个补丁后会做什么”。 - 谨慎使用超大范围:一个提交只做一件事。如果一个
feat里面混入了一个fix,请拆分为两次提交。 - 破坏性变更未声明:即使 API 不兼容的小改动也要明确标记,避免使用者在不知情的情况下升级。
- 忽略正文:当
fix修复了一个诡异的 Bug 时,用三行正文解释原因能省去数小时的二次调查。
实际案例:一次完整的提交流程
假设你要为一个电商应用新增“愿望清单”功能,同时发现支付模块有内存泄漏。合理的提交历史应如下:
docs: update API documentation for wishlist endpoints
feat(cart): add wishlist feature
- Users can now save products to wishlist
- Wishlist persists across sessions via database
- Includes basic add/remove/list endpoints
Closes #321
fix(payment): resolve memory leak in transaction listener
The listener was not detaching from event emitter after
transaction complete, causing retained references.
Closes #458