failed to push some refs / git push rejected (non-fast-forward) — how to fix
Quick Answer
git pull --rebase origin main
git push origin main
When this happens
! [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. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
Someone else pushed new commits to the remote branch since your last pull. This is the usual cause of "failed to push some refs" and the non-fast-forward error.
Why Git rejects the push
The remote branch contains history you do not have locally
A normal push is allowed only when the remote branch can move forward to your commit without losing any commits. If origin/main has commit B while your local main was created from the earlier commit A, moving the remote directly to your commit would hide B. Git rejects that update as non-fast-forward.
This message means the remote connection and authentication usually worked. It is a history-integration problem, not an SSH-key or access-token problem. Fetch the current remote state before deciding whether to rebase, merge, or intentionally replace it.
Diagnose first
Confirm the branch and compare both histories
Do not start with a force push. First verify the checked-out branch and its upstream, then fetch without modifying your working branch. The final command shows commits that exist only locally on the left and only remotely on the right.
- Lines beginning with
<are commits reachable only from your current branch. - Lines beginning with
>are commits reachable only fromorigin/main. - If the target branch is not
main, replace it consistently in every command.
git status
git branch -vv
git remote -v
git fetch origin
git log --oneline --left-right --graph HEAD...origin/main
Recommended fix
Replay your local commits on the updated remote branch
Rebase keeps the branch linear by temporarily removing your local-only commits, advancing to origin/main, and replaying those commits. This is appropriate for unpublished local commits. It changes their commit IDs, so avoid rebasing commits that teammates already use.
git fetch origin
git rebase origin/main
git push origin main
Conflict handling
Resolve each conflicted commit or abort cleanly
If rebase stops, git status lists the conflicted files. Edit each file, remove the conflict markers, run the relevant tests, stage the resolved files, and continue. Repeat until every local commit has been replayed. If the situation is unclear, aborting returns the branch to its pre-rebase state.
git status
# edit and test each conflicted file
git add path/to/resolved-file
git rebase --continue
# Return to the state before rebase if needed
git rebase --abort
Merge alternative
Use a merge when preserving published history matters
A merge combines both histories without rewriting existing commits. It may create a merge commit, but it is often the safer choice on a shared branch or when local commits have already been published. Check the repository policy before choosing between merge and rebase.
git fetch origin
git merge origin/main
git push origin main
Verification
Confirm local and remote now point to the expected history
After the push, fetch once more and compare the branch tips. An empty diff and identical commit IDs confirm that the remote accepted the intended history. Also check the repository host to make sure the expected commits and checks appear on the branch.
git fetch origin
git rev-parse HEAD
git rev-parse origin/main
git diff --stat HEAD origin/main
Other causes & fixes
The remote branch name is different
Use git branch -r to list remote branches. A repository may use master, a release branch, or a feature branch instead of main. Push the branch you actually intend to update.
git branch -r
git push -u origin your-branch
Force push — last resort, never on shared branches
Force push intentionally replaces remote history. Use it only when the branch is yours to rewrite and the remote-only commits are known to be unwanted. --force-with-lease is safer than --force because it fails if the remote changed since your last fetch.
git push --force-with-lease origin main
Related