When Git prints ! [rejected] master -> master (non-fast-forward) or the variant with (fetch first), it means the remote branch has commits your local branch does not. Git refuses to overwrite them. The fix is almost always: pull those commits first, then push.
Why This Happens
A fast-forward push is only possible when your local branch is a strict extension of the remote β every commit the remote has, you also have. The moment someone else pushes to the same branch while you were working, the histories diverge:
Remote: A -- B -- C β teammate pushed C
You: A -- B -- D β your local commit D
Git cannot figure out where to put C and D without your input. It rejects the push and tells you to fetch first.
Reproducing the Error
Here is the exact output you will see:
$ git push origin master
To /path/to/remote.git
! [rejected] master -> master (fetch first)
error: failed to push some refs to '/path/to/remote.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
Older Git versions show (non-fast-forward) instead of (fetch first); the cause and fix are identical.
Fix 1: Rebase Your Work on Top (Recommended)
This is the cleanest approach. It replays your local commits on top of the remote commits, keeping the history linear:
$ git pull --rebase origin master
From /path/to/remote.git
* branch master -> FETCH_HEAD
Successfully rebased and updated refs/heads/master.
$ git push origin master
To /path/to/remote.git
d7e2304..26db0f8 master -> master
After the rebase, your history looks like this:
A -- B -- C -- D β your commit D replayed after C
No extra merge commit, no "Merge branch 'master'" noise in the log.
Fix 2: Merge the Remote Changes In
If you prefer merge commits, or your team uses a merge-based workflow:
$ git pull --no-rebase origin master
From /path/to/remote.git
* branch master -> FETCH_HEAD
Merge made by the 'ort' strategy.
newfile.txt | 1 +
1 file changed, 1 insertion(+)
$ git push origin master
To /path/to/remote.git
ef4f1cb..6072dd8 master -> master
The log gains a merge commit:
$ git log --oneline -4
6072dd8 Merge branch 'master' of /path/to/remote
ef4f1cb teammate new file
ffaf50c my local work
26db0f8 previous commit
Both approaches are correct; the difference is cosmetic unless your team has a strict policy on linear history.
The "Divergent Branches" Prompt in Git 2.27+
If you run plain git pull without a flag on a recent Git version, you may hit a second error before you even get to push:
hint: You have divergent branches and need to specify how to reconcile them.
hint:
hint: git config pull.rebase false # merge
hint: git config pull.rebase true # rebase
hint: git config pull.ff only # fast-forward only
fatal: Need to specify how to reconcile divergent branches.
Git changed its default in 2.27 to require an explicit choice. Fix this once with a global config so you never see it again:
# Rebase approach (recommended for most solo and feature-branch workflows)
$ git config --global pull.rebase true
# Or merge approach
$ git config --global pull.rebase false
After setting this, plain git pull will work without prompting.
Handling a Conflict During Rebase
If both you and your teammate edited the same lines, the rebase will pause at a conflict:
$ git pull --rebase origin master
CONFLICT (content): Merge conflict in README.md
error: could not apply abc1234... my local change
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
Open the conflicted file, resolve the markers, then continue:
$ git add README.md
$ git rebase --continue
If you want to abandon the rebase entirely and go back to where you were:
$ git rebase --abort
When to Use --force-with-lease
--force-with-lease is not a fix for the non-fast-forward error in the normal case above. Use it only when you have intentionally rewritten history that you already pushed β for example, after amending the last commit or running an interactive rebase on a feature branch you own:
# You amended your last pushed commit and need to overwrite it
$ git push --force-with-lease origin my-feature-branch
Unlike --force, --force-with-lease refuses to push if someone else pushed to the branch since you last fetched β it protects you from overwriting a teammate's work by accident.
Never use --force or --force-with-lease on a shared branch like main or master unless you are the only person working on it and you know what you are doing.
Quick Reference
# Standard fix (keeps history linear)
git pull --rebase origin <branch>
git push origin <branch>
# Merge-based fix (adds a merge commit)
git pull --no-rebase origin <branch>
git push origin <branch>
# Stop Git from asking each time
git config --global pull.rebase true # or false
# Force push only for history you own and already rewrote
git push --force-with-lease origin <your-feature-branch>
Tested on Ubuntu 24.04 with git 2.43.0.