When a project has jobs using rules that match both $CI_COMMIT_BRANCH and $CI_PIPELINE_SOURCE == "merge_request_event", a push to a branch with an open merge request creates two pipelines: one branch pipeline and one merge request pipeline. Both consume runner minutes and both post status to the merge request.
GitLab calls these duplicate pipelines and warns about them in the merge request widget. The fix belongs at the top of the file, not in each job. A workflow: rules: block controls whether a pipeline is created at all, and the standard three-clause form drops branch pipelines when an open merge request exists.
GitLab ships this as a ready-made include: include: template: 'Workflows/MergeRequest-Pipelines.gitlab-ci.yml'. Since GitLab 15.x the same effect is available with workflow: rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" followed by a clause excluding $CI_OPEN_MERGE_REQUESTS on branch pushes. Whichever form is used, jobs still need their own rules; workflow only decides whether the pipeline exists.