3829 字
约 12 分钟
5
S01-J02 · 分支策略与 Code Review / CI 流程

适用读者:从"一个人提交代码"进入"多人在同一仓库协作"的人;被问过"你这个分支从哪儿拉的"的人。
前置:S01-B10(Git 入门)、S01-J01(引用与对象模型)。
实测环境:Ubuntu 24.04.1 / Git 2.43.0;commit-msg 钩子拦截效果为 2026-11-05 实测。
覆盖旧文:A-555(团队 Git 开发规范:分支、Commit、MR 与 CI,543 个点位,本系列最大单篇)。
新增扩展:E-S01-016(提交签名)、E-S01-017(Conventional Commits 与 commitlint)、E-S01-018(pre-commit 钩子)。
上一篇:S01-J01 · Git 内部模型:快照、对象、引用与内容寻址
下一篇:S01-J03 · Git 手术与效率。


1. 一句话结论

规范的价值不是"让人守规矩",而是让半年后的 git log 还能读懂。 其余(分支、MR、CR、CI)都是为这一条服务的。

旧文开篇论证得很对,但只有一句"让代码修改本身具有可读性"。本篇把它拆成四个可检查的产物:

产物 可检查的标准
Commit 消息 能否只看 git log --oneline 判断"改了什么、影响哪里"
分支名 能否从名字知道它要合到哪、属于什么类型
MR 描述 评审者能否不看代码就知道"为什么改、怎么验证"
CI 结果 合并后是否自动验证,失败是否可定位

2. Commit 规范:九种类型

旧文的九类划分是对的,直接沿用:

类型 含义 例子(旧文格式)
feat 新功能 feat/multi_merchant: 支持多商户下单
bugfix 普通 Bug 修复 bugfix/user: 修复手机号为空导致登录失败
hotfix 紧急线上问题 hotfix/create_order: 修复重复创建订单
perf 性能优化 perf/user_login: 优化登录查询性能
style 格式、清理,不影响业务 style/log_print
refactor 重构(不改行为) refactor/user
test 测试相关 test/user
docs 文档、注释 docs/user
chore 依赖、配置、脚手架 chore/dependency

2.1 格式之争:旧文的 / 与主流的 ()

写法 示例 生态兼容性
旧文写法(斜杠) feat/user: 支持多商户下单 人类可读;commitlint / semantic-release 默认不认
Conventional Commits feat(user): 支持多商户下单 工具链(changelog、版本号自动生成、release)通用

建议:用 type(scope): 描述(括号格式)。理由不是"哪个更好看",而是:

  • 你的 changelog / 版本号自动化(semantic-release、standard-version)按这个格式解析;
  • GitHub/GitLab 的提交关联(Closes #123)与类型解析都基于它;
  • 社区文档与工具默认值都是它。

如果你团队已经在用斜杠格式,不要混用——二选一,并写进仓库的 CONTRIBUTING.md。混用的后果是工具一半认一半不认。

V-14 记录:旧文的斜杠格式与 Conventional Commits 存在冲突,本篇按"括号格式为准、斜杠格式作为历史约定保留"处理,并把差异写进 gaps.md。

2.2 好与坏:一条判据

坏 为什么坏 好
update 不知道改了什么 fix(order): 修复并发下单重复创建
修复问题 同上,且没有范围 fix(order): 修复并发下单重复创建
修改代码 等于没说 refactor(order): 抽取订单校验到独立方法
fix 缺范围与说明 fix(user): 修复手机号为空导致登录失败

判据:把这条消息单独发给同事,他能否知道"改了什么、影响哪块业务"。

2.3 自动化:commit-msg 钩子(实测)

规则靠人守不住,靠钩子兜底。在仓库里放 .git/hooks/commit-msg:

#!/bin/sh
PATTERN='^(feat|bugfix|hotfix|perf|style|refactor|test|docs|chore)(\(.+\))?!?: .{4,}'
if ! grep -qE "$PATTERN" "$1"; then
  echo "❌ 提交信息不符合规范:type(scope): 说明(至少 4 字)"
  echo "   允许类型:feat bugfix hotfix perf style refactor test docs chore"
  echo "   你的输入:$(head -1 "$1")"
  exit 1
fi

实测效果:

$ git commit -m "update"
❌ 提交信息不符合规范:type(scope): 说明(至少 4 字)
   允许类型:feat bugfix hotfix perf style refactor test docs chore
   你的输入:update

$ git commit -m "feat(git): 增加 commit-msg 钩子校验"
[master (root-commit) 6fed6b5] feat(git): 增加 commit-msg 钩子校验
 1 file changed, 1 insertion(+)

钩子不进仓库(.git/hooks 是本地目录)。团队共享要走 core.hooksPath 或 husky/pre-commit 框架(E-S01-018)。

mkdir -p .githooks && cp <hook> .githooks/commit-msg
git config core.hooksPath .githooks     # 让仓库里的钩子生效

3. 分支模型:四个环境分支

旧文给的是一个"环境分支"模型(与 GitFlow 不同):

分支 受保护 作用
test ✅ 集成测试环境,日常合入门
pre ✅ 预发布,验收
prod ✅ 生产
master ✅ 线上代码存档,与 prod 同步
开发分支 ❌ 个人/特性分支
feature/* ──► test ──► pre ──► prod
                │                │
                └── master ◄─────┘(存档,与 prod 同步)

3.1 从哪个分支拉?(旧文最关键的一节)

项目状态 从哪拉 为什么
已上线 master / prod 线上稳定代码是唯一可信基线
未上线 test 此时 test 是最新集成基线

为什么不能从 test 拉新功能(已上线项目):test 里可能混着未验证完的他人改动。从 test 拉分支,等于把你的特性建在流沙上——出问题时无法判断是你的代码还是别人的。

3.2 分支命名与 Commit 类型一致

feat/multi_merchant      hotfix/create_order
bugfix/user_login        perf/user_login

反例:test2、dev123、mybranch——别人完全不知道用途,也没法判断该合到哪。

3.3 与 trunk-based 的取舍(本篇补充)

模型 适合 代价
环境分支模型(旧文,test/pre/prod) 有独立测试环境、发布节奏固定、需要人工验收 分支多,合并链条长,test 容易积压
trunk-based(主干开发 + 短分支 + 特性开关) 高频发布、自动化测试完备 要求 CI 强、开关治理好
GitFlow(develop/release/hotfix) 版本化交付(客户端/私有化) 分支最重,小团队用不动

判据:你的 CI 能不能在 10 分钟内给出可信的"能发"结论? 能 → 可以走 trunk-based;不能 → 老老实实用环境分支。


4. 标准开发流程(新功能)

# 1. 同步基线(已上线项目)
git checkout master && git pull
# 2. 建分支
git checkout -b feat/multi_merchant
# 3. 开发 + 小步提交
git add -p && git commit -m "feat(multi_merchant): 增加多商户下单入口"
git commit -m "feat(multi_merchant): 增加商户维度校验"
# 4. 推送
git push -u origin feat/multi_merchant
# 5. 开 MR(→ test)
# 6. CR 通过 → 合并 → CI 自动跑
# 7. 测试环境验证 → 验收通过 → 合入 pre → prod

关键纪律:

纪律 原因
分支存活 ≤ 3 天 分支越久,合并冲突越贵
一个分支只做一件事 混类型提交让 CR 无法判断风险
不直接 push 到 test(旧文第二十章) 保护分支的意义:所有改动经 MR + CR
提交粒度 ≤ 一次逻辑改动 出问题能 git bisect 定位(S01-J03)

5. Merge Request / PR:写什么

旧文对 MR 的定义很清楚:"请别人评审你的代码"的正式请求。但没给模板。这里补一个够用的:

## 做了什么
- 增加多商户下单入口(`OrderController#create` 支持 merchantId)

## 为什么
- 需求:单商户 → 多商户(PRD 链接)

## 怎么验证
1. `mvn -pl order-service test`
2. 手工:用商户 A/B 各下一单,确认 `order.merchant_id` 正确

## 风险与回滚
- 风险:老客户端不传 merchantId → 默认单商户(已兼容)
- 回滚:`git revert <merge-commit>`,无数据迁移

评审者最关心的三件事:为什么改(动机)、怎么验证(证据)、出问题怎么退(回滚)。不写这三样,MR 就是"你看我代码吧",评审质量必然低。


6. Code Review 看什么(旧文第三至五章的重组)

6.1 顺序(先看大的,再看小的)

  1. 改动范围:是否符合 MR 描述(有没有夹带无关改动)
  2. 接口与契约:对外 API/DB 结构有没有变,变了是否向下兼容
  3. 边界与异常:空值、并发、超时、重试
  4. 测试:新增/修改逻辑是否有对应测试
  5. 命名与可读性:最后再看风格(工具能查的别让人查)

反例:一上来逐字读格式与命名,读完发现整体方案不该这么做——白花 40 分钟。

6.2 一条硬规则:CR 不能只是"看起来没问题"

结论 需要什么
通过 至少说明"我看过哪些文件、验证了哪些场景"
打回 指出具体位置 + 期望做法,不写"再改改"
不确定 明确写出"这块我不熟,需要 X 确认",别假装通过

7. CI:合并之后自动做什么

阶段 内容 失败意味着
构建 mvn -B verify 代码不能编译 → 立即打回
单元测试 同上(surefire) 逻辑坏了
静态检查 Checkstyle/SpotBugs/Sonar 规范或潜在缺陷
打包 生成制品(jar/镜像) 发布链路断了
部署到 test 自动/半自动 环境问题(区别于代码问题)

纪律:CI 失败的人负责修,不能"下次一起修"。CI 红了还往上合,等于把验证环节废掉。


8. 提测、Bug 与 Hotfix(旧文第六至八章)

8.1 提测

feature 分支 → MR → test 分支合并 → CI 部署 test → 测试同学验证

提测前自检(旧文没有,但决定提测是否被打回):

  • 本地 mvn verify 通过
  • 新增逻辑有测试
  • 数据库变更脚本已提交(且可在 test 环境重放)
  • 配置项变更已同步给运维
  • MR 描述里写了验证步骤

8.2 Bug 修复

git checkout master && git pull
git checkout -b bugfix/user_login
# 修复 + 提交(附复现步骤)
git commit -m "fix(user): 修复手机号为空导致登录失败"

8.3 Hotfix(线上紧急)

# 1. 从 prod(或与 prod 同步的 master)拉
git checkout prod && git pull
git checkout -b hotfix/create_order

# 2. 最小改动修复 + 提交
git commit -m "fix(order): 修复并发下单重复创建"

# 3. 先合 prod,再回合 master(防止下次发版把修复覆盖掉)

Hotfix 的两条铁律:

  1. 必须从 prod 拉,不能从 test 拉(否则会带上未上线代码)。
  2. 修完必须回合 master。只合 prod 不合 master,下次从 master 拉分支再发版时,hotfix 会被"覆盖丢失"——这是线上事故的经典复发路径。

9. 提交签名(E-S01-016)

# SSH 签名(Git 2.34+,比 GPG 简单)
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
git log --show-signature -1        # 验证
git verify-commit HEAD             # 显式验证
场景 是否值得开
开源项目接受外部贡献 值得(可验证来源)
公司内部受控仓库 可选;强制会带来密钥管理成本
需要合规审计(金融/医疗) 建议开,并与 CI 校验联动

代价:签名失败会阻断提交(gpg failed to sign the data),团队必须先统一密钥配置方式。


10. 五个反模式

反模式 后果 正确做法
直接 git push origin test 绕过 CR,测试环境被污染 走 MR;分支保护开启
git push -f 到共享分支 抹掉他人提交(S01-J01 §5 的对象还在,但引用没了) 只在个人分支用;共享分支走 revert
一个 MR 混 5 个特性 无法评审、无法回滚 拆 MR,按"一件事"切
Commit 里混 style 与 feat git bisect 与回滚都会误伤 分开提交
分支从 test 拉 建在未验证代码上 已上线项目从 master/prod 拉

11. 旧文对照

旧文章节 内容 本篇处理
一 为什么要有规范(fix/update 的灾难) 保留论证,补"四个可检查产物"
二 九类 Commit 类型 保留;新增格式之争(斜杠 vs 括号)与生态兼容性判断
三–十二 九类各自的示例(feat/bugfix/hotfix/perf/style/refactor/test/docs/chore) 压缩为 §2 表 + 好坏对比;示例移入类型表
十三 不推荐的 Commit 写法 §2.2 判据化
十四 分支规范(test/pre/prod/master) §3 保留,补与 trunk-based/GitFlow 的取舍
十五–十六 已上线/未上线项目从哪拉 §3.1 保留并补"为什么不能从 test 拉"的解释
十七 分支命名 §3.2 保留 + 反例
十八–二十 标准流程、提交、为什么不能直接 push test §4 保留,补"分支存活 ≤3 天"等纪律
二十一–二十五 MR、CR、为什么必须 CR §5、§6 重组:新增 MR 模板与 CR 顺序
二十六 CI 自动工作 §7 补阶段表与"谁负责"
二十七–二十九 提测、Bug、Hotfix 流程 §8 保留 Hotfix 两条铁律(旧文有但没强调回合 master 的后果)
— 无自动化、无签名、无反模式 新增 §2.3(钩子实测)、§9、§10

12. 自测

  1. 已上线项目为什么不能从 test 拉新功能分支?
  2. feat/user: ... 与 feat(user): ... 除了外观,还有什么实质差别?
  3. Hotfix 只合 prod 不合 master,什么时候会炸?
  4. .git/hooks 里的钩子为什么团队共享不了?怎么解决?
  5. CR 时"先看格式再看方案"错在哪?

13. 下一篇

S01-J03 · Git 手术与效率:reset、revert、reflog、bisect、alias

流程规范了,事故仍然会发生:误提交到 main、误 push、误 merge、找不到是哪次引入的 bug。下一篇把五种事故各复现一次,给出"该 reset 还是该 revert"的判断标准,并用 git bisect 把"哪次改动引入问题"从人工排查变成二分查找。


本篇属于系列《S01 · 开发环境与工具链》。旧文来源:A-555(543 点位);扩展点 E-S01-016/017/018。commit-msg 钩子拦截效果为 2026-11-05 实测。

S01-J02 · 分支策略与 Code Review / CI 流程
https://www.clxhxhhr.top/posts/5167/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。