Fix: git push rejected – non-fast-forward error

The Error

You run git push and get:

To github.com:user/repo.git
 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to 'github.com:user/repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Why This Happens

Git is telling you that the remote branch has commits that your local branch does not. Your local history and the remote history have diverged, so Git refuses to push because doing so would overwrite those remote commits.

Common causes:

  • A teammate pushed to the same branch. Someone else merged or pushed new commits to main while you were working locally.
  • You pushed from another machine. You committed and pushed from your laptop, then tried to push older local state from your desktop.
  • You amended or rebased locally. Running git commit --amend, git rebase, or git reset rewrites commit history. The rewritten commits have different hashes than what the remote already has, so Git sees them as divergent. (Rebasing can also leave you in a detached HEAD state if interrupted.)
  • A PR was squash-merged on GitHub/GitLab. You merged your feature branch through the web UI with “Squash and merge,” then tried to push the original (non-squashed) branch again.

In all of these cases the underlying problem is the same: your local branch tip is not a direct descendant of the remote branch tip, so a push would lose commits.

Fix 1: Pull and Merge (Safest)

The simplest fix. Fetch the remote commits and merge them into your local branch:

git pull origin main

This creates a merge commit that joins the two histories together. Resolve any merge conflicts if they appear, then push:

git push origin main

This is the safest option because no commits are lost or rewritten. The downside is that it adds a merge commit to your history.

Fix 2: Pull with Rebase (Cleaner History)

If you prefer a linear history without merge commits, rebase your local commits on top of the remote ones:

git pull --rebase origin main

This takes your local commits, temporarily removes them, pulls the remote commits, then replays your commits on top. If there are conflicts, Git will pause and ask you to resolve them one commit at a time. After resolving:

git rebase --continue

Once the rebase is complete, push normally:

git push origin main

Use this when you have a small number of local commits and want a clean, linear history.

Fix 3: Force Push with Lease (Safe Override)

If you intentionally rewrote history (for example, after an interactive rebase or amend) and you want the remote to match your local branch, use --force-with-lease:

git push --force-with-lease origin main

--force-with-lease checks that the remote branch is still at the commit you last fetched. If someone else pushed new commits since your last fetch, the push is rejected. This prevents you from accidentally overwriting a teammate’s work.

Only use this when you understand that you are replacing the remote history with your local history. This is appropriate after an intentional rebase or amend, not as a workaround for being behind.

Fix 4: Force Push (Use with Extreme Caution)

git push --force origin main

This unconditionally overwrites the remote branch with your local branch. It does not check whether someone else pushed in the meantime.

Only use --force on personal branches that nobody else is working on. Never force push to a shared branch like main or develop unless you have coordinated with your team and everyone is aware.

—force vs —force-with-lease

--force--force-with-lease
Overwrites remoteYes, unconditionallyOnly if remote hasn’t changed since your last fetch
Risk of losing others’ workHighLow (fails if remote was updated)
When to usePersonal/throwaway branches onlyAfter intentional local history rewrites

In practice, prefer --force-with-lease whenever you need to force push. There is almost no reason to use --force on any branch that others might be using.

Still Not Working?

Protected branches block force push

GitHub, GitLab, and Bitbucket allow administrators to protect branches. If main is protected, you will see an error like:

remote: error: GH006: Protected branch update failed for refs/heads/main.
remote: error: Cannot force-push to this protected branch.

In this case you cannot force push at all. You must use Fix 1 or Fix 2 to integrate the remote changes, or ask a repository admin to temporarily disable branch protection.

On GitHub, this is under Settings > Branches > Branch protection rules. On GitLab, it is under Settings > Repository > Protected branches.

Diverged branches after a rebase

If you rebased a feature branch that was already pushed, your local and remote copies of that branch have diverged. A normal push will fail. This is expected. Use --force-with-lease to update the remote:

git push --force-with-lease origin feature-branch

This is a normal part of rebase workflows. Just make sure no one else is basing work on that branch, or coordinate with them first.

First push to a new remote with existing commits

If you initialized a repo locally and are pushing to a new remote that already has commits (for example, GitHub created a repo with a README), the histories are completely unrelated. You will see:

! [rejected]        main -> main (fetch first)

Fix this by pulling with --allow-unrelated-histories (see our full guide: Fix: fatal: refusing to merge unrelated histories):

git pull origin main --allow-unrelated-histories

Resolve any conflicts, then push:

git push origin main

You fetched but the error persists

If git pull --rebase still fails, your local branch might be tracking the wrong remote branch. Verify with:

git branch -vv

Make sure your branch is tracking origin/main (or whichever remote branch you intend). If it is not, set the upstream:

git branch --set-upstream-to=origin/main main

Then pull and push again. If you get a Permission denied (publickey) error when pushing, see Fix: git permission denied (publickey).

Related Articles