git push rejected non-fast-forward: decision path for safe recovery (2026)

Published February 28, 2026 · 8 min read

Fix It With a Tool

Compare local vs remote changes before choosing a recovery path

Use Git Diff Viewer to check exactly what changed before rebasing, merging, or correcting target branch selection.

When git push is rejected with non-fast-forward, the safest fix depends on the failure state. Use this decision path to avoid accidental history overwrite.

Guardrail: Do not force-push shared branches. Confirm branch ownership and remote state first.

Decision path

  1. Behind remote: remote has new commits, local does not
  2. Diverged history: both local and remote have unique commits
  3. Wrong target branch: upstream or push target mismatch
  4. Final safety check before retrying push

1. Behind remote

Use this path when remote moved ahead and your local branch has no unique commits that need preservation.

# inspect current branch and upstream
git status -sb
git branch -vv

# sync remote refs
git fetch origin

# confirm remote-only commits
git log --oneline HEAD..origin/your-branch

# replay local work on top of remote (or merge if your team policy requires)
git pull --rebase origin your-branch

# retry push
git push origin your-branch
Do not force-push here: behind-remote rejection usually resolves with fetch + integrate + normal push.

2. Diverged history

Use this when both local and remote contain unique commits. Preserve both sides, then resolve conflicts safely.

# fetch latest remote graph
git fetch origin

# visualize divergence
git log --oneline --left-right --graph origin/your-branch...HEAD

# choose one integration strategy
git rebase origin/your-branch
# or: git merge origin/your-branch

# resolve conflicts if needed
git add <resolved-files>
git rebase --continue   # if rebasing
# or: git commit         # if merging

# push integrated result
git push origin your-branch
Team-safe default: use rebase only if branch policy allows rewritten local history; otherwise merge.
Do not force-push while conflicts are unresolved. Recover cleanly first, then push integrated history.

3. Wrong target branch

Use this when you are pushing the right commit to the wrong branch name, or upstream tracking points to an unexpected remote ref.

# verify local branch and upstream mapping
git status -sb
git branch -vv
git remote -v

# check where HEAD is expected to go
git config --get branch.$(git branch --show-current).merge

# push explicitly to the intended target branch
git push origin HEAD:correct-branch

# optionally set upstream for future pushes
git push -u origin correct-branch
Do not force-push to "make it work". Correct the branch mapping first, especially on protected branches.

If the rejection appears right after stash/worktree cleanup, these guides help classify side effects quickly: git stash pop conflict and git worktree already checked out fix.

If push errors are preceded by fatal: Unable to create '.git/index.lock': File exists, run the git index.lock file exists root-cause fix guide first so remote history fixes are not mixed with local lock-state failures.

4. Final safety check before retrying push

  1. Run tests for the branch scope.
  2. Inspect diff one more time with Git Diff Viewer.
  3. Confirm current branch and target branch names are correct.
  4. Push normally; escalate only if policy explicitly permits force-with-lease.

FAQ

Can I skip fetch and just force-push?

No. Always fetch first so you can classify the failure state and avoid overwriting remote commits.

When is --force-with-lease acceptable?

Only on branches you own, after intentional history rewrite, and only when your team policy allows it.

Why does non-fast-forward happen repeatedly on active branches?

Because remote changes continue landing between your local updates. Shorten pull-to-push windows and verify upstream mapping before each push.