Push & pull problemsAlways fixable

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.

quick fix
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

  1. 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 1
    git fetch origin
    git log --oneline HEAD..origin/main    # their commits you're missing
    git log --oneline origin/main..HEAD    # your commits they're missing
    
  2. Integrate 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 2
    git pull --rebase origin main
    # resolve any conflicts, then:
    git push
    

    If the rebase hits a conflict, fix the files, git add them, then git rebase --continue.

  3. 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 3
    git pull --no-rebase origin main
    git push
    
  4. Only 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 4
    git push --force-with-lease
    

    Never 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 pull

Fetch from the remote and immediately integrate the changes into your branch.

git fetch

Download new commits from the remote without changing any of your files.

git push

Upload your commits to the remote repository.

git rebase

Replay your commits on top of another branch to produce a straight, linear history.

git log

Browse 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

← All Git Rescue guides