签入代码治理要求(Commit/MR/分支的可追溯治理)
-
签入代码治理要求(Commit/MR/分支的可追溯治理)
目标
把工程治理的最小闭环“工程化”:
- 任何进入主干/可发布链路的变更,都必须可追溯到一个全局可索引主键(Key)。
- 追溯链路稳定:
Key -> 分支 -> Commit -> MR -> 代码变更。 - 通过 CI 把规范“卡死”,避免靠自觉。
术语
- Key(工程主键):全局唯一、可搜索、可聚合的变更主键。
- Intake Layer(需求接入层):运营/需求方只接触的单一入口项目,不触碰 250 个仓库。
一、统一工程主键:Key 必须全局唯一(MUST)
1. Key 规则
Key 必须带来源前缀,保证全局唯一,避免同号冲突。
- 禅道:
ZD-<id>(例:ZD-10452) - Taiga:
TG-<id>(例:TG-8931) - GitLab Intake Issue(推荐):
REQ-<iid>(例:REQ-1234)
备注:未来新增系统,只需要新增前缀(例如
JIRA-、WX-),不破坏既有规则。2. 禁止使用的写法(禁止)
以下写法会导致“搜索不稳定”,禁止:
1234#1234bug1234TAIGA-1234(除非这是你们统一前缀)
二、三处强约束:分支名 + Commit + MR 都必须携带 Key(MUST)
只强制一处会漏;三处都强制,GitLab 搜索必然命中。
1. 分支命名规范(MUST)
必须包含 Key,且 Key 紧跟类型后。
feature/<KEY>-<slug>bugfix/<KEY>-<slug>hotfix/<KEY>-<slug>
示例:
feature/ZD-10452-order-discountbugfix/TG-8931-fix-null-refhotfix/REQ-1234-fix-payment-timeout
2. Commit message 规范(MUST)
Commit 第一行必须以 Key 开头:
<KEY>: <message>
示例:
ZD-10452: Fix discount roundingTG-8931: Add validation for phone
建议:合并提交(merge commit)也要包含 Key(如果你们允许 merge commit)。
3. MR 标题规范(MUST)
MR 标题必须包含 Key(建议放最前):
<KEY> - <title>
示例:
ZD-10452 - Fix discount roundingREQ-1234 - Payment timeout hotfix
三、用 CI 把规范卡死:没有 Key 不准合并(MUST)
靠制度和自觉会被绕开;CI 是唯一稳定控制点。
必须校验的 3 条
- 分支名必须包含合法 Key(
ZD-\d+|TG-\d+|REQ-\d+) - MR 标题必须包含合法 Key
- **Commit messages(最近 N 个)**必须包含 Key(至少一个 Key;更严格可要求与分支 Key 一致)
建议策略
- MR pipeline / merge request event 时执行(最有效)
- 主干 pipeline 也可再执行一次(兜底)
四、需求入口治理:运营不碰 250 个仓库(SHOULD)
结论
- 运营/需求方只接触一个 GitLab 项目(Intake Layer)
- 工程师才接触各个业务仓库
推荐组织结构
Group: company | |-- Group: _intake | |-- Project: requirements (运营只在这里提 Issue) | |-- Group: products | |-- Project: erp1.0-sales | |-- Project: store-manage | |-- ... (工程仓库)Intake Issue 如何成为 Key
- 运营提 Issue 后,系统自动生成 IID
- 约定 Key 为:
REQ-<iid> - 工程师在分支/commit/MR 中引用
REQ-<iid>
五、合并策略:让“后门”消失(MUST)
合并到主干的硬规则
- 任何进入
master/main的变更必须:- 有 Key
- 走 MR
- 通过 CI 校验
建议的最短制度句子(可写进团队规范)
任何需要上线/进入主干的变更,必须有 Key(ZD/TG/REQ),否则拒绝合并。
六、落地清单(你可以逐项勾选)
- 确认 Key 前缀集合(
ZD-/TG-/REQ-) - 发布分支命名规范(feature/bugfix/hotfix)
- 发布 Commit message 第一行规范(
<KEY>:) - 发布 MR 标题规范(
<KEY> -) - 在 CI 中增加校验 job(MR pipeline 必跑)
- 建立
_intake/requirements项目并统一入口
七、常见问题
Q1: 为什么 Key 要全局唯一?
因为你将来会做跨仓库聚合、审计、统计、回溯;如果 Key 不全局唯一(例如都叫
1234),必然出现误聚合。Q2: 为什么要三处都要带 Key?
- 分支:源头可见
- commit:最终 diff 可追溯
- MR:跨仓库聚合最稳定(你大概率以 MR 为中心)
Q3: 运营只面对一个入口,会不会增加工程团队负担?
短期会增加“路由/翻译”成本;但中长期会显著降低补账/救火成本,并让工程事实可审计,这是规模化治理唯一可行路径。
-
1. 背景:我们为什么要改流程
近期我们在 CI/CD 与分支治理上做了一轮升级,目标很明确:
- 提升生产环境安全性:避免“谁改了 master 就把生产一起带崩”的风险
- 提升可追溯性:每个改动都能对应到一个明确的需求/缺陷 Key(如
ZD-xxx/TG-xxx/REQ-xxx) - 在多仓库、多级触发(A->B->C)的情况下保持稳定:避免触发链路分支漂移、遗漏合并
这套升级会增加一些管理动作,但它换来的是可控、可审计、可回滚的发布链路。
2. 现场问题:为什么“我要上生产”会变成“要把所有人都合进去”
我们遇到的典型困惑是:
- 当前大家都在
develop上做修改 - 某个修复已经在
develop测试通过,现在要发布生产 - 但
develop同时包含其他人的改动
一旦用develop -> master合并,就会把其他人的改动也带上生产
结果看起来就像“流程绕了一圈,和以前直接改 master 没区别”,甚至更累
这不是某个人的问题,而是**“只有 master+develop 且大家直接改 develop”**这种工作方式的必然结果:
develop会自然变成“所有进行中的改动大杂烩”,不具备按需挑选发布的能力。
3. 根因剖析:为什么会出现“hotfix 从 develop 拉”的错误用法
当大家想紧急发生产时,很自然会做出一个看似合理、实则危险的动作:
- 从
develop拉一个hotfix/*分支 - 然后
hotfix/* -> master发生产
问题在于:如果 hotfix 的基线来自 develop,它本质上等价于“把 develop 发布到生产”。
这就必然把别人还未准备好的改动一起带进去。关键原则:
生产修复(hotfix)必须从“生产基线”分出来
也就是:hotfix/*必须从master拉,而不是从develop拉。
4. 共识目标:我们到底要的是什么制度
我们需要一套“成本不高、不会出错”的制度,解决两个核心问题:
- 生产发布可控:能明确“这次上生产到底包含哪些提交”
- 多仓库链路稳定:上游是什么分支,下游就尽量跟随;不跟随也要有可预测的 fallback
换句话说:让
master真的“受保护”,让生产发布变成一个可管理动作。
5. 建议落地方案(推荐,成本最低且最符合治理闭环)
5.1 分支定位(长期分支 + 临时分支)
master(生产基线,受保护)- 只允许 MR 合入
- 对应 production/staging 发布(手动)
develop(集成基线,建议受保护)- 通过 MR 合入,减少“无意破坏”
- 对应 test 环境(自动)
- 允许临时分支(不等于分支爆炸,而是必要的“隔离器”)
feature/<KEY>-<slug>:从develop拉,合回develophotfix/<KEY>-<slug>:从master拉,合回master发生产
这并不是回到复杂 GitFlow,而是最小集合:
“两个长期分支 + 两类临时分支”,用来解决“只发布一部分”的刚需。5.2 发布路径(把“整合发布”和“紧急修复”分开)
- 整合发布(develop 当前整体可发布)
- 走
develop -> masterMR - staging 验证后,手动 production
- 走
- 紧急修复(develop 不可整体发布)
- 从
master拉hotfix/<KEY> - 只把必要提交带过去(见下一节
cherry-pick) hotfix -> master上生产- 回灌:再做一次
master -> develop,避免修复丢失或未来重复出现
- 从
5.3
cherry-pick的角色:解决“只想上其中几个提交”当修复已经在
develop做完,但develop夹杂其他人改动时:- 不再用
develop -> master解决 - 而是从
master拉hotfix/<KEY>,然后对develop上属于本次修复的提交执行cherry-pick
这样生产只包含你要发布的那几个提交,不会把别人的未完成工作带上去
6. 多仓库下游触发:FANOUT_BRANCH 的意义与补充规则
在多级触发(A->B->C)里,容易出现“分支漂移”:
每一跳都用CI_COMMIT_BRANCH触发下游,会导致链路不可预测。现有文档已定义唯一原则:
FANOUT_BRANCH贯穿整条触发链路- 上游传了就沿用;没传就等于
CI_COMMIT_BRANCH
补充建议:同名分支优先 + 不存在则回落 master(可选增强)
当上游是
hotfix/<KEY>时,下游可能未创建同名分支。理想策略是:- 下游存在同名分支:触发同名分支
- 下游不存在:回落到
master
这类 fallback 要做到“稳定不报错”,通常需要在触发前做一次“分支存在性判断”(脚本/机制层面),避免触发直接失败。是否引入该增强,视团队对稳定性与实现成本的取舍决定。
7. 这套制度能解决什么、代价是什么
能解决
- 生产发布可控:不会再“合并一次把所有人带上生产”
- 责任边界清晰:谁的改动、哪个 Key、进了哪些仓库、是否已合 master 一目了然
- 发布更安全:
master真正成为“受保护的生产基线”
需要付出的代价
- 合并与回灌变成日常管理工作(但这是“可控发布”的必要成本)
- 要求改动尽量小步快跑,否则合并冲突会指数级上升
8. 执行清单(请团队统一遵守)
- 分支来源
hotfix/*必须从master拉feature/*从develop拉
- 发布生产
- 非紧急:
develop -> master(前提是 develop 已声明为可发布集合) - 紧急:
hotfix/* -> master(必要提交用cherry-pick精准带入)
- 非紧急:
- 回灌
- hotfix 发布后必须
master -> develop
- hotfix 发布后必须
- 治理约束
- 分支名、commit、MR 标题必须带合法 Key(
ZD/TG/REQ) master必须受保护,只允许 MR
- 分支名、commit、MR 标题必须带合法 Key(
9. 下一步(建议)
- 把以上规则固化进团队文档(branch-rules)
- 在 CI 中增加最小校验(例如:hotfix MR 目标必须是 master;关键作业基于分支类型区分环境)
- 结合“按 Key 聚合查询”工具:每天/每周形成“哪些 Key 已合 master、哪些仍在 develop/feature”的报告,降低遗漏风险
如果你希望我把这篇公告进一步“贴近你们仓库的现实参数”(比如:当前只允许
master/develop还是允许feature/hotfix;哪些环境 job 是自动/手动;是否需要下游分支 fallback),你回复两句话即可:- 你们最终是否允许临时分支
feature/*、hotfix/*? - 下游触发 fallback(同名分支优先,不存在回落 master)要不要做成强制能力?