Directly write SHA of a merge train ref to the target branch instead of creating a new commit

Summary

Currently, Merge Trains discard temporary refs (cascading/combined refs) after a merge request is merged, however, we can use this temporary ref more effectively by directly writing the SHA into the target branch ref.

Benefits

Eliminate redundant pipelines: The merge train pipeline validates code on a temporary ref. When merged, GitLab creates a new commit SHA on the target branch, requiring the pipeline to run again. This change would skip that second run by fast-forwarding the target branch to the merge train's already-validated SHA.

Reuse build artifacts: Build systems often embed commit SHAs in artifacts. When the SHA changes between merge train and final merge, artifacts can't be reused—teams must rebuild everything post-merge. Keeping the same SHA means artifacts from the merge train pipeline are valid for production.

Details

When a merge train runs, here is what happens in general:

  1. An MR gets on a merge train.
  2. The MR creates a cascading ref as a SHA SHA-TMP-1.
  3. The MR creates a new pipeline on SHA-TMP-1, and this pipeline has passed.
  4. The MR gets merged, a merge train creates a new SHA SHA-1 on the target branch.

The point of this issue is to replace the SHA-1 by SHA-TMP-1 when the target branch is updated. Technically, these SHA contains the same history so that end result will be identical.

This issue is similar to #27117 (closed).

Use Cases

  1. If a commit SHA in an artifact used for identification later (what build is running?) then projects need to rebuild to get the real sha

Concerns

  • What if the merge method is not no-ff? (e.g. ff-merge-only, squash option, etc)

Implementation

Directly write SHA of a merge train ref to the target branch instead of creating a new commit.

We already do this for fast-forward merges and semi-linear merges with merge trains. Standard merges were left alone to avoid disruption. To implement it, we "just" need to:

  1. add a beta feature flag
  2. make this line of code conditional on the flag being disabled
  3. roll it out carefully on GitLab.com (leave it enabled for internal projects for at least a week), and leave the flag in the codebase for at least one full self-managed release

Reference

Edited by 🤖 GitLab Bot 🤖