Table of Contents
- Overview
- Quickstart (most common fix)
- Minimal working example (reproduce and fix)
- Choose a resolution strategy
- Step-by-step: rebase-based fix
- Step-by-step: merge-based fix
- When to use --force-with-lease
- Handling special cases
- Recommended configuration
- Pitfalls to avoid
- Performance notes
- Troubleshooting checklist
- FAQ
Overview
A non-fast-forward push rejection happens when your local branch is behind or diverged from the remote branch. Git refuses to update the remote because it would drop or overwrite remote commits.
Typical error:
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to 'origin'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref.
Quickstart (most common fix)
- You have local commits and remote is ahead. Rebase onto the remote, then push:
# Update tracking info
git fetch origin
# Rebase local work onto the latest remote commits
git rebase origin/main # use your branch name
# If conflicts appear: resolve, then
git add <resolved-files>
git rebase --continue
# Push the linearized history
git push
If you previously rewrote history (e.g., git commit --amend or rebase) and need to update the remote, use a guarded force:
git push --force-with-lease
Minimal working example (reproduce and fix)
# Create a bare central repo
mkdir -p /tmp/git-nff && cd /tmp/git-nff
git init --bare central.git
# Clone it twice (simulate two developers)
git clone central.git alice && git clone central.git bob
# Alice adds a commit and pushes
cd alice
echo A > file.txt
git add file.txt
git commit -m "Alice: add file"
git push -u origin main 2>/dev/null || git push -u origin master # pick default branch
BR=$(git symbolic-ref --short HEAD)
cd ..
# Bob makes a conflicting commit without fetching Alice's change
cd bob
echo B > file.txt
git add file.txt
git commit -m "Bob: add file"
# This push will be rejected as non-fast-forward
git push || true
# Fix: rebase Bob onto remote, then push
git fetch origin
git rebase origin/$BR
# Resolve conflicts if prompted, then continue
# Finally
git push
Choose a resolution strategy
| Situation | Goal | Command(s) | Safe for shared branches |
|---|---|---|---|
| You want a linear history | Reapply your commits on top of remote | git fetch → git rebase origin/<branch> → git push | Yes |
| You want a merge commit | Keep both histories with a merge | git pull --no-rebase or git merge origin/<branch> → git push | Yes |
| Your local commits are disposable | Align local to remote, discard local | git fetch → git reset --hard origin/<branch> → git push (no-op) | Yes |
| You rewrote history locally (amend/rebase) and need remote to match | Overwrite remote safely | git push --force-with-lease | Usually; check policies |
| Branch is protected (no force, require up-to-date) | Satisfy policies | git fetch → git rebase or git merge → git push | Yes |
Step-by-step: rebase-based fix
- Ensure your working tree is clean.
- Commit or stash changes.
- Fetch the latest remote state.
git fetch origin
- Rebase your local branch onto the remote.
git rebase origin/<branch>
- Resolve conflicts if any.
- Open conflicted files, edit to keep desired changes.
git add <file>for each resolved file.- Continue:
git rebase --continue. - To abort:
git rebase --abort.
- Push.
git push- If you rewrote history earlier and the push still rejects, use
git push --force-with-lease.
Step-by-step: merge-based fix
git fetch origingit merge origin/<branch>- Resolve conflicts →
git add ...→git commit git push
Use this when merges are acceptable or required by policy.
When to use --force-with-lease
Use only when your local history intentionally rewrites commits and you need the remote to match (e.g., after git rebase -i, git commit --amend). --force-with-lease protects against clobbering others’ new remote commits by ensuring your push only proceeds if the remote tip matches what you last fetched.
Examples:
# After interactive rebase on a feature branch
git push --force-with-lease origin feature/login
# Never use plain --force on shared branches
Handling special cases
- Out-of-date branch protection: Some servers require “update branch before merging.” Do
git pull --rebasebefore opening or updating PRs. - Protected main branch: Force pushes may be blocked. Use rebase or merge, not force.
- Dropped remote commits (history rewrite upstream): You may need
git fetch --prunethengit rebase --rebase-mergesor reset to the new tip after review. - Tags: Non-fast-forward tag updates are often blocked. Create a new tag or delete+recreate intentionally:
git tag -d v1.2 && git push origin :refs/tags/v1.2 && git tag v1.2 <commit> && git push --tags(coordinated with policy).
Recommended configuration
# Prefer rebase for pull (linear history)
git config --global pull.rebase true
# Auto-stash before rebase to avoid dirty tree issues
git config --global rebase.autoStash true
# Prune deleted remote branches on fetch
git config --global fetch.prune true
Pitfalls to avoid
- Using
--forceinstead of--force-with-leaseon shared branches. - Rebasing public branches others already based work on, causing duplicate/conflicting histories.
- Ignoring conflicts during rebase/merge; always inspect and test before pushing.
- Working with an unclean tree; stash or commit first to avoid losing changes.
- Mixing merge and rebase strategies haphazardly; agree on a team policy per branch.
Performance notes
- Fetch depth and partial clone: For large repos,
git fetch --depth=50orgit clone --filter=blob:nonecan speed up syncs, but be cautious when rebasing deep histories. - Prune and GC: Periodically run
git fetch --pruneandgit gc --aggressivelocally to reduce object store size; on servers, admins may tune GC schedules. - Pack/delta tuning: Pushing many small commits is fine, but squashing (when allowed) can reduce push size and CI load.
- Large files: Use Git LFS for binaries to avoid heavy pushes that exacerbate non-FF conflicts on large objects.
Troubleshooting checklist
- Did you run
git fetch originand target the correct branch? - Do you actually want a merge (
git merge) instead of rebase? - Are there branch protections blocking force pushes?
- Did someone rewrite remote history? Compare:
git log --oneline --graph --decorate --all. - Do you need
--force-with-leasedue to local history edits?
FAQ
- What is a fast-forward? When the remote tip is an ancestor of your local tip; Git can move the pointer without creating a merge.
- What causes non-fast-forward? Divergent histories: both sides have commits the other does not.
- Rebase or merge? Rebase for linear history; merge to preserve original commit topology.
- How do I undo a bad rebase? Use
git reflogto find the previous HEAD, thengit reset --hard <hash>. - How do I avoid this long-term? Regularly
git fetchand rebase small batches before pushing; align with branch protection policies.