Home DevOps & Cloud Security Software Engineering AI & Machine Learning Web Development Developer Tools Programming Languages Databases Architecture & Systems Design Emerging Tech About
Developer Tools

git push rejected non-fast-forward: Causes and Fixes

NanoTech Insight
NanoTech Insight
2026-10-20
How this article was made: drafted with AI assistance and grounded in the sources linked at the end of the article. Spotted an error? Let us know.
none

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.

References

git version control command line bash
NanoTech Insight
Published by
NanoTech Insight
Independent technology site

This article was drafted with AI assistance and grounded in the references linked above. Technology changes quickly, so please verify details against official documentation before relying on them in production.

Related Articles

Python asyncio vs threading vs multiprocessing
2026-10-23
Python list comprehension vs generator: when to use which
2026-10-16
json.decoder.JSONDecodeError: Expecting value: line 1 column 1
2026-10-13
git fatal: refusing to merge unrelated histories
2026-10-09
← Back to Home