Git Developer Guide

fatal: Need to specify how to reconcile divergent branches

What git pull is telling you

You ran git pull and got a screen of hints ending in a fatal error. This is the whole message from Git 2.50:

hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint:   git config pull.rebase false  # merge
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
hint:
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
fatal: Need to specify how to reconcile divergent branches.

Divergent means both sides have commits the other does not: you committed locally, and someone (or you, on another machine) pushed to the same branch. Git cannot simply move your branch forward to the remote's commit, so it has to either merge the two lines or replay yours on top of theirs, and it will not pick for you. Nothing has been lost or changed. The pull did fetch, so your origin/main is now current, but your branch and working tree are exactly as they were.

If your branch is only behind, with no local commits, git pull fast-forwards without asking and you never see this. git status describes the same situation once you have fetched:

$ git status
On branch main
Your branch and 'origin/main' have diverged,
and have 2 and 1 different commits each, respectively.
  (use "git pull" if you want to integrate the remote branch with yours)

Every message and command on this page was reproduced with Apple Git 2.50, from the Xcode command line tools, using a throwaway bare remote and two clones.

Look at both sides first

Before choosing, see which commits are on each side. The three dots mean commits on one side or the other but not both, and @{u} is your branch's upstream:

$ git rev-list --left-right --count HEAD...@{u}
2	1
$ git log --oneline --left-right HEAD...@{u}
< e0587da a work2
> 5ef8f2e b work
< 2260c4c a work

< is yours and not pushed, > is on the remote and not yet in your branch. If the remote side is something you did not expect, such as a rewritten history after someone force-pushed, stop here and find out why before merging it into your work. Git ahead and behind remote explains the counts in more detail.

Fix it this once

Rebase replays your commits on top of the remote's, giving a straight history. This is the usual choice for local commits nobody else has seen:

$ git pull --rebase
Successfully rebased and updated refs/heads/main.
$ git log --oneline --graph
* 9949cc3 a work2
* efd2abc a work
* 5ef8f2e b work
* ffc5cfa one

Your commits get new hashes (2260c4c became efd2abc), which is why you should not rebase commits other people have already pulled. Afterwards the branch is only ahead, and git push works.

Merge keeps both lines as they are and joins them with a merge commit:

$ git pull --no-rebase
$ git log --oneline --graph
*   be61504 Merge branch 'main' of github.com:you/app
|\
| * 5ef8f2e b work
* | e0587da a work2
* | 2260c4c a work
|/

Nothing is rewritten, at the cost of a merge commit every time this happens. Some teams want exactly that; others reject such commits in review.

Fast-forward only does not reconcile anything. On a diverged branch it refuses, with a different message:

$ git pull --ff-only
hint: Diverging branches can't be fast-forwarded, you need to either:
hint:
hint: 	git merge --no-ff
hint:
hint: or:
hint:
hint: 	git rebase
hint:
hint: Disable this message with "git config set advice.diverging false"
fatal: Not possible to fast-forward, aborting.

That is the point of it: it only ever moves your branch forward, and makes you choose by hand when it cannot.

Set a default so it stops asking

The hint offers three settings. Pick one for every repository with --global, or leave it out to set it for the current repository only:

git config --global pull.rebase true    # rebase local commits on top
git config --global pull.rebase false   # merge, the old default
git config --global pull.ff only        # refuse; decide each time

pull.rebase false gives you what git pull did before Git started asking. pull.ff only is the cautious one: ordinary pulls still fast-forward, and a diverged branch stops with Not possible to fast-forward so you can look first. If you choose rebase, also consider git config --global rebase.autoStash true, because a rebasing pull refuses to start with uncommitted changes:

$ git pull --rebase
error: cannot pull with rebase: You have unstaged changes.
error: Please commit or stash them.

With autostash, Git sets your changes aside, rebases, and puts them back (Created autostash … Applied autostash.). git pull --rebase --autostash does the same once. A flag on the command line always beats the configured default.

If the rebase or merge stops on a conflict

When both sides changed the same lines, the pull stops part-way:

CONFLICT (content): Merge conflict in g
error: could not apply 4c3db61... d edits g

Fix the file, git add it, and run git rebase --continue (or git commit after a merge). To back out completely and get your branch back exactly as it was before the pull, run git rebase --abort or git merge --abort. After an abort, git status -sb shows the branch diverged again, [ahead 1, behind 1], ready for you to try the other strategy.

When you want the remote's version and not yours

Sometimes the local commits are junk: an experiment, or a branch the remote has since rewritten on purpose. Then neither merge nor rebase is right, and you want your branch to match the remote. Save a pointer to your commits first, then reset:

git branch backup-before-reset
git reset --hard @{u}

reset --hard also throws away uncommitted changes in tracked files, so commit or stash anything you want to keep. The backup branch can be deleted once you are sure.

Catch it before you pull

A branch usually diverges because you committed on one Mac while the same branch moved on another, or you forgot to push before switching. GitMon shows every repository on your Mac in the menu bar popover with its ahead (↑) and behind (↓) counts, so a repo with both is a diverged one, and you see it before you start work rather than when git pull stops. It is read-only and never fetches: the counts come from the remote-tracking refs your last fetch left, as with git status. Pair it with a fetch loop from git pull or fetch every repo in a folder.

Download GitMon Free Trial Learn more

Related Guides