When Your Local and Remote Branches Disagree
One of the more confusing situations in Git is discovering that a local branch and its remote counterpart have gone their separate ways—commits exist on both sides that the other doesn't have. This can be especially tricky because the error messages Git produces in this scenario are often generic and intimidating.
There are two parts to handling this: recognizing you're in this situation at all, and then deciding what to do about it. The right fix depends heavily on your workflow and why the branches diverged in the first place.
What “Diverged” Actually Means
For a local main and a remote main, there are four basic states:
- Up to date: Both branches point to the exact same commit.
- Local is behind: The remote has commits you don't have locally. A
git pullwould bring you in sync. - Remote is behind: You have commits the remote doesn't. A
git pushwould bring things in sync. - Diverged: Both branches have commits the other lacks. Neither a simple push nor pull will work cleanly.
In the diverged case, there's no single "correct" recipe for resolution. How you proceed depends on which changes you want to preserve and how you prefer to integrate them.
Spotting a Diverged Branch
There are three common ways to discover that your local branch has diverged from its remote tracking branch.
Check git status
Run git fetch to update your remote tracking refs, then git status. Git will report that your branch and the remote branch have diverged and will tell you how many commits each side has that the other doesn't.
Attempt a git push
A rejected push with a message like ! [rejected] main -> main (non-fast-forward) doesn't always mean the branches have diverged—it can also happen when your local branch is simply behind. But it's a strong signal worth investigating. Running git fetch followed by git status will clarify the situation.
Run git pull
Git may print a clear warning about having "divergent branches," but that's not guaranteed. The behavior depends on your pull configuration:
- With
git config pull.rebase false, Git automatically starts a merge. - With
git config pull.rebase true, Git automatically rebases onto the remote. - With
git config pull.ff only, Git exits withfatal: Not possible to fast-forward, aborting.
Three Ways to Get Back in Sync
Which strategy you choose depends on whether you want to keep both sets of changes or discard one side entirely. Three common approaches cover most cases.
Keep both: git pull --rebase
When you want to integrate the remote commits while preserving your own local work, git pull --rebase replays your local commits on top of the remote branch. This works well when your work sits on main and you want a linear history.
You can make this automatic with git config pull.rebase true, but there's a reason to avoid that: you may not always want to rebase. Sometimes you'll prefer to overwrite one side entirely, and having Git pause to warn you gives you a chance to make that choice deliberately rather than having it made for you.
Discard the remote: git push --force (and safer variants)
If you know the remote commits are wrong or unwanted, git push --force overwrites the remote branch with your local state. This is common on private, single-committer repositories where you might amend a commit after pushing and then force an update. On shared repositories, force-pushing is risky and best prevented with branch protection rules.
A safer alternative is git push --force-with-lease, which aborts if someone else has pushed to the branch since your last fetch or push. It's not foolproof, though: if a tool like VSCode's autofetch runs git fetch continuously, the lease can be based on already-updated refs and no longer protects against clobbering a colleague's work. The more recent --force-with-lease --force-if-includes variant checks the reflog to confirm you've actually integrated the remote state before allowing the force push.
Discard the local: git reset --hard origin/main
The reverse situation—you have local commits that shouldn't be there—calls for abandoning them in favor of the remote state. There's no git pull --force, so git reset --hard origin/main is the way to make your local branch match the remote exactly.
Before doing this, run git status to ensure you don't have uncommitted changes you'd lose. If you made commits to the wrong branch, you can first create a new branch pointing at your current main to preserve that work, then reset main to the remote state. This pattern is useful when you're not yet sure how to reconcile the two histories—merging two local branches is often more comfortable than dealing with the remote directly.
Two alternatives can achieve the same result without the hard reset:
- Check out another branch, then run
git branch -f main origin/main. - Check out another branch, then run
git fetch origin main:main --force.



