极狐GitLab

合并列车实战:多个合并请求并行安全合入

极狐GitLab
2026年9月4日
17860
分享:

合并列车实战:多个合并请求并行安全合入

在向默认分支高频合并的团队里,一个常见的困境是:多个合并请求(Merge Request,MR)各自通过了流水线检查,但合入时却因为彼此的改动相互冲突,导致默认分支被"合坏"。传统的串行处理方式——一个 MR 合并完再合下一个——虽然稳妥,却让合并吞吐量成为交付瓶颈,主线上的关键修复被迫排队。

极狐GitLab 的合并队列(Merge Train,合并列车)就是为解决这个矛盾而设计的:它把待合并的 MR 放入一个队列,让每个 MR 都在"自己与队列中所有更早 MR 的改动叠加到目标分支之后"的结果上运行流水线,从而在真正合入之前就提前验证它们能否协同工作。

01 合并列车解决的是什么问题

默认分支频繁合并时,不同 MR 的改动可能相互冲突。问题在于:单个 MR 的流水线只验证了"源分支相对当前目标分支"的结果,却没有验证"这个 MR 与其他即将合入的 MR 叠加之后"的结果。于是可能出现这样的情况——A 和 B 各自测试通过,但两者同时合入后,合并结果却构建失败。

合并队列的做法是:不等到合入之后再发现问题,而是在合入之前,就把 MR 放到队列里做"合并结果级"的验证。官方文档这样描述:每个合并请求都会与队列中更早的其他合并请求进行比较,以确保它们都能协同工作。

02 工作原理:并行排队,逐层验证

合并队列的运行方式可以用三个 MR(A、B、C)的典型场景来理解。

当没有 MR 等待合并时,你选择 MergeSet to auto-merge,合并队列就启动了。极狐GitLab 会为第一个 MR 启动一条合并队列流水线——它与合并结果流水线相同,在源分支与目标分支变更的组合上运行。

随后把第二个 MR 加入队列时,第二条合并队列流水线会在"两个 MR 的更改与目标分支的组合"上运行;第三个 MR 加入后,第三条流水线会在"三个 MR 与目标分支的合并结果"上运行。关键是,这些流水线是并行运行的。

按照官方文档的示例,A、B、C 三个 MR 依次入队,会创建三条并行运行的合并结果流水线:

  1. 第一条流水线在 A 的更改与目标分支的组合上运行。

  2. 第二条流水线在 A 和 B 的更改与目标分支的组合上运行。

  3. 第三条流水线在 A、B 和 C 的更改与目标分支的组合上运行。

每个 MR 只有在满足两个条件后才会合入目标分支:该 MR 的流水线成功完成,并且所有排在其之前的 MR 都已合并

如果 B 的流水线失败:A 继续运行;B 被移出队列;C 的流水线被取消,并启动一条新的、只针对"A 和 C 的更改与目标分支组合"的流水线(不再包含 B 的更改)。随后 A 成功合入,C 继续运行。任何新加入队列的 MR 都会包含已合入的 A 的更改。

这套机制保证了:默认分支上的每一次合并,都是经过"合并结果"验证的,而不是只验证了单点改动。每个合并队列最多可并行运行 20 条流水线,超过 20 个 MR 的会排队等待,等待入队的 MR 数量没有上限。

03 前置条件:先有合并请求流水线

合并队列不是独立存在的,它建立在两层流水线能力之上。启用合并队列前,需要满足以下前提(官方文档明确列出):

  • 需要维护者角色

  • 代码仓库必须是极狐GitLab 仓库,而不是外部仓库。

  • 流水线必须配置为合并请求流水线(下文详述),否则 MR 可能陷入未解决状态,流水线可能被丢弃。

  • 必须启用合并结果流水线

其中最关键的一步是让流水线在合并请求事件下运行。合并请求流水线基于源分支的内容运行,触发条件是在 .gitlab-ci.yml 中匹配 CI_PIPELINE_SOURCE == "merge_request_event"

最简单的做法是用 workflow: rules 控制整条流水线:

workflow:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

test:
  script:
    - echo "此作业在合并请求流水线中运行"
    - ./run-tests.sh

也可以只针对单个作业配置 rules,实现更细粒度的控制:

test:
  script:
    - ./run-tests.sh
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

lint:
  script:
    - npm run lint
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
      changes:
        - "*.js"

需要留意一个细节:官方文档强调,在 include: 中定义的规则(例如使用 include: component)不满足这一要求,匹配 merge_request_eventrules:workflow: rules 必须直接写在 .gitlab-ci.yml 里。

04 启用与使用合并队列

满足前置条件后,启用合并队列的路径是:项目 → 设置合并请求 → 在 合并选项 部分,确保 启用合并结果流水线 已开启,再勾选 启用合并队列保存更改

启用后,日常使用就是在 MR 页面点击 Merge(无流水线运行时)或 Set to auto-merge(有流水线运行时)。MR 的合并队列状态会显示在流水线组件下方,例如"This merge request is 2 of 3 in queue"。从极狐GitLab 17.3 起,还可以在合并请求列表上方进入 合并队列 视图,查看队列中活跃 MR 与已合并 MR 的顺序和状态。

在版本演进上,合并队列有一系列值得关注的节点:

  • 16.0 起,按钮由 "Start merge train" 改为 Set to auto-merge,"Remove from merge train" 改为 Cancel auto-merge

  • 16.5 起,支持快进式半线性合并方法。

  • 17.2 引入合并队列的自动合并,17.4 默认启用,17.7 正式 GA。

05 自动流水线取消与故障排查

合并队列会检测冗余流水线并自动取消,以节省 CI/CD 资源。冗余流水线主要发生在三种情况:队列中某个 MR 的流水线失败、跳过合并队列立即合并、从队列中移除某个 MR。这些情况下,队列中部分或全部 MR 需要重新构建合并队列流水线,旧的流水线因针对的是"已失效的组合"而被取消。

实际使用中,MR 可能在流水线运行期间被自动从队列中丢弃,常见原因包括:把 MR 改为草稿、出现合并冲突、在启用了"所有讨论串都必须解决"时出现未解决的新讨论串。丢弃原因可以在系统笔记中查到。

另一点值得注意:合并队列流水线失败后无法重试——因为合并结果已经过时。此时要么重新把 MR 加入队列触发新流水线,要么给作业配置 retry 关键词应对间歇性失败。

对于必须紧急合入的关键补丁,可以选择 Merge immediately(立即合并),它会忽略队列状态直接合入,但代价是队列中其他 MR 的流水线会被取消并重启——所以官方建议仅在关键情况下使用。极狐GitLab 16.5 起还引入了跳过合并队列的实验性功能,允许次要改动(如文档更新)在不完全重启队列的情况下合并,但会带来"组合更改不兼容"的风险。

写在最后

合并队列把"合并结果验证"从人工经验变成了系统化的默认行为。对于高频合并的团队,它用并行排队替代了串行等待,在保证默认分支稳定的同时提升了合并吞吐量;对于关键补丁,又保留了立即合并的逃生通道。

落地合并队列的本质,是先把团队的流水线规范成"合并请求流水线"这一前置形态,再叠加合并结果流水线与队列能力。当每一次合入都建立在"与队列中其他改动叠加后的验证结果"之上时,默认分支的稳定性就不再依赖合入顺序的运气,而是成为流程本身的保证。