06-Git 规范与自动化
分支策略是契约,规范是契约的细则,自动化是让规范"不靠人盯也能执行"的手段。本章讲四块:Conventional Commits、SemVer、Git Hooks、CODEOWNERS 与 GPG 签名。
Conventional Commits#
没有规范的 commit 历史是这样的:
fix
aaa
test
update
改了个东西
wip半年后想搞清楚"某段代码为什么这么写",看到这种历史只能抓瞎。Conventional Commits 是一套轻量级 commit message 规范,格式:
<type>(<scope>): <description>
[optional body]
[optional footer(s)]type 列表#
| type | 含义 | 影响 SemVer |
|---|---|---|
feat | 新功能 | minor (1.2.3 → 1.3.0) |
fix | Bug 修复 | patch (1.2.3 → 1.2.4) |
perf | 性能优化 | patch |
refactor | 重构(不改行为) | 无 |
docs | 文档 | 无 |
style | 格式 | 无 |
test | 测试 | 无 |
build | 构建系统 | 无 |
ci | CI 配置 | 无 |
chore | 杂项 | 无 |
revert | 回滚 | 反向 |
破坏性变更#
用 ! 标注或在 footer 写 BREAKING CHANGE::
feat(api)!: change response format for /users endpoint
BREAKING CHANGE: response is now paginated, clients need to handle
the new `data` and `pagination` fields这会触发 major 版本升级(1.2.3 → 2.0.0)。
实际示例#
feat(auth): add OAuth2 login with Google
fix(api): return 404 when resource not found
refactor(db): extract connection pool to separate module
docs(readme): add deployment instructions
chore(deps): upgrade express from 4.17 to 4.18
ci(actions): add matrix build for node 20 and 22为什么值得用#
Conventional Commits 不只是"为了好看"。它让工具能根据 commit 历史自动算版本号、自动生成 changelog(详见 09 章的 release-please/semantic-release)。全团队都用 CC,发版这件事就能从"人的判断"变成"工具的执行"。
scope 的用法#
feat(api): ... 里的 api 是 scope,表示影响哪个模块。常见 scope:
api:后端 APIui:前端 UIdeps:依赖升级(给 Renovate/Dependabot 用)ci:CI 配置docs:文档release:发版本身
monorepo 里 scope 常对应包名:feat(@org/api): ...。release-please 和 changesets 都能自动识别 scope 对应的包。
BREAKING CHANGE 的三种写法#
# 写法 1:type 后加 !
feat!: remove deprecated endpoint
# 写法 2:scope 后加 !
feat(api)!: remove /v1
# 写法 3:footer 详细说明
feat(api): remove /v1
BREAKING CHANGE: /v1 is removed. Use /v2.
Migration guide: docs/migration-v2.md第三种用 footer 的方式更详细,可以写多行说明迁移路径——对用户最友好。
SemVer#
Semantic Versioning(语义化版本)规范版本号的格式和含义。它让"版本号"从人肉决策变成可自动计算的信号——配合 Conventional Commits,工具能根据 commit 历史自动决定下一个版本号,不需要人去判断"这次该升 minor 还是 patch"。
版本号格式:
MAJOR.MINOR.PATCH
1 2 3- MAJOR:破坏性变更(不兼容 API 修改)
- MINOR:向后兼容的新功能
- PATCH:向后兼容的 bug 修复
与 Conventional Commits 的映射#
| Conventional Commit | SemVer 变化 |
|---|---|
feat: | minor +1 |
fix: | patch +1 |
feat!: 或 BREAKING CHANGE: | major +1 |
refactor: docs: chore: 等 | 不发版 |
这套映射让自动化发版工具(如 semantic-release、release-please)能根据 commit 历史决定下一个版本号,不需要人介入。详见 09 章。
预发布和构建元数据#
1.0.0-alpha.1 ← 预发布版本
1.0.0-beta.2
1.0.0-rc.1
1.0.0+20260101 ← 构建元数据(不影响版本顺序)预发布版本优先级:alpha < beta < rc < 正式版。
Git Hooks#
Git Hooks 是 Git 在特定时机自动执行的脚本,存放在 .git/hooks/ 目录。常用 hooks:
| Hook | 时机 | 用途 |
|---|---|---|
pre-commit | git commit 之前 | 跑 lint、格式化、敏感信息扫描 |
commit-msg | 写完 commit message 之后 | 校验 message 格式(CC 规范) |
pre-push | git push 之前 | 跑测试、阻止 push 到受保护分支 |
pre-rebase | git rebase 之前 | 防止 rebase 已 push 的分支 |
post-merge | git merge 之后 | 重新安装依赖 |
原生 Hook 的问题#
.git/hooks/ 下的脚本不进版本控制——每个开发者要手动配置,时间一长就漂移。解决方案是用 husky/lefthook 这类工具,把 hooks 配置在项目里、自动同步到 .git/hooks/。
husky(JS 生态最流行)#
# 安装
npm install --save-dev husky @commitlint/cli @commitlint/config-conventional
# 初始化
npx husky init
# 添加 commit-msg hook(校验 commit message)
echo 'npx --no -- commitlint --edit ${1}' > .husky/commit-msg
# 添加 pre-commit hook(跑 lint)
echo 'npm run lint' > .husky/pre-commitcommitlint.config.js:
module.exports = { extends: ['@commitlint/config-conventional'] };lefthook(多语言友好)#
非 JS 项目用 husky 装 npm 比较重,lefthook 是 Git 原生的替代,单二进制:
# lefthook.yml
pre-commit:
commands:
lint:
run: golangci-lint run ./...
format:
run: gofmt -l .
commit-msg:
commands:
conventional:
run: conventional-pre-commit {1}# 安装
brew install lefthook
lefthook installpre-commit 框架(Python 生态)#
# .pre-commit-config.yaml
repos:
- repo: https://github.com/compilerla/conventional-pre-commit
rev: v3.2.0
hooks:
- id: conventional-pre-commit
stages: [commit-msg]
- repo: https://github.com/golangci/golangci-lint
rev: v1.57.0
hooks:
- id: golangci-lintpip install pre-commit
pre-commit install --hook-type commit-msg三层规范拦截#
规范的执行不能只靠 hook——hook 可以被 --no-verify 绕过。完整的拦截体系是三层:
- 本地 hook(pre-commit/commit-msg):最快反馈,10 秒内
- CI 检查(PR check):兜底,hook 被绕过时拦截
- 平台分支保护:最终闸门,没通过 CI 不让 merge
# CI 里再跑一遍 commitlint(防止 --no-verify 绕过)
- uses: wagoid/commitlint-github-action@v6
# PR 时校验所有 commit message单层都不够——本地 hook 被 --no-verify 绕过、CI 可能被 admin 强制 merge、平台保护也可能有漏洞。三层叠加才能让规范真正落地。
CODEOWNERS#
CODEOWNERS 让 GitHub/GitLab 自动给特定文件的 PR 添加 reviewer。这是"代码归属制"的工程化落地——每个模块都有明确的 owner,PR 改了哪个模块,对应 owner 就会被自动 review。
# .github/CODEOWNERS
# 全局默认 owner
* @team/backend
# 特定目录
/frontend/ @team/frontend
/infra/ @team/sre
/.github/ @team/sre
# 特定文件
/go.mod @team/backend @team/sre
/Dockerfile @team/sre
*.sql @dba-team配合分支保护规则(main 分支必须通过 CODEOWNERS review 才能 merge),形成"谁懂谁 review"的机制。新人改了 DB schema 的 SQL 文件,DBA 团队自动被加为 reviewer,不用开发者记得主动 @。
GPG 签名#
commit 的 author 字段是可以随便写的——git config user.name "Linus Torvalds" 就能伪造。GPG 签名让 commit 带上密码学证明:"这个 commit 确实是我用私钥签的"。这在开源协作中尤其重要——maintainer 需要确认 patch 确实来自声称的作者。
# 生成 GPG key
gpg --full-generate-key
# 选 RSA and RSA,keysize 4096,有效期 1 年
# 列出 key
gpg --list-secret-keys --keyid-format=long
# sec rsa4096/ABCDEF1234567890 2026-01-01 [SC]
# 这里的 ABCDEF1234567890 是 key ID
# 配置 Git 使用 GPG 签名
git config --global user.signingkey ABCDEF1234567890
git config --global commit.gpgsign true # 默认所有 commit 都签名
git config --global tag.gpgsign true # tag 也签名
# 签名 commit
git commit -S -m "feat: signed commit"
# 签名 tag
git tag -s v1.0.0 -m "release v1.0.0"GitHub/GitLab 上配置 GPG 公钥后,签名的 commit 会显示 "Verified" 标记。这对开源项目和审计要求高的场景有价值。
对内部团队:GPG 签名成本不低(key 管理、过期续签、新机器配置),收益是"commit 不可伪造"。如果团队信任基础设施(VPN + SSO + Git 平台审计),GPG 签名可以暂缓;如果是开源项目或有合规要求,建议启用。
实战要点#
- Conventional Commits + commitlint 是性价比最高的规范。一周时间能让 commit 历史从"乱码"变成"可读、可自动发版"。
- husky/pre-commit 在 PR 前拦截。但 CI 也要再跑一遍——hook 可以被
--no-verify绕过,CI 是兜底。 - CODEOWNERS + 分支保护 = 强制 review。光配 CODEOWNERS 没用,要配合分支保护规则"必须 CODEOWNERS approval 才能 merge"。
- squash merge 让 PR title 成为 commit message。这是让 CC 规范落地的捷径——开发者只需关注 PR title 写对,不用管每个 commit。配合
amannn/action-semantic-pull-request这类 Action 校验 PR title。 - 自动化规范的层级:hook 拦截 → CI 拦截 → 平台分支保护。三层都要有,单层都不够。
小结#
Git 规范的本质是把"开发者的口头约定"变成"工具的强制执行"。Conventional Commits 让 commit 历史可读、可自动发版;Git Hooks 让规范在本地拦截;CODEOWNERS 让 review 自动分配;GPG 签名让 commit 不可伪造。
这些规范一次配置长期受益,但前提是团队愿意一起执行——光靠个别人写规范 commit、其他人不写,规范就会沦为摆设。落地建议:先从 Conventional Commits + commitlint + PR title 校验入手(成本最低、收益最快),团队适应后再加 CODEOWNERS 和 GPG 签名。不要一次全上,容易反弹。下一章讲 Git 托管平台怎么选。