You type
git push, hit Enter, and Git slaps you with a rejection. No warning. No obvious reason. Just an error that makes you stare at the screen wondering what you broke.
You didn't break anything — but you do need to fix it. Here's how.
What's Actually Going On
When Git says your push was rejected due to a non-fast-forward error, it's telling you one specific thing: your local branch and the remote branch have diverged — they've each gone in different directions since their last shared point.
In plain terms: someone (or something — maybe an automated CI commit) added a commit to the remote branch after you last pulled. Now your local branch has a commit the remote doesn't, and the remote has a commit you don't.
Git's response? Refuse the push. Pushing would silently overwrite the remote's new commit, and Git will never let that happen without a fight.
Spot the Divergence in Seconds
Run this to see exactly where you stand:
git status
If you're in this situation, you'll see something like:
Your branch and 'origin/your-branch-name' have diverged,
and have 1 and 1 different commits each, respectively.
Ahead 1, behind 1 — that's the smoking gun. Local and remote have each moved forward independently. Before you can push, you need to reconcile them.
The Fix: Rebase, Don't Merge
The cleanest way to resolve this is a rebase pull followed by a push:
git pull --rebase origin your-branch-name
git push origin your-branch-name
That's it. Two commands and you're done — assuming no conflicts.
What does this actually do?
Rebasing takes your local commit and replays it on top of the latest remote commit, as if you'd written it after the remote update. The result is a perfectly linear history with no ugly merge bubble cluttering up your git log.
Before rebase:
Remote: A --- B --- C (origin/branch)
Local: A --- B --- D (your work)
After rebase:
Result: A --- B --- C --- D (clean, linear, ready to push)
What If There Are Conflicts?
If your changes overlap with what's on the remote, Git will pause the rebase and ask you to resolve them. Here's how to handle it:
- Open the conflicted file(s) — Git will mark the conflicts clearly.
- Edit the file to keep the right changes.
- Stage the resolved file:
git add your-file.js
- Continue the rebase:
git rebase --continue
- Push once the rebase completes:
git push origin your-branch-name
If things go sideways and you want to start over, you can always abort:
git rebase --abort
Why Rebase Instead of a Regular Pull?
A standard git pull (without --rebase) creates a merge commit to join the two diverged histories. That works, but it adds noise — especially for small, clean feature branches. Your log ends up littered with Merge branch 'origin/xyz' into xyz commits that don't add any meaningful context.
Rebase gives you a cleaner story:
git pull (merge) |
git pull --rebase |
|
|---|---|---|
| History shape | Branching + merge commit | Linear |
| Extra commits | Yes (merge commit) | No |
| Best for | Long-lived or shared branches | Feature branches, solo work |
| Conflict handling | One step | Step-by-step per commit |
For most short-lived feature branches, rebase is the better choice.
How to Prevent This From Happening Again
Non-fast-forward errors are almost always avoidable with a few consistent habits:
- Pull before you push — especially on branches others might touch.
- Push often — smaller, more frequent pushes reduce the window for divergence.
- Set rebase as your default pull strategy so you never accidentally create merge commits:
git config --global pull.rebase true
-
Check your status before pushing — a quick
git statuscosts two seconds and saves you from surprises.
The Big Takeaway
A git push rejection almost never means something is wrong with your code. It means Git is protecting history, and all you need to do is sync up before you push.
The next time you see non-fast-forward, remember the two-step formula:
git pull --rebase origin your-branch-name
git push origin your-branch-name
Sync first. Push second. Ship faster.
Top comments (0)