GitLab merge request pipelines: detached, merged results, and duplicates

By Melih Toksari, developer of MergeBell · Updated · 6 min read

Short answer

A merge request pipeline runs for a merge request on its source branch alone, ignoring the target branch. GitLab labels it merge request; GitLab 14.8 and earlier labeled it detached, and GitLab's CI/CD variables and API still call it that. It runs only when your configuration asks for it, with rules for merge_request_event in .gitlab-ci.yml, or when the beta Skip branch pipelines for merge requests setting is on. A merged results pipeline, in Premium and Ultimate, tests the source branch merged into the latest target branch instead, and GitLab falls back to a merge request pipeline when the two conflict. When one push starts both a branch pipeline and a merge request pipeline, fix it with workflow:rules, or with that setting, a beta since GitLab 19.2.

A merge request’s Pipelines tab can list pipelines labeled branch, merge request, merged results or merge train, sometimes two for the same commit, and older answers call one kind detached. Here is what each one tests in GitLab 19.5, how to turn merge request pipelines on, and how to stop a push from running two pipelines.

The pipelines on a merge request

Label What it tests What it needs
branch The branch as you pushed it. Runs on every push. Nothing: it’s the default.
merge request (detached until GitLab 14.8) The merge request’s source branch alone, ignoring the target branch. Rules for merge_request_event in .gitlab-ci.yml, or a project setting: see below.
merged results A temporary commit that merges the source branch into the latest target branch. It’s in neither branch, but the pipeline details show it. The same rules, Premium or Ultimate, and Enable merged results pipelines.
merge train The merge request combined with the target branch and every merge request ahead of it on the train. All of the above and merge trains: see GitLab merge trains.

A merge request from a fork can show a fork badge as well: more below.

Why GitLab called it detached

Until GitLab 14.8, the pipeline list labeled a merge request pipeline detached. It meant what today’s merge request label means: the pipeline ran only on the contents of the source branch, ignoring the target branch. GitLab 14.9 changed the label, but the old word is still in:

  • the CI_MERGE_REQUEST_EVENT_TYPE variable: detached in a merge request pipeline, merged_result in a merged results pipeline, merge_train in a merge train pipeline;
  • the GraphQL API, where PipelineMergeRequestEventType has the value DETACHED;
  • the merge requests API, which can create “a detached merge request pipeline”.

If you expected a merged results pipeline and got a merge request one, one of these is why:

  • the project is on the Free tier, and merged results pipelines need Premium or Ultimate;
  • Enable merged results pipelines is off in Settings › Merge requests;
  • the source branch conflicts with the target branch, so GitLab can’t build the merged commit and runs a merge request pipeline instead. Resolve the conflicts, and the next pipeline tests the merged result.

Turn merge request pipelines on

GitLab doesn’t create merge request pipelines until your configuration asks for them, or you turn on the Skip branch pipelines for merge requests setting. This workflow from GitLab’s docs runs a merge request pipeline when a branch has an open merge request, and a branch pipeline when it doesn’t:

workflow:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
      when: never
    - if: $CI_COMMIT_BRANCH

It runs no tag pipelines: add - if: $CI_COMMIT_TAG if you release from tags. With workflow:rules, jobs that have no rules of their own run in both kinds of pipeline. To add single jobs to merge request pipelines instead, give them a rule:

test:
  script: make test
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

Without workflow:rules, jobs that have no rules keep running in a separate branch pipeline, so each push to a merge request’s branch runs two pipelines. Give every job rules, or use the workflow above.

  • Write the rules in .gitlab-ci.yml itself. GitLab’s docs say rules defined in an include:, such as a CI/CD component, don’t satisfy the requirement.
  • You need the Developer role or higher on the source project, and the repository has to be hosted on GitLab, not mirrored from GitHub or Bitbucket for CI/CD.
  • It runs when someone creates a merge request from a branch with commits, pushes to its source branch, or selects Run pipeline on its Pipelines tab.
  • Some variables change. CI_COMMIT_BRANCH is empty in merge request pipelines, so rules like $CI_COMMIT_BRANCH == "main" don’t match there: use CI_MERGE_REQUEST_SOURCE_BRANCH_NAME or CI_MERGE_REQUEST_TARGET_BRANCH_NAME. CI_MERGE_REQUEST_SOURCE_BRANCH_SHA and CI_MERGE_REQUEST_TARGET_BRANCH_SHA are empty too, and are only set in merged results pipelines.

To test the merged result instead, a Maintainer selects Enable merged results pipelines under Merge options in Settings › Merge requests. Turn it on only once the rules above are in place: without them, GitLab warns, merge requests can get stuck or their pipelines dropped.

Stop duplicate pipelines

When you push to a branch with an open merge request, GitLab tries to create a branch pipeline and a merge request pipeline. If your rules allow both, every push runs two. The usual causes:

  • a job whose last rule is a bare when: always, which matches both kinds;
  • jobs with rules in the same file as jobs without them, or with only and except;
  • a job with one rule for push and another for merge_request_event, and no workflow:rules.

Two fixes:

  • workflow:rules. The example above runs one kind per push. If you already have workflow:rules, put this rule first:

    workflow:
      rules:
        - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
          when: never
        # your existing rules follow

    The push condition keeps it from blocking pipelines started by triggers.

  • Skip branch pipelines for merge requests, a beta setting since GitLab 19.2, in Settings › CI/CD › General pipelines. Pushes to a merge request’s source branch then create only a merge request pipeline, and jobs without rules, only or except join it. The push that creates the merge request still gets a branch pipeline, and scheduled, API, manual and triggered pipelines aren’t affected. If your jobs only run in branch pipelines, merge requests get no pipeline at all.

Running one kind also settles which pipeline counts. With Pipelines must succeed on and both kinds present, GitLab checks the merge request pipeline, but GitLab’s docs note that a race decides which result blocks or passes the merge request (issue 384927).

Common problems

You see Why Fix
No merge request pipeline No rules for merge_request_event in .gitlab-ci.yml. Add them, above.
“An error occurred while trying to run a new pipeline for this merge request.” You selected Run pipeline, but the configuration no longer has merge request pipelines. Push a commit or rebase to get a branch pipeline, or add the rules back.
Two failed pipelines after one push .gitlab-ci.yml is invalid, so both the branch and the merge request pipeline fail. Fix it with the pipeline editor or CI Lint.
A protected variable is empty, or a job waits for a protected runner Merge request pipelines get protected variables and runners only when both branches are protected and in the same project, the person running the pipeline has push or merge access to the target branch, and Allow merge request pipelines to access protected variables and runners is selected in Settings › CI/CD › Variables. Run that job in a branch pipeline on a protected branch, or unprotect the variable if it isn’t secret. A job stuck in pending explains the runner side.
The merge request stays on “Checking pipeline status.” Pipelines must succeed is on, but workflow:rules let no pipeline run for the merge request. Change the rules so a pipeline runs for merge requests.

When a merge request pipeline fails or is canceled and Pipelines must succeed is on, the merge request can’t merge until one passes: retry the failed jobs, select Run pipeline, or push a fix. The other merge blockers are in why can’t I merge my GitLab merge request?

Merge requests from forks

A merge request from a fork runs its pipeline in the fork, with the fork’s configuration, runners and variables, and the parent project shows it with a fork badge. A member of the parent project can select Run pipeline on the merge request’s Pipelines tab to run it in the parent project instead, with the parent’s runners and variables but still the fork’s .gitlab-ci.yml. GitLab asks them to accept a warning first: read the changes, because a fork’s code can try to steal the parent’s secrets. Fork pipelines never get protected variables or runners.

On your phone

In MergeBell, a merge request shows its latest pipeline in its overview, and its Pipelines tab lists its pipelines, with live job logs, Retry and Cancel on the free tier. A retry reruns jobs in the same pipeline, so a merge request pipeline stays one: after you turn on merged results pipelines or resolve a conflict, the next push starts the new kind, or Run pipeline in GitLab in a browser. When your merge request’s pipeline fails, the “Pipeline failed” To-Do arrives as a push, free and checked every five minutes. More in how to retry a failed GitLab pipeline.

Frequently asked questions

What is a detached pipeline in GitLab?

A merge request pipeline: one that ran on the merge request's source branch alone, ignoring the target branch. GitLab 14.8 and earlier labeled it detached, and GitLab 14.9 and later label it merge request. The old name lives on in the CI_MERGE_REQUEST_EVENT_TYPE variable, which is detached in these pipelines, merged_result in merged results pipelines and merge_train in merge train pipelines.

What is the difference between a merge request pipeline and a merged results pipeline?

A merge request pipeline tests the source branch as it is. A merged results pipeline tests a temporary commit that merges the source branch into the latest target branch, so it catches breakage from changes that reached the target branch after you branched. Merged results pipelines need Premium or Ultimate and Enable merged results pipelines in Settings › Merge requests, and GitLab runs a merge request pipeline instead when the branches conflict.

Why does GitLab run two pipelines for my merge request?

Your rules let one push create both a branch pipeline and a merge request pipeline. Add workflow:rules that run a merge request pipeline when the branch has an open merge request and a branch pipeline otherwise, using CI_OPEN_MERGE_REQUESTS. Since GitLab 19.2, the beta setting Skip branch pipelines for merge requests, in Settings › CI/CD › General pipelines, does it without editing the file.

Why doesn't my GitLab merge request pipeline run?

GitLab doesn't create merge request pipelines by default. The .gitlab-ci.yml file needs job rules or workflow:rules matching $CI_PIPELINE_SOURCE == "merge_request_event" (or the older only: [merge_requests]), written in the file itself: rules from an include:, such as a CI/CD component, don't count. You also need the Developer role or higher, and the repository must be hosted on GitLab.

Why is a protected variable empty in my merge request pipeline?

A merge request pipeline gets protected variables and protected runners only when the source and target branches are both protected and in the same project, the person who ran it has push or merge access to the target branch, and Allow merge request pipelines to access protected variables and runners is selected in Settings › CI/CD › Variables. A merge request from an unprotected feature branch, or from a fork, doesn't get them.

Sources

Checked against these pages on 31 October 2026. GitLab changes quickly — if something here no longer matches, tell us and we will fix it.

Do this from your phone with MergeBell

Free for the whole review loop: the To-Do inbox, diffs and line comments, approve and merge, pipelines with retry, and push alerts for your To-Dos. Works with gitlab.com and self-managed GitLab 16.0+.

Get MergeBell for iPhoneGet MergeBell for Android

On the App Store for iOS 26 or later and on Google Play for Android 12 or later. An independent app, not affiliated with GitLab Inc.