You’re working on your self-hosted GitLab, everything’s going smoothly. You merged a feature to develop, tested it, created a merge request from develop to main, and then… this lovely message appears:

The merge is blocked The source branch is 2 commits behind the target branch.

Sound familiar? If you’re seeing this message repeatedly, you’re probably missing one critical step in your Git workflow.

⏱️ Reading time: 5-8 minutes

The Problem

Here’s a typical scenario that causes this issue:

  1. As a developer: Pull latest from develop, create feature branch, implement feature, push, create MR to develop
  2. As an admin: Approve and merge the MR to develop. Check staging - looks good!
  3. Create MR from developmain: And boom - blocked
The merge is blocked
The source branch is 2 commits behind the target branch.
1 commit and 1 merge commit will be added to main (squashes 34 commits).
Source branch will not be deleted.

Why does this keep happening?

The Root Cause

The issue is simple but often overlooked:

The Missing Step

After merging developmain, you’re NOT merging main back into develop.

This is the #1 mistake that causes “source behind target” errors.

When you merge develop into main, GitLab creates a merge commit on main. This merge commit exists only on main, not on develop. Now main has commits that develop doesn’t have.

Here’s what’s happening visually:

%%{init: { 'theme': 'base', 'themeVariables': { 'git0': '#2496ED', 'git1': '#00AA00', 'gitBranchLabel0': '#ffffff', 'gitBranchLabel1': '#ffffff', 'commitLabelColor': '#ffffff', 'commitLabelBackground': '#333333' }}}%%
gitGraph
  commit id: "v1.0.0"
  branch develop
  checkout develop
  commit id: "feat: new feature"
  commit id: "fix: bug fix"
  checkout main
  merge develop id: "Merge commit" tag: "v1.1.0"
  checkout develop
  commit id: "feat: another feature"

See the problem? After the merge to main, there’s a merge commit on main that develop doesn’t have. When you try to merge developmain again, Git sees that develop is “behind” because it’s missing that merge commit.

The Fix: Sync Back After Every Merge to Main

After every merge from developmain, you MUST sync main back to develop:

# Step 1: Switch to develop
git checkout develop

# Step 2: Get the latest from remote
git fetch origin

# Step 3: Merge main into develop
git merge origin/main

# Step 4: Push the synced develop
git push origin develop
Why This Works

By merging main back into develop, you’re bringing that merge commit into develop. Now both branches have the same history, and future merges won’t be blocked.

What Happens When You Run git merge

When you run git merge origin/main, Git might open a text editor (usually Nano or Vim) asking you to confirm the merge commit message:

Merge remote-tracking branch 'origin/main' into develop

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.

If you see Nano (the default editor):

  1. You’ll see the merge message at the top
  2. Press Ctrl + O to write/save (you’ll see “File Name to Write: .git/MERGE_MSG”)
  3. Press Enter to confirm
  4. Press Ctrl + X to exit

If you see Vim:

  1. Press Esc to ensure you’re in normal mode
  2. Type :wq and press Enter (write and quit)

Or if you want to accept the default message without opening an editor:

git merge origin/main --no-edit

The Golden Rule

Rule: Always Sync Back

After every merge to main, immediately sync main back to develop:

Make this a habit. Better yet, automate it.

git checkout develop
git fetch origin
git merge origin/main --no-edit
git push origin develop

Automating the Sync-Back

GitLab CI/CD Pipeline

Add this to your .gitlab-ci.yml to automatically sync after merges to main:

sync-develop:
  stage: post-deploy
  rules:
    - if: $CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"
  script:
    - git checkout develop
    - git pull origin develop
    - git merge origin/main --no-edit
    - git push origin develop
  tags:
    - docker

Complete Workflow Example

Here’s the complete flow from feature to production, including the sync-back step:

# 1. Developer creates feature
git checkout develop
git pull origin develop
git checkout -b feature/awesome-feature

# Work on feature...
git add .
git commit -m "feat: add awesome feature"
git push origin feature/awesome-feature

# 2. Create MR: feature → develop (in GitLab UI)
# Get it reviewed and merged

# 3. After feature merged to develop, create MR: develop → main
git checkout develop
git pull origin develop

# Before creating MR, ensure develop is synced with main
git merge origin/main --no-edit
git push origin develop

# Now create MR in GitLab: develop → main

# 4. After MR merged to main, SYNC BACK!
git checkout develop
git fetch origin
git merge origin/main --no-edit
git push origin develop

Why GitLab Blocks the Merge

GitLab has a setting called “Pipelines must succeed” and merge checks that prevent merging when the source is behind the target. This is actually a safety feature:

  • Prevents overwriting commits on main
  • Ensures you’ve tested against the latest production code
  • Forces you to resolve any potential conflicts before merging

You can see this in Settings → Merge Requests → Merge checks:

  • Pipelines must succeed: Requires CI to pass
  • All discussions must be resolved: Requires all comments addressed
  • Source branch must be up to date: Blocks if behind target
Don't Disable the Check

While you can disable the “Source branch must be up to date” check, this is NOT recommended. Instead, fix the root cause by syncing your branches properly.

Prevention is Better Than Cure

To prevent this issue from recurring:

1. Establish the Sync-Back Rule

Make it a team policy: Every merge to main must be followed by syncing main back to develop.

2. Use Protected Branches

Configure your branches in GitLab:

  • main: Fully protected, requires approval
  • develop: Protected, but allows merges from main

Quick Reference

When you see “source branch is X commits behind target”:

# Sync the source branch with target
git checkout develop          # or your source branch
git fetch origin
git merge origin/main         # or your target branch
git push origin develop

After every merge to main:

git checkout develop
git merge origin/main --no-edit
git push origin develop

If you’re in Nano editor:

  1. Ctrl + OEnterCtrl + X

If you’re in Vim editor:

  1. Esc:wqEnter

Summary

The “source branch behind target” error is caused by not syncing main back to develop after merges. The fix is simple:

  1. After every merge to main, run:

    git checkout develop
    git merge origin/main
    git push origin develop
  2. Make this a team habit or automate it in CI/CD

  3. Never disable the merge check - fix the root cause instead

This small habit will save you (and your team) countless headaches. Now go sync those branches!