本篇学习指南
读完你能
  • 写出符合 Conventional Commits 规范的提交信息
  • 选对 type(feat/fix/docs…)和 scope(模块名)
  • 知道为什么要规范 commit message,以及 hook 怎么帮你自动校验
阅读时长
约 8 分钟 · 纯阅读
前置知识
先读 01-分支与Tag管理;会基本 git commit 操作
概念入门
如果你是第一次提交代码
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新功能
fixBug 修复
docs文档
refactor重构(不改行为)
chore构建/依赖/杂项
perf性能优化
test测试
style格式(不改逻辑)
ciCI/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 $errors
commit-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。