TL;DR
Conventional Commits 是一个用于编写标准化提交消息的规范,遵循 type(scope): description 格式。它支持使用语义化版本(SemVer)自动版本管理、自动 CHANGELOG 生成和流水线化的 CI/CD 管道。借助 Commitlint、Commitizen、Semantic Release 和 Release Please 等工具,团队可以完全自动化发布流程。在 2026 年,Conventional Commits 已成为 Angular、Vue、Babel、Electron 和数千个开源项目和企业采用的事实标准。
Key Takeaways
- Conventional Commits 遵循 type(scope): description 格式,附带可选的 body 和 footer 部分
- feat 和 fix 等提交类型直接对应 SemVer 的 MINOR 和 PATCH 版本升级
- 破坏性变更(BREAKING CHANGE footer 或类型后的 !)触发 MAJOR 版本升级
- Commitlint + Husky 在本地强制执行规范;GitHub Actions 在 CI 中强制执行
- Semantic Release 和 Release Please 完全自动化版本管理、变更日志和 npm 发布
- Changesets、Lerna 和 Turborepo 等 Monorepo 工具原生集成 Conventional Commits
Conventional Commits 是一个叠加在提交消息之上的轻量级规范,提供了一种结构化格式来传达代码库变更的性质。通过遵循一个简单的约定,团队可以实现自动化版本管理、变更日志生成和发布管理。本指南涵盖了从规范本身到完整 CI/CD 自动化的所有内容,包括可以直接复制到项目中的真实配置文件。
什么是 Conventional Commits 以及为什么重要
Conventional Commits 是一个定义提交消息标准格式的规范。格式简单:每条提交消息由类型、可选的范围和描述组成。提交正文和脚注提供额外上下文。这种结构使提交对机器可读,使工具能够自动化版本管理和变更日志生成。
没有提交规范,变更日志必须手动编写,版本升级靠猜测,提交历史变成一堆不可读的消息,如"修了点东西"、"wip"和"最终更新"。Conventional Commits 通过给每次提交赋予清晰的语义含义来解决所有这些问题。
该规范受 Angular 提交指南启发,后来发展为独立标准。在 2026 年,它被数千个项目采用,包括 Vue.js、Babel、Electron、Yargs、Lerna 和许多企业代码库。
规范格式
每条 conventional commit 消息遵循以下结构。type 和 description 是必需的。scope、body 和 footer 是可选的。头部(第一行)不应超过 72 个字符。
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
# Examples:
feat: add user registration endpoint
fix(auth): resolve token expiration bug
docs(readme): update installation instructions
feat(api)!: change response format for /users
BREAKING CHANGE: The /users endpoint now returns
an object instead of an array.提交类型参考
规范定义了两个必需类型(feat 和 fix)并推荐了其他几个。以下是常用提交类型及其用途的完整列表:
| Type | Description | SemVer |
|---|---|---|
| feat | A new feature for the user | MINOR |
| fix | A bug fix for the user | PATCH |
| docs | Documentation only changes | None |
| style | Code style (formatting, semicolons) | None |
| refactor | Code change (no bug fix, no feature) | None |
| perf | Performance improvement | PATCH |
| test | Adding or correcting tests | None |
| build | Build system or dependencies | None |
| ci | CI configuration and scripts | None |
| chore | Routine tasks (no src/test change) | None |
| revert | Reverts a previous commit | Varies |
范围约定和最佳实践
scope 提供关于代码库哪个部分受影响的额外上下文。它放在类型后面的括号中。scope 应该在项目中保持一致,并在贡献指南中记录。
常见的 scope 策略包括使用模块名(auth、api、ui)、层名(controller、service、model)或功能区域(checkout、onboarding、dashboard)。在 monorepo 中,包名通常用作 scope。
# Module-based scopes
feat(auth): add two-factor authentication
fix(payments): correct tax calculation
refactor(database): optimize query builder
# Layer-based scopes
fix(controller): validate request params
refactor(service): extract email service
test(repository): add integration tests
# Monorepo package scopes
feat(@myorg/ui): add DatePicker component
fix(@myorg/api): handle timeout errors
build(@myorg/shared): update tsconfig破坏性变更
破坏性变更是提交消息中最重要的信息,因为它们触发 MAJOR 版本升级。有两种方式表示破坏性变更:
第一种方法在提交脚注中使用 BREAKING CHANGE footer(必须大写)。第二种方法在类型或 scope 后追加感叹号(!)。两种方法可以组合使用以获得最大清晰度。
# Method 1: BREAKING CHANGE footer
feat(api): change user response format
The /api/users endpoint now returns paginated results
instead of a flat array.
BREAKING CHANGE: The response shape changed from
User[] to { data: User[], meta: { page, total } }.
All API consumers must update their parsing logic.
# Method 2: ! after type/scope
feat(api)!: change user response format
# Method 3: Both combined (recommended)
feat(api)!: change user response format
BREAKING CHANGE: Response changed from User[] to
{ data: User[], meta: { page, total } }.实际提交消息示例
以下是好的和不好的提交消息示例。好的消息遵循规范并提供清晰的上下文。不好的消息模糊、不一致或缺少类型前缀。
Good vs Bad Commit Messages
# GOOD — follows the convention
feat(auth): add OAuth 2.0 login with Google
fix(cart): prevent negative quantity on update
docs(api): add OpenAPI spec for /orders endpoint
perf(search): add database index for full-text queries
build(deps): upgrade React from 18 to 19
revert: feat(auth): add OAuth 2.0 login with Google
# BAD — avoid these patterns
added new feature # missing type prefix
fix: fix stuff # too vague
fix: add dark mode toggle # wrong type (should be feat)
feat: add login and fix header # multiple changes
FEAT: add search feature # uppercase type语义化版本集成
Conventional Commits 直接映射到语义化版本(SemVer)。这种映射使自动版本升级成为可能。理解这种关系对于设置自动化至关重要。
当生成发布时,工具扫描自上次发布以来的所有提交。如果任何提交有 BREAKING CHANGE,则升级主版本。否则,如果任何提交类型为 feat,则升级次版本。否则,fix 和 perf 提交导致修订版本升级。docs、style、refactor、test、build、ci 和 chore 等类型默认不触发任何发布。
| Commit | Version Bump | Example |
|---|---|---|
| fix(...): ... | PATCH (1.0.0 -> 1.0.1) | Bug fixes, patches |
| feat(...): ... | MINOR (1.0.0 -> 1.1.0) | New features |
| feat(...)!: ... or BREAKING CHANGE | MAJOR (1.0.0 -> 2.0.0) | Breaking API changes |
| docs, style, refactor, test, ci | No release | Internal changes |
| perf(...): ... | PATCH (1.0.0 -> 1.0.1) | Performance improvements |
Commitlint 设置
Commitlint 是提交消息的 linter。它检查每条提交消息是否遵循 Conventional Commits 规范。结合 Husky,它在每次提交时自动运行,防止不符合规范的消息进入仓库。
安装 commitlint 和 conventional 配置,然后创建配置文件并设置 Husky commit-msg 钩子:
Installation
npm install --save-dev @commitlint/cli @commitlint/config-conventional
# Or with pnpm
pnpm add -D @commitlint/cli @commitlint/config-conventionalcommitlint.config.js
// commitlint.config.js
export default {
extends: ['@commitlint/config-conventional'],
rules: {
// Type must be one of these
'type-enum': [
2,
'always',
[
'feat', 'fix', 'docs', 'style', 'refactor',
'perf', 'test', 'build', 'ci', 'chore', 'revert',
],
],
// Scope must be lowercase
'scope-case': [2, 'always', 'lower-case'],
// Header max length 100 characters
'header-max-length': [2, 'always', 100],
// Description must not be empty
'subject-empty': [2, 'never'],
// Type must not be empty
'type-empty': [2, 'never'],
},
};Husky commit-msg Hook
# Install Husky
npm install --save-dev husky
npx husky init
# Create the commit-msg hook
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
chmod +x .husky/commit-msgCommitizen (cz-cli) 交互式提交
Commitizen 提供编写 conventional commit 消息的交互式提示。开发者回答关于类型、范围和描述的问题,而不是手动输入格式。这消除了格式错误,让新团队成员更容易上手。
# Install Commitizen globally
npm install -g commitizen
# Initialize with conventional changelog adapter
commitizen init cz-conventional-changelog --save-dev --save-exact
# Or configure manually in package.json:
# "config": {
# "commitizen": {
# "path": "cz-conventional-changelog"
# }
# }
# Now use "cz" or "git cz" instead of "git commit"
# The interactive prompt guides you through:
# type -> scope -> description -> body -> breaking changes -> issues.czrc Configuration
{
"path": "cz-conventional-changelog",
"maxHeaderWidth": 100,
"maxLineWidth": 100,
"defaultType": "feat",
"defaultScope": ""
}Standard Version 和 Release Please
Standard Version 是一个遵循 SemVer 和 Conventional Commits 规范的版本管理工具。它读取提交历史,确定版本升级,更新 package.json,生成或更新 CHANGELOG.md,并创建 git 标签。注意:Standard Version 现已弃用,推荐使用 Release Please。
Release Please 是 Google 维护的 Standard Version 继任者。它创建包含版本升级和变更日志更新的发布拉取请求。当 PR 被合并时,发布即完成。
Release Please GitHub Action
# .github/workflows/release-please.yml
name: Release Please
on:
push:
branches: [main]
permissions:
contents: write
pull-requests: write
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: googleapis/release-please-action@v4
with:
release-type: node
token: \${{ secrets.GITHUB_TOKEN }}Semantic Release:完全自动化发布
Semantic Release 将自动化推到极致。它分析提交,确定版本升级,生成发布说明,发布到 npm(或其他注册表),创建 GitHub Release,并更新变更日志。它是 2026 年完全自动化发布的黄金标准。
以下是典型 Node.js 项目的完整 Semantic Release 配置:
# Install Semantic Release and plugins
npm install --save-dev semantic-release @semantic-release/commit-analyzer \
@semantic-release/release-notes-generator @semantic-release/changelog \
@semantic-release/npm @semantic-release/github @semantic-release/gitrelease.config.js
// release.config.js
export default {
branches: ['main'],
plugins: [
// Analyze commits to determine version bump
'@semantic-release/commit-analyzer',
// Generate release notes from commits
'@semantic-release/release-notes-generator',
// Update CHANGELOG.md
['@semantic-release/changelog', {
changelogFile: 'CHANGELOG.md',
}],
// Publish to npm
'@semantic-release/npm',
// Create GitHub release with notes
'@semantic-release/github',
// Commit CHANGELOG.md and package.json back
['@semantic-release/git', {
assets: ['CHANGELOG.md', 'package.json'],
message: 'chore(release): \${nextRelease.version} [skip ci]',
}],
],
};Semantic Release GitHub Action
# .github/workflows/semantic-release.yml
name: Semantic Release
on:
push:
branches: [main]
permissions:
contents: write
issues: write
pull-requests: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: actions/setup-node@v4
with: { node-version: 22, cache: npm }
- run: npm ci
- run: npx semantic-release
env:
GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: \${{ secrets.NPM_TOKEN }}GitHub Actions 中的提交消息检查
即使有本地钩子,CI 级别的强制执行也是必不可少的,因为钩子可以被绕过。一个检查提交消息的 GitHub Actions 工作流确保没有不符合规范的提交到达主分支。
# .github/workflows/commitlint.yml
name: Commitlint
on:
push: { branches: [main] }
pull_request: { branches: [main] }
jobs:
commitlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: actions/setup-node@v4
with: { node-version: 22 }
- run: npm install @commitlint/cli @commitlint/config-conventional
- run: npx commitlint --from \${{ github.event.pull_request.base.sha || 'HEAD~1' }} --to HEAD --verboseMonorepo 约定
Monorepo 对 Conventional Commits 提出了独特的挑战,因为单个仓库包含多个包。scope 字段变得至关重要,用于识别提交影响哪个包。以下是主要方法:
Changesets 是一个流行的 monorepo 版本管理工具。它使用单独的变更集文件而不是仅依赖提交消息。每个 PR 添加一个描述变更及其影响的变更集文件。发布时,Changesets 读取所有待处理的变更集,升级版本,并为每个受影响的包生成变更日志。
Monorepo Commit Examples
# Lerna / Nx monorepo with package scopes
feat(web): add dashboard page
fix(api): handle rate limiting correctly
build(shared): upgrade TypeScript to 5.7
test(mobile): add login screen snapshot tests
# Turborepo with app/package scopes
feat(apps/web): integrate Stripe payments
fix(packages/ui): correct Button hover state
ci(turbo): add remote caching to pipeline
# Changesets workflow
npx changeset # create changeset file
git add .changeset/ && git commit -m "feat(api): add rate limiting"
npx changeset version # bump versions when ready
npx changeset publish # publish to npmAngular vs Conventional Commits vs Gitmoji
2026 年广泛使用三种提交约定。Angular Commit Convention 是 Conventional Commits 的原始灵感来源,使用相同的 type(scope): description 格式。Conventional Commits 是一个独立规范,更轻量更灵活。Gitmoji 使用 emoji 前缀而非文本类型。以下是它们的比较:
| 方面 | Angular | Conventional Commits | Gitmoji |
|---|---|---|---|
| 格式 | type(scope): subject | type(scope): description | :emoji: description |
| 工具 | Commitlint, ng-commit | Commitlint, Commitizen, SR | gitmoji-cli, gitmoji-changelog |
| SemVer 映射 | 是(严格) | 是(内置) | 部分支持 |
| 采用度 | Angular 生态 | 通用(最流行) | 增长中的小众 |
| 可读性 | 良好 | 良好 | 视觉化(依赖 emoji) |
| 灵活性 | 严格 | 可扩展 | 灵活 |
自动生成 CHANGELOG.md
Conventional Commits 使完全自动化的变更日志生成成为可能。工具读取提交历史,按类型分组变更,并生成格式良好的 markdown 文件。以下是工具生成的内容:
# Auto-generated CHANGELOG.md example:
## [2.3.0](https://github.com/org/repo/compare/v2.2.0...v2.3.0) (2026-02-28)
### Features
* **auth:** add two-factor authentication ([#142](https://github.com/org/repo/issues/142))
* **search:** implement full-text search with Elasticsearch ([abc1234](https://github.com/org/repo/commit/abc1234))
### Bug Fixes
* **payments:** correct tax calculation for EU ([#156](https://github.com/org/repo/issues/156))
* **ui:** fix modal z-index on Safari ([def5678](https://github.com/org/repo/commit/def5678))
### Performance Improvements
* **database:** add composite index for user queries ([ghi9012](https://github.com/org/repo/commit/ghi9012))PR 标题约定
许多团队也在拉取请求标题上强制执行 Conventional Commits 格式。使用 squash 合并时,PR 标题成为合并提交消息,使 PR 标题检查与提交消息检查同样重要。GitHub Actions 可以强制执行:
# .github/workflows/pr-title.yml
name: PR Title Lint
on:
pull_request:
types: [opened, edited, synchronize]
jobs:
lint-pr-title:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
with:
types: |
feat
fix
docs
style
refactor
perf
test
build
ci
chore
revert
requireScope: false团队采用指南
在团队中采用 Conventional Commits 需要渐进式推出。一夜之间强制执行新约定会导致挫败和抵触。以下是经过验证的四阶段采用策略:
- 阶段 1(第 1-2 周):在团队会议中介绍规范。分享本指南。在 CONTRIBUTING.md 中添加解释约定的部分。暂时不强制执行——只是建立意识。
- 阶段 2(第 3-4 周):添加 Commitizen 以便开发者使用交互式提示。以仅警告模式设置 Commitlint。创建共享的 .gitmessage 模板。开发者可以逐步加入。
- 阶段 3(第 2 个月):通过 Husky 在本地启用 Commitlint 强制执行。添加 CI 检查作为必需检查。所有新提交必须遵循约定。旧历史不追溯更改。
- 阶段 4(第 3 个月以后):使用 Semantic Release 或 Release Please 自动化发布。自动生成变更日志。约定现在通过完全自动化带来回报。
IDE 集成
VS Code 和 JetBrains IDE 提供了使编写 conventional commits 更容易的扩展。这些集成提供类型自动完成、scope 建议和内联验证。
VS Code:安装 Conventional Commits 扩展(vivaxy.vscode-conventional-commits)。它添加了一个源代码控制面板小部件,引导你完成编写提交消息的过程,包括类型选择、可选 scope、描述、正文和破坏性变更字段。扩展还支持通过 .vscode/settings.json 自定义类型和 scope。
JetBrains(IntelliJ、WebStorm、PyCharm):从市场安装 Conventional Commit 插件。它在提交时提供弹出对话框,确保消息遵循约定。它支持从 commitlint 配置读取自定义 scope。
// .vscode/settings.json — custom scopes for VS Code extension
{
"conventionalCommits.scopes": [
"auth",
"api",
"ui",
"database",
"payments",
"search",
"config"
],
"conventionalCommits.showNewVersionNotes": false
}自定义提交类型和扩展规范
Conventional Commits 规范有意保持最小化——它只强制要求 feat 和 fix。团队可以根据特定工作流自由添加自定义类型。这通过扩展 commitlint 配置来实现:
一些流行的自定义类型包括 deps(依赖更新)、release(发布提交)、wip(进行中的工作——通常在 CI 中被拒绝)、security(安全相关变更)和 i18n(国际化变更)。
// commitlint.config.js — extending with custom types
export default {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
// Standard types
'feat', 'fix', 'docs', 'style', 'refactor',
'perf', 'test', 'build', 'ci', 'chore', 'revert',
// Custom types
'deps', // dependency updates
'security', // security patches
'i18n', // internationalization
'a11y', // accessibility
'release', // release commits
],
],
// Enforce allowed scopes
'scope-enum': [
2,
'always',
['auth', 'api', 'ui', 'db', 'payments', 'search', 'config', 'deps'],
],
},
};提交消息模板(.gitmessage)
git 提交消息模板帮助开发者记住格式而无需记忆规范。在项目根目录创建 .gitmessage 文件并配置 git 使用它:
.gitmessage Template
# .gitmessage
# <type>(<scope>): <description>
# [optional body]
# [optional footer(s)]
#
# Types: feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert
# Rules: header <= 72 chars, imperative mood, no capital, no period
# Breaking: add BREAKING CHANGE: footer or ! after type
# Example: feat(auth): add two-factor authenticationConfigure Git to Use the Template
# Set the template for this repository
git config commit.template .gitmessage
# Or set it globally
git config --global commit.template ~/.gitmessageHusky + lint-staged 预提交钩子
Husky 管理 git 钩子,lint-staged 在暂存文件上运行 linter。结合 Commitlint,它们创建了强大的本地强制执行管道。以下是完整设置:
# Install dependencies
npm install --save-dev husky lint-staged @commitlint/cli @commitlint/config-conventional
npx husky init
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
echo "npx lint-staged" > .husky/pre-commitpackage.json Configuration
{
"scripts": { "prepare": "husky", "commit": "cz" },
"lint-staged": {
"*.{js,ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{css,scss,json,md}": ["prettier --write"]
}
}常见错误及修复方法
即使经验丰富的开发者也会犯 Conventional Commits 错误。以下是最常见的错误及其解决方案:
- 使用大写类型——类型必须小写:feat,不是 Feat 或 FEAT。修复:commitlint 默认强制执行。
- 类型后缺少冒号——格式是 type: description 而不是 type description。类型(或 scope)后的冒号和空格是必需的。
- 写太长的头部——保持第一行在 72 个字符以内。将细节放入提交正文。Commitlint 有 header-max-length 规则(默认 100)。
- 使用错误的类型——当变更实际上是重构时使用 fix,或当它是文档时使用 feat。每种类型都有与 SemVer 绑定的特定语义。误用会导致不正确的版本升级。
- 忘记 BREAKING CHANGE footer——仅在类型后添加 ! 对某些工具来说不够。始终包含 BREAKING CHANGE: footer,描述什么会中断以及迁移路径。
- 不一致的 scope——在一次提交中使用 auth,在另一次中使用 authentication。在 commitlint 配置中记录允许的 scope 并强制执行。
- 包含多个变更的巨大提交——一次提交应该代表一个逻辑变更。如果需要在一次提交中写 "feat(auth): add login and fix(ui): fix button",应该分成两次单独的提交。
提交粒度最佳实践
每次提交应该代表一个单一的、原子的、逻辑的变更。一个好的测试:你能否在不使用"和"的情况下用一句话描述提交?如果不能,它可能应该是多次提交。
对于功能开发,将工作分成小的提交:一个用于数据模型,一个用于 API 端点,一个用于 UI 组件,一个用于测试。这使代码审查更容易,二分查找更简单,回退更安全。
永远不要将格式变更与逻辑变更混合。如果需要重新格式化文件并修复 bug,在单独的提交中进行:style(utils): format helper functions 后跟 fix(utils): correct null check in parseConfig。这样,格式变更提交在代码审查时可以安全跳过。
# Good: atomic commits with clear purpose
feat(auth): add User model with password hashing
feat(auth): add POST /api/auth/register endpoint
feat(auth): add registration form component
test(auth): add unit tests for registration flow
docs(auth): add registration API documentation
# Bad: one giant commit
feat(auth): add complete user registration system
# (includes model, endpoint, UI, tests, docs all in one)常见问题
什么是 Conventional Commits 规范?
Conventional Commits 是一个用于编写标准化提交消息的约定。每条消息遵循 type(scope): description 格式,其中 type 表示变更性质(feat、fix、docs 等),scope 可选地标识受影响的区域,description 概述变更。该约定支持自动化版本管理、变更日志生成和 CI/CD 自动化。
Conventional Commits 与语义化版本有什么关系?
Conventional Commits 直接映射到 SemVer。feat 提交触发 MINOR 版本升级(1.0.0 到 1.1.0)。fix 提交触发 PATCH 升级(1.0.0 到 1.0.1)。带有 BREAKING CHANGE 的提交触发 MAJOR 升级(1.0.0 到 2.0.0)。docs、style、refactor 等类型默认不触发版本变更。
如何使用 Husky 设置 Commitlint?
使用 npm 安装 @commitlint/cli 和 @commitlint/config-conventional。创建 commitlint.config.js 文件并扩展 conventional 配置。然后安装 Husky,运行 npx husky init,并创建运行 npx commitlint --edit 的 .husky/commit-msg 钩子。每条提交消息现在都会根据 Conventional Commits 规范进行验证。
Semantic Release 和 Release Please 有什么区别?
Semantic Release 是一个完全自动化的工具,在每次 CI 运行时确定版本、生成变更日志、发布到 npm 并创建 GitHub Release。Release Please(由 Google 维护)创建一个累积变更的发布拉取请求;发布只在你合并 PR 时发生。Release Please 在发布前给你一个审查步骤。
可以在 monorepo 中使用 Conventional Commits 吗?
可以。使用 scope 字段标识受影响的包(例如 feat(api): add endpoint)。Changesets、Lerna 和 Release Please 等工具有内置的 monorepo 支持。它们可以根据带 scope 的提交独立管理每个包的版本,并生成每个包的变更日志。
如果提交消息写错了怎么办?
如果还没推送,使用 git commit --amend 重写最后一条提交消息。对于更早的提交,使用 git rebase -i 编辑特定提交消息。如果提交已推送和合并,最好保持不变并确保未来的提交遵循约定。Commitlint + Husky 能主动防止这些错误。
PR 标题也应该使用 Conventional Commits 吗?
是的,特别是如果你的团队使用 squash 合并。当 PR 被 squash 合并时,PR 标题成为合并提交消息。action-semantic-pull-request 等工具可以在 CI 中检查 PR 标题。这确保主分支上的最终提交遵循约定,即使 PR 分支中的单独提交没有遵循。
如何处理 revert 提交?
使用 revert 类型后跟原始提交描述:revert: feat(auth): add OAuth login。在提交正文中包含被撤销提交的哈希:This reverts commit abc1234。一些工具如 Semantic Release 会检测到 revert 并相应调整版本,可能从下一个发布中移除某个功能。