提交规范
研发开发规范
Conventional Commits
Git Hooks
commit-msg
本篇学习指南
读完你能
- 写出符合 Conventional Commits 规范的提交信息
- 选对 type(feat/fix/docs…)和 scope(模块名)
- 知道为什么要规范 commit message,以及 hook 怎么帮你自动校验
概念入门
如果你是第一次提交代码
- commit(提交)
- Git 保存一次改动的「快照」。每次 commit 需要写一条提交信息,说明改了什么。
- Conventional Commits
- 一种提交信息格式约定,格式为
<type>(<scope>): <subject>。统一风格后可以自动生成更新日志、快速定位改动范围。 - Git Hook
- Git 在特定操作(如 commit、push)前自动执行的脚本,用来检查代码是否符合规范。就像门禁系统,不合格的提交会被拦截。
每次 commit 就是给代码拍一张快照
代码改动
→
git commit
Git Hook 自动检查
→
快照 #1
feat(bacnet): 新增 COV 订阅
1
提交信息格式(Conventional Commits)
2.5 提交规范(Conventional Commits)
沿用项目已有的 fix(bacnet): / feat(zw): 风格,规范化为:
Conventional Commits 格式拆解
feat
type 必填
做了什么类型
(
bacnet
scope 可选
改了哪个模块
)
:
支持 SubscribeCOV 降级订阅
subject 必填
简短描述,中文可
正确
feat(bacnet): 支持 SubscribeCOVProperty 降级
fix(zw): 补充 maxTokens 配置
docs(auth): 新增授权机制架构文档
refactor(driver): 抽取公共轮询逻辑
错误
update — 无 type、无 scope
修改了点东西 — 中文无信息量
WIP — 临时提交不应入库
fix: bug — subject 太空泛
CODE
<type>(<scope>): <subject> [可选 body:说明 why] [可选 footer:Breaking Changes / Closes #issue]
type 类型:
| type | 用途 |
|---|---|
feat | 新功能 |
fix | Bug 修复 |
docs | 文档 |
refactor | 重构(不改行为) |
chore | 构建/依赖/杂项 |
perf | 性能优化 |
test | 测试 |
style | 格式(不改逻辑) |
ci | CI/CD |
为什么非要用这套格式?它不只是「好看」——规范化的 commit 能自动生成 CHANGELOG(feat 进新功能列表、fix 进修复列表),能让 git log --grep="fix(bacnet)" 一秒定位某个模块的所有修复。半年后排查「这个 bug 之前修过吗」,靠的就是这条 message。随手写「改了点东西」= 给未来的自己挖坑。
scope 参考表(前后端共用,按模块;以下为 EasyPro 现有模块,新产品按实际模块增减):
| scope | 模块 |
|---|---|
system / iot / energy / eoms | 后端业务模块 |
driver / bacnet / modbus | 驱动 |
auth / security | 鉴权 |
zw | 智问模块 |
fe / optiview | 前端 |
db / sql | 数据库脚本 |
build / deps | 构建/依赖 |
规范的提交信息
git log
feat(bacnet): 支持 SubscribeCOVProperty 降级订阅 fix(zw): ZwModelProvider 补充 maxTokens 配置 docs(auth): 新增授权机制全链路架构文档 refactor(driver): 抽取公共轮询逻辑
type+scope+subject 齐全,半年后还能看懂改了什么
不规范的提交信息
git log
update [✗] 无 type、无 scope 修改了点东西 [✗] 中文且无信息量 WIP [✗] 临时提交不该入库 fix: bug [✗] subject 太空泛
三个月后无人知道这些 commit 改了什么、为什么改
提交 subject 可中文(与现有风格一致),但 type/scope 必须英文。
2
Git Hooks 自动校验
2.7 Git Hooks 自动校验(把人工约定变强制拦截)
前面 §2.1~§2.6 全是人工约定,靠自觉容易破窗。通过本地 Git Hook 可在 commit/push 时自动拦截违规情况,推荐每位开发本地启用。脚本统一放仓库 githooks/ 目录,通过 core.hooksPath 指向它(不污染各自 .git/hooks):
BASH
# 一次性启用(克隆后执行一次) git config core.hooksPath githooks
注意:Git Hook 是本地工具,可被 --no-verify 绕过,无法替代 §2.8 的服务端保护。二者配合形成「本地拦截 + 服务端兜底」。
pre-commit:拦截误提交文件与分支名
githooks/pre-commit(bash,Windows 下用 Git Bash 自带的环境运行):
BASH
#!/bin/bash
# 拦截误提交的日志/构建产物/系统文件
set -e
FORBIDDEN_PATTERNS='hs_err_pid.*\.log|replay_pid.*\.log|spy\.log|\.DS_Store|log\.bin|^target/|\.iml$'
errors=0
while IFS= read -r file; do
if [[ "$file" =~ $FORBIDDEN_PATTERNS ]]; then
echo "[✗] 禁止提交: $file (匹配忽略规则)"
errors=1
fi
done < <(git diff --cached --name-only --diff-filter=ACM)
# 校验当前分支名是否符合 <前缀>/<版本号>/<语义> 或 main / release/<版本> / line/<产品>/<版本>
branch=$(git rev-parse --abbrev-ref HEAD)
if [[ ! "$branch" =~ ^(main|release/|line/|feature/|hotfix/|custom/|dev/) ]]; then
echo "[✗] 分支名 [$branch] 不符合规范,应为 release/line/feature/hotfix/custom/dev 前缀之一"
echo " 格式: 主产品 <前缀>/<版本号>/<语义>(如 feature/2.1.1/bacnet-cov)或产品线 <前缀>/<产品>/<版本>/<语义>(如 feature/eos/2.1.0/择机算法)"
errors=1
fi
# 三段式校验:feature/hotfix/custom 必须有 <版本>/<语义> 两段(语义段中文/英文均可)
# (子产品四段式 feature/<产品>/<版本>/<语义> 天然满足"至少两段",此处不误报)
if [[ "$branch" =~ ^(feature|hotfix|custom)/([^/]+)$ ]]; then
echo "[✗] 分支名 [$branch] 缺少语义段,应为 <前缀>/<版本号>/<语义>,如 hotfix/2.1.1/cov-fix 或 hotfix/2.1.1/knx隧道假死优化"
errors=1
fi
# 语义段禁止空格(中文短语或英文小写连字符均可)
if [[ "$branch" =~ ^(feature|hotfix|custom)/[^/]*/.*[[:space:]] ]]; then
echo "[✗] 分支名 [$branch] 语义段含空格,请用连字符或去掉空格"
errors=1
fi
exit $errorscommit-msg:校验提交信息格式
githooks/commit-msg:
BASH
#!/bin/bash # 强制 Conventional Commits: <type>(<scope>): <subject> set -e msg_file="$1" first_line=$(head -1 "$msg_file") # 合法 type;merge/revert 开头放行 PATTERN='^(feat|fix|docs|refactor|chore|perf|test|style|ci|build|revert)(\(.+\))?: .+' if [[ "$first_line" =~ ^Merge ]] || [[ "$first_line" =~ ^Revert ]]; then exit 0 fi if [[ ! "$first_line" =~ $PATTERN ]]; then echo "[✗] 提交信息格式错误: $first_line" echo " 应为: <type>(<scope>): <subject>" echo " 例: feat(bacnet): 支持 SubscribeCOVProperty 降级订阅" echo " type: feat|fix|docs|refactor|chore|perf|test|style|ci|build|revert" exit 1 fi
prepare-commit-msg:填充模板(可选)
githooks/prepare-commit-msg(用 git commit 不带 -m 时自动填模板,引导规范书写):
BASH
#!/bin/bash # 仅在交互式编辑器提交且为新建提交时填模板 case "$2" in message|template|merge|squash|commit) exit 0 ;; esac cat >> "$1" <<'EOF' # ---- 提交信息模板(删除本行及以上注释后填写)---- # <type>(<scope>): <subject> # # type: feat|fix|docs|refactor|chore|perf|test|style|ci # scope(可选): system|iot|energy|eoms|driver|bacnet|modbus|auth|zw|fe|db|sql # subject: 祈使句,中文,结尾不加句号 # # [body: 说明 why,非 what] # [footer: Breaking Changes / Closes #issue] EOF
启用后记得 chmod +x githooks/*(Windows 下 Git 会自动识别)。
新手常见错误
提交信息是团队协作的「对话」,这些错误会让别人看不懂你改了什么
我就写了「修了bug」,git 没拦我啊,有什么问题?
问题不在能不能提交,而在于三个月后没人知道这个 commit 改了哪个 bug、为什么这么改。规范的写法是
fix(modbus): 修复写功能码切换导致线圈状态错乱——type 是 fix、scope 是 modbus、subject 说清了问题。这样回溯问题时 git log --grep 能快速定位。scope 我写了中文「能源管理」,为什么不规范?
scope 用英文模块名(如
energy、alarm、modbus),和代码包名、分支命名保持一致。中文在自动化脚本、CI 流水线、命令行 grep 时容易出编码问题。subject 里可以用中文描述,但 scope 跟代码结构走。我一次提交里改了 3 个不相关的东西,commit message 怎么写?
说明你应该拆成 3 个 commit。一个 commit 只做一件事,是规范的核心原则。用
git add -p 分块暂存、分多次提交。混在一起的 commit 既没法写清楚 message,也没法在出错时单独 revert。commit-msg hook 报错了,我该怎么改?
看报错提示——它会告诉你缺了什么(通常是缺 type 或格式不对)。最常见的是首行不符合
<type>(<scope>): <subject> 格式。用 git commit --amend 修改最近一次提交的信息即可,不用重新 commit。