Git

Git Push Rejected Non-Fast-Forward Fix

A "non-fast-forward" rejection isn't Git being difficult — it's Git protecting you from accidentally overwriting commits that someone else (or another one of your own sessions) already pushed to the remote branch.

The Problem

You try to push your local commits, and instead of succeeding, Git rejects it with:

! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to 'origin'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart.

Your local commits are real and correct, but the remote branch has moved forward since you last pulled — usually because someone else pushed, or you pushed from a different machine earlier.

Why It Happens

Git only allows a "fast-forward" push by default — meaning your local branch history must contain everything currently on the remote, plus your new commits on top. When the remote has commits your local branch doesn't have, a plain push would require rewriting remote history, which Git blocks unless you explicitly force it. Common causes:

  • A teammate pushed to the same branch after your last pull, and you haven't fetched those changes yet
  • You pushed from a different machine or a different clone of the same repo, and this local copy is now behind
  • A CI/CD pipeline or automated process committed directly to the branch (version bumps, generated files) without you knowing

The Fix

In almost all cases, the correct fix is to integrate the remote changes first, not to force-push over them. Pull with rebase to keep history clean:

git pull --rebase origin main

This replays your local commits on top of the latest remote commits, avoiding an unnecessary merge commit. If conflicts appear during the rebase, resolve them file by file, then continue:

git add resolved-file.js
git rebase --continue

Once the rebase finishes cleanly, push normally:

git push origin main

If you'd rather preserve a clear merge commit instead of rebasing (some teams prefer this for auditability), pull without rebase, resolve any conflicts as a normal merge, then push:

git pull origin main
git push origin main

Avoid git push --force on shared branches unless you are absolutely certain the remote commits you'd be discarding are safe to lose — force-pushing on a branch other people are also working from can silently delete their work from the remote, even though it may still exist in their local history until they notice something is wrong.

Still Not Working?

If you're certain the remote commits are stale or genuinely wrong (for example, an accidental push from an old branch state) and you have explicit agreement from your team, use the safer force option instead of a plain force push:

git push --force-with-lease origin main

--force-with-lease checks that the remote hasn't changed since you last fetched it, and refuses to overwrite it if someone pushed something new in the meantime — giving you the power of a force push without the risk of silently destroying someone else's very recent work.

It's also worth understanding why this protection exists in the first place, since it changes how you think about the workflow going forward. Every push is Git comparing "what I think the remote branch looks like" against "what the remote branch actually looks like right now." If those two don't match — because someone else moved the remote forward since your last fetch — Git has no safe way to know whether your push is meant to build on top of their work or accidentally erase it. Rejecting the push by default is the conservative, safe choice; it forces a human to make the decision explicitly rather than Git guessing.

If this keeps happening frequently on a fast-moving branch with multiple contributors, it's often a sign that pulling more frequently — before starting new work, not just before pushing — would save time overall. Fetching and rebasing in small, frequent steps tends to produce much smaller, easier-to-resolve conflicts than letting local and remote history diverge for a long stretch before reconciling everything at once.