KhueApps
Home/DevOps/Fix Git error: fatal: not possible to fast-forward, aborting

Fix Git error: fatal: not possible to fast-forward, aborting

Last updated: October 07, 2025

What this error means

You see this when Git is asked to fast‑forward only (e.g., git pull --ff-only or git merge --ff-only) but your branch has diverged from its upstream. A fast‑forward is possible only if your branch tip is an ancestor of the target. When both sides have new commits, Git would need a merge commit or a rebase, so it aborts.

Common triggers:

  • git pull --ff-only on a branch with local commits while the remote also advanced.
  • git merge --ff-only origin/main when your branch and origin/main both have new commits.
  • Submodules updated with --ff-only where the recorded commit cannot be fast‑forwarded.

Quickstart (choose one)

  • Keep linear history (recommended):
    • git fetch then git rebase origin/main
  • Allow a merge commit:
    • git fetch then git merge origin/main
  • Update config to prefer rebase on pull:
    • git config --global pull.rebase true

Minimal working example (reproduce and fix)

# Setup a bare repo and two clones
mkdir ff-demo && cd ff-demo
git init --bare origin.git

git clone origin.git alice
git clone origin.git bob

# Create main and push initial commit from alice
cd alice
git switch -c main
printf "hello\n" > file.txt
git add file.txt
git commit -m "alice: initial"
git push -u origin main

# Bob pulls and adds a commit
cd ../bob
git switch -c main  # tracks origin/main automatically
printf "bob line\n" >> file.txt
git commit -am "bob: change"
git push

# Alice adds her own commit locally
cd ../alice
printf "alice line\n" >> file.txt
git commit -am "alice: change"

# This fast-forward-only pull will now fail
git pull --ff-only  # fatal: not possible to fast-forward, aborting

# Fix option 1: rebase for linear history
git fetch
git rebase origin/main
# Resolve any conflicts if prompted, then continue
# After successful rebase, push
git push

# Alternatively, fix option 2: allow a merge commit instead
# (reset back one commit to redo the scenario)
# git reset --hard HEAD~1
# git fetch
# git merge origin/main
# git push

Why it happens

  • Your branch has commits A..B.
  • The upstream branch has commits C..D not present locally.
  • --ff-only refuses to create a merge commit. Since neither side is an ancestor, fast‑forward is impossible, so Git aborts.

Resolution paths

  1. Rebase your work onto the upstream (linear history)
  1. Save or stash local changes (if any):
    • git stash push -u or use --autostash below.
  2. Fetch latest remote:
    • git fetch
  3. Rebase onto the upstream tip:
    • git rebase origin/main
  4. Resolve conflicts if shown, then:
    • git rebase --continue
  5. Push updated commits:
    • git push (may need --force-with-lease if you had pushed the old commits already).

Tip: git pull --rebase --autostash combines fetch + rebase and stashes dirty work.

  1. Merge the upstream into your branch (allow merge commit)
  1. Fetch latest remote:
    • git fetch
  2. Merge remote branch:
    • git merge origin/main
  3. Resolve conflicts, commit the merge if needed.
  4. Push:
    • git push
  1. Discard local divergence (dangerous; only if local commits aren’t needed)
  • Hard reset to the upstream:
    • git fetch
    • git reset --hard origin/main
  • This deletes local commits from the branch tip; ensure they are truly disposable.
  1. Configure your default pull strategy
  • Always rebase on pull:
    • git config --global pull.rebase true
  • Always allow merge commits on pull:
    • git config --global pull.ff false
  • Always require fast‑forward (current behavior that triggered the error):
    • git config --global pull.ff only

Per‑repo versions: drop --global and run in the repo.

Handling conflicts during rebase or merge

  • When conflicts occur, Git stops and marks files as conflicted.
  • Use your diff tool to fix them, then:
    • Rebase: git add <file>, git rebase --continue
    • Merge: git add <file>, git commit (if Git didn’t auto‑create the merge commit)
  • To abort:
    • Rebase: git rebase --abort
    • Merge: git merge --abort

Special cases

  • Submodules: git submodule update --ff-only can fail similarly. Use git submodule update --merge or update the recorded submodule commit in the superproject, then commit.
  • Protected branches: After a rebase, you may need --force-with-lease to push rewritten history. Some servers disallow this on protected branches; open a PR instead.

Pitfalls

  • Rebasing shared branches: Rewriting history after others pulled your commits forces them to reconcile. Prefer merging in that case.
  • Using --force instead of --force-with-lease: --force-with-lease protects others’ work by refusing to overwrite unexpected remote tips.
  • Dirty working tree: Pulling with local uncommitted changes can fail or complicate rebases. Use --autostash or stash manually.
  • Wrong upstream: Ensure your branch tracks the intended remote branch:
    • git branch -vv
    • Set upstream: git branch --set-upstream-to origin/main

Performance notes

  • Fetch first: git fetch decouples network from local operations, making rebase/merge faster and more predictable.
  • Large histories: Rebases work only on your local commits; they are usually fast. Merges can examine more history but are still cheap locally.
  • Minimize conflict churn: Enable conflict reuse to speed repeated merges:
    • git config --global rerere.enabled true
  • Shallow clones reduce network time: git clone --depth 50 if full history isn’t needed.
  • Keep repos healthy: git gc --aggressive is rarely necessary; prefer git gc and pruning stale refs with git fetch --prune.

FAQ

  • Why does git pull --ff-only fail but git pull --rebase works?

    • Rebase rewrites your local commits on top of upstream, avoiding a merge commit. Fast‑forward‑only refuses non‑fast‑forward updates.
  • Is rebase safe?

    • Yes for local/unshared commits. Avoid rebasing commits that others already pulled unless coordinated.
  • When should I choose merge over rebase?

    • When preserving exact history or when the branch is shared and you want to avoid history rewrites.
  • Do I need to force push after rebasing?

    • Only if you had previously pushed the old commits. Use git push --force-with-lease.
  • Can I make this error go away permanently?

    • Decide your policy: set pull.rebase true (linear history) or pull.ff false (allow merges) so git pull won’t abort on divergence.

Series: Git

DevOps