3. 保护分支与合并权限设计

在大型团队中,分支协作的核心风险不是“没人写代码”,而是“谁都能往关键分支写”。保护分支(Protected Branch)与合并权限设计,本质上是在 GitLab 中为不同分支划定一条清晰的权限边界:哪些角色可以直接推送,哪些角色只能通过合并请求(Merge Request,MR)进入,合并前必须满足哪些审批条件。本节围绕保护分支、审批规则、代码所有者(Code Owners)和最小审批人数四个要素,说明如何把这条边界落到可执行的配置上。

一、保护分支:先定义“不可随意写入”的边界

保护分支解决的是写入权限问题。以常见的 main、release/* 为例,团队通常希望:主干分支不允许任何人直接 git push,只能通过 MR 合入;发布分支只允许发布负责人合并;普通开发分支则保持开放。在 GitLab 中,进入 Settings → Repository → Protected branches 即可配置,关键字段包括:

  • Branch:分支名或通配符,如 main、release/*、hotfix/*。
  • Allowed to merge:允许点击合并的角色,例如 Maintainer。
  • Allowed to push and merge:允许直接推送并合并的角色,主干分支通常设为 No one。
  • Allowed to force push:是否允许强制推送,主干分支必须关闭。

一个可直接参考的配置示例如下(角色按 GitLab 默认层级):

main            Allowed to merge: Maintainer   Push: No one        Force push: 关闭
release/*       Allowed to merge: Maintainer   Push: No one        Force push: 关闭
hotfix/*        Allowed to merge: Maintainer   Push: Maintainer    Force push: 关闭
develop         Allowed to merge: Developer    Push: Developer     Force push: 关闭
feature/*       Allowed to merge: Developer    Push: Developer     Force push: 允许(仅个人分支)

这里要强调一个判断依据:“Allowed to push”设为 No one 并不等于没人能改主干,而是强制所有变更走 MR。这样代码审查、流水线校验和审批记录才有统一的入口。若某分支确实需要紧急直推,应单独建 hotfix/* 并限定到具体角色,而不是放开 main。

二、合并请求审批规则:把“能合并”变成“满足条件才能合并”

保护分支只控制“谁能合”,审批规则控制“满足什么条件才能合”。在 Settings → Merge requests 中,与权限边界最相关的选项有:

  • Merge request approvals:启用审批,设置最小审批人数。
  • Prevent approval by author / committers:禁止作者或提交者自审,避免“自己写自己批”。
  • Reset approvals on push:新提交后重置已有审批,防止“批完再改”。
  • Pipelines must succeed:流水线通过才允许合并。
  • All threads must be resolved:所有讨论必须解决。

这些选项组合起来,才构成完整的合并门槛。例如主干分支可以设为:最小审批 2 人、禁止作者自审、推送后重置审批、流水线必须成功、讨论全部解决。此时即使某人是 Maintainer,也不能绕过审批直接合并——除非该分支的“Allowed to merge”被显式放开。

三、代码所有者:让“该谁审”由目录结构决定

最小审批人数只规定“几个人批”,但不规定“谁来批”。代码所有者通过仓库根目录的 CODEOWNERS 文件,把目录或文件映射到具体团队或个人,GitLab 会在 MR 中自动请求对应审批人。示例:

# CODEOWNERS
*                       @team-core
/src/payment/           @team-payment @li.ming
/src/auth/              @team-security
/infra/terraform/       @team-sre
/docs/                  @tech-writers

配合保护分支中的 Code owner approval 选项,可以要求“涉及该路径的变更必须由对应 Code Owner 批准”。这样,支付模块的改动不会只由前端同学批准,安全相关目录也不会被无关人员放行。需要注意两点:一是 CODEOWNERS 的匹配遵循“最后匹配优先”,越具体的规则应写在越后面;二是 Code Owner 审批与最小审批人数是叠加关系,不是替代关系。

四、最小审批人数:按分支风险分级,而不是一刀切

最小审批人数应随分支风险递增。一个可落地的分级参考如下:

分支类型最小审批人数是否需 Code Owner典型场景
feature/*1否日常功能开发
develop1~2关键目录需要集成分支
release/*2是版本发布
main2~3是生产主干

人数不是越多越好。审批人过多会导致 MR 长时间挂起,反而促使团队寻找绕过手段。更合理的做法是:主干 2 人且其中至少 1 人为 Code Owner,发布分支 2 人,普通功能分支 1 人。若某类变更风险极高(如数据库迁移、支付逻辑),可对特定路径单独提高要求。

五、把四要素串成一条权限边界

下面这张图展示了从开发者提交到合并进入主干的完整权限判定链:保护分支决定“能不能推”,审批规则与最小人数决定“够不够批”,Code Owner 决定“该谁批”,流水线与讨论状态决定“是否放行”。

保护分支与合并权限判定链 开发者 feature 分支提交 保护分支判定 main / release/* Push: No one → 必须走 MR 直接 push 被拒绝 提示:protected branch 创建合并请求 MR 目标:main 触发审批与流水线 合并前必须满足的条件 最小审批人数 ≥ 2 禁止作者自审 Code Owner 已批准 按 CODEOWNERS 匹配 流水线通过 Pipelines must succeed 讨论全部解决 All threads resolved 允许合并 进入 main 主干 阻止合并 条件未满足,MR 挂起 关键原则 保护分支管“能不能推”,审批规则管“够不够批”, Code Owner 管“该谁批”,三者叠加才形成完整边界。

从图中可以看到,权限边界不是单一开关,而是四层叠加:保护分支拦截直推,MR 作为唯一入口,审批规则与最小人数控制批准数量,Code Owner 控制批准质量,流水线与讨论状态作为最终放行条件。任何一层缺失,都会留下绕过路径。

六、落地时的常见判断与调整

配置完成后,建议用一次真实 MR 做验证:让一名 Developer 尝试直接推送 main,应被拒绝;再提交一个 MR,观察是否自动请求 Code Owner、是否要求 2 人审批、推送新提交后审批是否重置。若发现 Maintainer 仍能绕过审批直接合并,检查该分支的“Allowed to push and merge”是否被误设为 Maintainer。若 Code Owner 未被自动请求,检查 CODEOWNERS 路径是否匹配、是否启用了 Code owner approval。

建议把 CODEOWNERS 纳入主干保护范围,修改它本身也需要审批,否则有人可以通过改规则来绕过审批。
不要为了“提高效率”把主干的最小审批人数降为 0 或允许作者自审。一旦放开,保护分支就只剩形式,事故复盘时也无法追溯是谁批准的。

总结来说,保护分支、审批规则、代码所有者和最小审批人数共同构成 GitLab 大型团队的分支写入与合并权限边界。设计时应按分支风险分级,主干从严、功能分支从简,并保证所有变更都经过 MR 这一统一入口,让权限边界既安全又可执行。