Conventional Commits

指导 Agent 将变更拆成可机器解析的提交头:类型、可选 scope、祈使句 subject,以及破坏性标记与正文脚注,便于 changelog、release 工具与 semver 规则一致落地。

SKILL 中应写清团队允许的类型集合、scope 命名约定(是否与包名 / 模块路径对齐)、以及「哪些类型参与版本 bump」——避免 Agent 把文档改动标成 feat

与 PR 描述、变更日志生成等技能衔接:合并请求正文承载评审上下文,提交头保持对解析器友好;发行说明可由工具从规范提交聚合。

提交信息从草稿到落盘

  [ 暂存区 / diff 摘要 ]
        │
        ▼
  ┌─────────────────┐     选 type;必要时填 scope;是否破坏性 → 决定是否加 !
  │  组装首行头       │──── 祈使语气、现在时;常见单行 ≤ 72 字符(含类型与 scope)
  └─────────────────┘
        │
        ▼
  ┌─────────────────┐     解释「为什么」、实现细节、评审注意事项;与首行空一行
  │  正文(可选)     │
  └─────────────────┘
        │
        ▼
  ┌─────────────────┐     BREAKING CHANGE: …;Closes #n;Reviewed-by:(团队约定)
  │  脚注(可选)     │
  └─────────────────┘
        │
        ▼
  [ git commit / hook / CI 校验通过 ]

机器可读的是「首行 + 脚注关键字」;正文仍建议写给人类。若仅用 ! 标示破坏性行为,须在正文或脚注中写清迁移说明,否则 release 工具可能无法生成完整 BREAKING 段落。

首行语法与长度

基本形态:type(scope)!: subject。其中 scope! 均可省略;冒号后必须有一个空格,再跟简短 subject。

  • type:小写,与团队约定列表一致;自定义类型也建议保持单一词根、避免空格。
  • subject:不要用句号结尾;避免尾逗号;首字母小写(部分团队允许专有名词大写,需在 SKILL 写明)。
  • 超长首行会被终端与邮件列表截断,commitlint 常默认 72;Agent 生成时可主动换行正文承载细节。

常用类型与 scope

featfix 最常驱动用户可见的 minor / patch;其余如 docschorecirefactortestperf 是否触发版本号由工具配置决定,须在仓库文档中固定。

  • Monorepo:scope 与包名或目录约定一致(如 feat(api):),便于按包过滤 changelog。
  • 禁止滥用 feat 描述仅格式化、注释或纯文档,除非团队明确把文档算作对外「特性」。
  • 关联 issue:在脚注使用 Closes #123 等,避免塞进 subject 造成噪声。
// commitlint 配置文件(.commitlintrc.json)
// 运行:npx commitlint --from HEAD~1 --to HEAD --verbose
{
  "extends": ["@commitlint/config-conventional"],
  "rules": {
    // 允许的类型(自定义添加 wip、release)
    "type-enum": [2, "always", [
      "feat",     // minor bump:新功能
      "fix",      // patch bump:缺陷修复
      "perf",     // patch bump:性能优化
      "refactor", // no bump:重构(无行为变更)
      "test",     // no bump:测试
      "docs",     // no bump:文档
      "style",    // no bump:格式(不影响逻辑)
      "chore",    // no bump:构建/工具
      "ci",       // no bump:CI 配置
      "build",    // no bump:构建系统
      "revert",   // patch bump:回滚提交
      "wip"       // no bump:进行中(禁止合并 main)
    ]],
    "subject-max-length": [2, "always", 72],
    "subject-case": [2, "always", "lower-case"],
    "body-leading-blank": [1, "always"],
    "footer-leading-blank": [1, "always"]
  }
}

破坏性变更(! 与脚注)

两种方式可并存:feat!feat(api)!: 在首行标明破坏性;或在脚注单独写 BREAKING CHANGE: 段落。semantic-release 等工具通常识别其一即可,但迁移说明仍应写在脚注或正文。

# 破坏性变更的正确写法示例

# 方式 1:! 符号(推荐,首行即可见)
feat(api)!: change /users endpoint to return paginated response

Before: GET /users → User[]
After:  GET /users → { items: User[], total: number, cursor: string }

BREAKING CHANGE: Response shape changed. Update all callers to use `.items`.
Migration: https://docs.example.com/migration/v2-users-api
Closes #1234

# 方式 2:仅脚注(! 省略,工具仍识别)
feat(auth): add OAuth 2.1 PKCE support

Implements RFC 7636 for public clients.

BREAKING CHANGE: Authorization endpoint now requires `code_challenge`.
Old flows without PKCE will receive 400 Bad Request after 2024-06-01.
Migration guide: https://wiki.example.com/oauth-pkce-migration

# 错误写法(勿学):
# feat: update user API and some breaking changes  ← 含糊,工具无法识别
# chore!: update deps                              ← chore 标 ! 会误触发 major

Agent 起草时若不确定是否破坏公共 API,应在 SKILL 中要求标注「待维护者确认」或拆成独立提交,避免静默 major bump。

与 squash merge 协作

Squash 后历史只剩一条合并提交:应在 SKILL 中约定「最终 squash 消息」是否由 PR 标题 + 正文模板生成,以及是否保留 Conventional 首行供自动化解析。

  • 若依赖 release-please 等从 PR 标题解析,标题本身应符合同一套 type(scope) 规则。
  • 分支上多条小提交可在合并说明中折叠为一条用户向描述,避免 changelog 重复条目。
  • 本地 commitlint 与 pre-commit:在 SKILL 中附上典型错误输出与修复示例,便于 Agent 自纠。
# Husky pre-commit hook 配置
# 1. 安装 husky 与 commitlint
npm install --save-dev husky @commitlint/cli @commitlint/config-conventional

# 2. 初始化 husky
npx husky init

# 3. 创建 commit-msg hook(.husky/commit-msg)
cat > .husky/commit-msg << 'EOF'
#!/bin/sh
npx --no -- commitlint --edit "$1"
EOF
chmod +x .husky/commit-msg

# 4. 创建 pre-commit hook(.husky/pre-commit)
cat > .husky/pre-commit << 'EOF'
#!/bin/sh
npm run lint:staged  # lint-staged:只 lint 暂存文件
npm test -- --passWithNoTests
EOF

# 5. package.json 配置 lint-staged
# "lint-staged": {
#   "*.{ts,tsx}": ["eslint --fix", "prettier --write"],
#   "*.sql":      ["sqlfluff fix --dialect postgres"]
# }

# 典型 commitlint 错误示例(便于 Agent 自纠):
# ✖ subject may not be empty [subject-empty]
# ✖ type must be one of [feat, fix, ...] [type-enum]
# ✖ subject must not be longer than 72 characters [subject-max-length]

提交头校验预览

下方表单用于拼装首行并做轻量校验(类型小写、scope 括号配对、冒号后空格、! 位置)。semver 提示为常见约定,实际 bump 以你仓库的 release 配置为准。

修改 type、scope、subject 或破坏性选项后,预览与校验结果会更新。

预览


              

---
name: conventional-commits
description: 提交类型规范、commitlint 配置、Husky hook 与破坏性变更写法
version: 2.0
---
# 提交格式
type(scope)!: subject   <-- 72 字符以内,祈使句,无句号

正文(可选):空一行后写「为什么」与实现细节

BREAKING CHANGE: 具体说明变更内容与迁移步骤
Closes #issue-number

# 类型与版本 bump 规则
feat     → MINOR(新功能,向后兼容)
fix/perf → PATCH(缺陷/性能,向后兼容)
feat!/fix! 或 BREAKING CHANGE 脚注 → MAJOR
docs/chore/ci/test/style/refactor/build → 不触发版本号

# commitlint 配置文件(.commitlintrc.json)
{"extends":["@commitlint/config-conventional"],
 "rules":{"type-enum":[2,"always",["feat","fix","perf",
   "refactor","test","docs","style","chore","ci","build","revert","wip"]],
  "subject-max-length":[2,"always",72]}}

# Husky 安装(一次性)
npm install --save-dev husky @commitlint/cli @commitlint/config-conventional
npx husky init
echo 'npx --no -- commitlint --edit "$1"' > .husky/commit-msg

返回技能库 更多技能入口