Fix a push rejected as non-fast-forward
My push was rejected — 'Updates were rejected because the remote contains work that you do not have'. What now?
Short answer
Someone pushed commits you don't have yet, so your push would erase them. Git is protecting you. Fetch and integrate their work first with git pull --rebase, then push again. Never reach for --force here unless you genuinely intend to delete what's on the server.
git pull --rebase
git push
Replay your commits on top of theirs, then push cleanly
Does this match your situation?
- '! [rejected] main -> main (non-fast-forward)' or '(fetch first)'.
- 'Updates were rejected because the remote contains work that you do not have locally.'
- A teammate pushed while you were working.
- You amended or rebased commits that were already pushed.
Step-by-step fix
See what the server has that you don't
Fetch first - it downloads their commits without touching your files, so you can look before deciding anything. This is always safe and always the right first move.
step 1git fetch origin git log --oneline HEAD..origin/main # their commits you're missing git log --oneline origin/main..HEAD # your commits they're missingIntegrate their work, then push
Rebase replays your commits on top of theirs for a clean linear history. Merge keeps both histories and adds a merge commit. Either works; rebase is the usual choice for a feature branch that only you are on.
step 2git pull --rebase origin main # resolve any conflicts, then: git pushIf the rebase hits a conflict, fix the files, git add them, then git rebase --continue.
Or merge instead, if the branch is shared
When several people work on the same branch, rebasing rewrites commits they already have. Merging is the safer integration in that situation.
step 3git pull --no-rebase origin main git pushOnly force-push if you deliberately rewrote history
If you amended, squashed or rebased commits that were already pushed, the rejection is expected and correct — you really do want to replace the remote branch. Use --force-with-lease so the push aborts if someone else pushed in the meantime.
step 4git push --force-with-leaseNever force-push a branch other people work on without telling them. On main, don't force-push at all.
Why this works
A push is only allowed to move a remote branch forward along its own history — a 'fast-forward'. If the server's branch contains commits that aren't ancestors of what you're pushing, moving the pointer would orphan them, so Git refuses. That rejection is the single most valuable safety check Git has: it is the reason accidental data loss on shared branches is rare. Pulling makes their commits ancestors of yours, which restores the fast-forward relationship and lets the push through.
How to stop it happening again
- Run git pull --rebase before you start work each day, and again before pushing.
- Set it as the default: git config --global pull.rebase true.
- Push small changes often — the longer you wait, the more divergence you accumulate.
- Never amend or rebase commits that have already been pushed to a shared branch.
Commands used in this guide
git pullFetch from the remote and immediately integrate the changes into your branch.
git fetchDownload new commits from the remote without changing any of your files.
git pushUpload your commits to the remote repository.
git rebaseReplay your commits on top of another branch to produce a straight, linear history.
git logBrowse commit history with as much or as little detail as you want.
Still stuck?
Search the full command reference and every other rescue guide — there are 56 of them, covering everything from detached HEAD to force-push disasters.
Browse all guides →Frequently asked questions
What does 'non-fast-forward' actually mean?
It means the commit you're pushing is not a descendant of what the remote branch currently points at, so accepting the push would drop the commits in between. Git only allows a branch to move forward along its own history unless you explicitly force it.
Should I just use git push --force to fix it?
Almost never. Force-pushing deletes the commits on the server that caused the rejection — which are usually a colleague's work. Only force-push when you intentionally rewrote your own history, and use --force-with-lease so it aborts if the remote moved unexpectedly.
Why does this keep happening on my feature branch?
Usually because you amended or rebased commits after pushing them. Once a commit is pushed, changing it produces a new hash and your branch no longer fast-forwards. Either stop rewriting pushed commits, or accept that this branch needs --force-with-lease each time.
Related rescue guides
git pull says I need to specify how to reconcile divergent branches — what do I choose?
Git 2.27+ refuses to guess whether you want a merge or a rebase when both sides have new commits. Pick one — git config --global pull.rebase true gives you clean linear history and is the best default for most people.
Read the fix →Always fixableHow do I undo a commit that's already on the remote?
On a shared branch, use git revert <hash> — it adds a new commit that reverses the change, so nobody else has to do anything. Rewriting with reset plus a force-push is only appropriate on a branch you're certain nobody else has pulled.
Read the fix →Usually recoverableSomeone force-pushed and my commits are gone — how do I get them back?
Your local clone still has the commits, and your reflog records where the branch pointed before the force-push overwrote it. Find that hash with git reflog, recover it to a branch, and push it back. If your local copy is already clean, a teammate's clone or the server's reflog may still have it.
Read the fix →Always fixableGit won't let me pull because of local changes — how do I fix it?
Git is refusing to overwrite uncommitted edits to files the incoming commits also change. Commit them, stash them, or discard them — stashing is the reversible choice and takes one command.
Read the fix →