Learn how to resolve the "Failed to Push Some Refs" error in Git with this step-by-step guide. Explore beginner, intermediate, and advanced solutions, including fixing merge conflicts and using force push and rebase.
You finish a chunk of work, run git push, and Git answers with ! [rejected] and error: failed to push some refs. The first time it happens it feels like something’s broken. It isn’t. Git is stopping you from throwing away work.
Here’s the whole thing in one sentence: the remote branch has commits your local branch doesn’t have yet. Someone pushed while you were heads-down, or you rewrote your own history, and now the two sides have split. Pushing would drop those remote commits, so Git refuses. Bring the remote’s work into your branch and the push goes through. Let’s walk it.
Table of Contents
- Introduction to Git and the Push Process
- Understanding the ‘Failed to Push Some Refs’ Error
- Common Causes of the Error
- Fixing the Error: Beginner Solutions
- Intermediate Techniques to Fix the Error
- Advanced Solutions: Force Pushing and Rebasing
- Preventing the Error: Best Practices
- Conclusion
Introduction to Git and the Push Process
When you run git push, Git sends your local commits up to a remote (GitHub, GitLab, a bare repo on your own server) so your teammates, or just future-you on another machine, can pull them down. The key detail: push only moves commits. Anything you haven’t committed stays on your machine and has no bearing on the push at all.
Hold onto that point. A lot of advice about this error gets it wrong, and this is where it goes wrong.
Understanding the ‘Failed to Push Some Refs’ Error
Git usually tells you exactly what’s wrong right under the rejection: (non-fast-forward). That’s the diagnosis. A fast-forward push is one where your branch simply adds new commits on top of what the remote already has, so the remote can slide its pointer forward to match yours. When the remote has commits you don’t, there’s nothing to fast-forward to. As the Git docs put it, pushing anyway “will lose history,” so Git rejects it instead.
Picture two people on one branch, both starting from the same commit. Your teammate pushes first. Now the remote sits one commit ahead of you. You commit your own work and push, and Git stops you: your history and the remote’s have diverged. You need their commit in your branch before yours can land.
Common Causes of the Error
Someone pushed before you did
The classic case. A teammate, or you from another laptop, pushed to the same branch after your last pull. The remote moved ahead, your local branch didn’t, and the two have diverged.
You rewrote your own local history
If you amended, squashed, or rebased commits you’d already pushed, your local branch no longer descends cleanly from the remote tip. Git sees divergence even when you’re the only author. This is the situation where force pushing eventually comes up, carefully.
First push to a branch that isn’t empty
Create a repo on GitHub with a README or license file, then push a local repo you started separately, and you hit the same wall. The remote already holds a commit your history never included.
One thing that is not a cause: uncommitted changes in your working directory. Push only deals with commits, so unsaved or unstaged files are simply ignored during a push. Committing more of them won’t clear a non-fast-forward rejection. Only syncing with the remote will. Keep that in mind as we go, because the fix is about history, not about your working tree.
Fixing the Error: Beginner Solutions
First, get your own work committed
Before you pull anything down, make sure your work is safe, so the sync can’t clobber it. Check what’s in your tree:
git statusCommit anything you want to keep:
git add .
git commit -m "Commit message"(You could git stash instead, but committing is easier to reason about.) Now try the push:
git push origin your-branch-nameIf it lands, you simply hadn’t committed yet and there was no real divergence. If Git still answers with non-fast-forward, the remote is genuinely ahead of you, and no amount of committing changes that. On to the next step.
The real fix: git pull
When the remote is ahead, you pull its commits into your branch first, then push:
git pull origin your-branch-nameBy default this fetches the remote commits and merges them with yours, adding a merge commit if the histories diverged. Once your branch holds the remote’s work plus your own, the push is a clean fast-forward:
git push origin your-branch-nameMost of the time, that’s the entire fix.
Intermediate Techniques to Fix the Error
Resolving Merge Conflicts
Sometimes the pull can’t merge on its own, because you and the remote changed the same lines. Git pauses and hands the decision to you:
- Open the conflicting files in your editor.
- Look for the conflict markers (e.g., <<<<<<< HEAD) that indicate the conflicting sections.
- Decide which side to keep, or stitch both together by hand.
- Once a file looks right, stage it:
git add conflicted-fileThen finish the merge by committing the result:
git commit -m "Resolved merge conflicts"With the conflicts settled, push again:
git push origin your-branch-nameUsing Git Pull with Rebase
A merge pull leaves a merge commit every time histories diverge. If you’d rather keep a straight, linear history, rebase instead:
git pull –rebase origin your-branch-nameThis fetches the remote commits, then replays your local commits on top of them, as if you’d written yours after theirs. You still resolve conflicts if any show up (fix the file, git add it, then git rebase --continue). When it finishes, push:
git push origin your-branch-nameOne honest caveat: rebase only commits you haven’t shared yet. Rebasing commits other people have already pulled rewrites the ground under them.
Advanced Solutions: Force Pushing and Rebasing
Force Push: When and How to Use It
Everything above brings the remote’s work into your branch. Force pushing does the reverse: it tells the remote to drop what it has and take your version instead.
git push –force origin your-branch-nameThe Git docs are blunt about this: --force disables the safety checks and “can cause the remote repository to lose commits.” Use it only when you meant to rewrite history you own, for example after squashing commits on your own feature branch that nobody else is building on. On a shared branch, a force push silently deletes your teammates’ commits from the remote, and that’s not something you undo casually.
There’s a safer version worth defaulting to: git push --force-with-lease. It only forces the push if the remote ref is still where you last saw it, so if someone pushed after your last fetch, the command refuses instead of steamrolling their work. Reach for --force-with-lease first, and save plain --force for the rare moment you truly mean “mine wins, no questions asked.”
Rebasing to Avoid Merge Conflicts
When your branch has drifted well behind the remote, rebasing keeps things tidy by replaying your commits on the current remote tip rather than tying the two together with a merge commit:
git pull –rebase origin your-branch-nameWork through any conflicts as they surface, then push:
git push origin your-branch-nameIf you’d already pushed the commits you just rebased, this is the push where you’d reach for --force-with-lease from above. If you were only catching up before ever pushing, a normal push lands fine.
Preventing the Error: Best Practices
You’ll never kill this error entirely, but you can make it rare:
- Pull (or at least fetch) before you start work and again before you push, so you’re always building on the latest remote commits.
- Talk to your team so two people aren’t reshaping the same files on the same branch at the same time.
- Work on feature branches instead of pushing straight to the main branch, so divergence stays small and easy to reconcile.
- Lean on
git pull --rebasefor everyday syncing to keep history readable, but stop rebasing the moment commits are shared.
Conclusion
“Failed to push some refs” isn’t a broken setup. It’s Git refusing to lose commits on your behalf. The remote is ahead of you; pull its work in, resolve any conflicts, and push. Save force pushing (ideally --force-with-lease) for history you own that nobody else has pulled. Do that, keep pulling before you push, and this one quietly stops showing up.


