As of Git version 2.27.0
running the command git pull
will display the following message unless your Git configuration includes certain settings.
warning: Pulling without specifying how to reconcile divergent branches is
discouraged. You can squelch this message by running one of the following
commands sometime before your next pull:
git config pull.rebase false # merge (the default strategy)
git config pull.rebase true # rebase
git config pull.ff only # fast-forward only
You can replace "git config" with "git config --global" to set a default
preference for all repositories. You can also pass --rebase, --no-rebase,
or --ff-only on the command line to override the configured default per
invocation.
Short Explanation
Git wants us to choose how it should handle the situation where our remote branch (e.g. origin/develop
) is out of sync with our local branch (develop
). Many people (myself included) feel the default way Git historically handled this situation was sub-optimal however changing the default behavior is a big deal. Instead Git has made it easy to modify the behavior and added this screen to remind you that you might want to change the default.
Short Fix
The short answer is yes, we do want to change the default behavior and we can do that by running the following from the command line.
git config --global pull.ff only
This will add a line to your global Git configuration file to use the “best” approach when using git pull
.
The Problem We are Solving
When working with Git you have your local branch on your computer (e.g. develop
) and your remote branch (e.g. origin/develop
). Your remote branch typically lives somewhere like GitHub.
You’d like to have the same commits on your local branch and your remote branch, so it looks something like this.
develop origin/develop
cem32k cem32k
b4d2o1 b4d2o1
abc123 abc123
(If viewing your Git branch as a list of commits like this seems unfamiliar, I suggest checking out my post on How to Improve Git Log)
Someone Else Adds a Commit
When someone else uses git push
to add their commit (e.g. zyx911
) to origin/develop
, we get out of sync.
develop origin/develop
zyx911
cem32k cem32k
b4d2o1 b4d2o1
abc123 abc123
The good news is we can bring things back in sync by running git pull
. In this case, it is obvious to Git that by adding zyx911
to our commits, we’ll be in sync. This is called a fast-forward merge.
develop origin/develop
zyx911 zyx911
cem32k cem32k
b4d2o1 b4d2o1
abc123 abc123
The command we added above (git config --global pull.ff only
) sets this to be the only kind of merge that Git should do unless we explicitly tell it otherwise.
Both Add a Commit
Imagine the situation where someone else adds a commit to the remote branch (e.g. zyx911
gets added to origin/develop
) and at the same time we add a commit to our local branch (e.g. we add dg34mp
to develop
).
develop origin/develop
dg34mp zyx911
cem32k cem32k
b4d2o1 b4d2o1
abc123 abc123
Now when we run git pull
Git says, “Whoa, hold on! I can’t add the zyx911
commit to our local branch because there is an extra commit on our local branch that doesn’t exist on the remote branch!” (a.k.a. I don’t know how to handle zyx911
).
Note: This is the same situation I discuss in Git failed to push some refs.
Three Ways to Handle the Situation
Create a Merge Commit
This is the historical default behavior. Git creates a new commit (e.g. 3649fc
) which is a parent to both dg34mp
and zyx911
. Merge commits are a tremendously useful tool in Git, however they also bring with them complexity, which is why most agree that Git should not create a merge commit without the user explicitly requesting it.
Additionally, because we are merging these to different commits this is a common time to have merge conflicts.
develop origin/develop
3649fc
dg34mp zyx911 zyx911
cem32k cem32k
b4d2o1 b4d2o1
abc123 abc123
Rebase our Local Commits
Rebasing is an important Git idea that deserves an entire post of its own but in broad strokes:
Rebasing takes our local commit (dg34mp
) and temporarily removes it from our local branch, which makes it an easy fast-forward merge
develop origin/develop
zyx911 zyx911
cem32k cem32k
b4d2o1 b4d2o1
abc123 abc123
then our commit (dg34mp
) is added back to our local branch
develop origin/develop
40r931 (dg34mp)
zyx911 zyx911
cem32k cem32k
b4d2o1 b4d2o1
abc123 abc123
however because the commit hash (dg34mp
) is based on not only our changes but all the changes before it, the commit hash changes (here to 40r931
). This is also a time that merge conflicts are common.
Don’t Do Anything By Default
Since both creating a merge conflict and rebasing have their own complexities, we don’t want Git to do either by default. This is why we set fast-forward only with git config --global pull.ff
. As long as we are only pulling in new commits, git pull
works fine but if things get out of sync we get the message
fatal: Not possible to fast-forward, aborting.
Then we can explicitly tell Git to rebase our changes
git pull --rebase
or create a merge commit
git pull --no-ff
Instead of explicitly creating a merge commit with git pull --no-ff
, we alternatively we could do a git fetch
followed by git merge origin/<branchname>
.
Global Git Configuration
When we run git config --global pull.ff only
it adds the following
[pull]
ff = only
to our global Git configuration, typically found at ~/.gitconfig
.
Great and very clear post Sal!
Note: formatting of the last example in the section “Rebase our Local Commits” needs to be looked at.
I appreciate your comment, Greg. Thanks to you, I’ve corrected the format on the last example.
Terrific, this is very clear. It’s really important to talk about the motivations for handling different situations in addition to the mechanics of the different options. Thanks a lot!
Thank you for this post. Clear and concise tutorial. Helped me understand this.
Thanks for the great article. I’m using git
2.30.0
on both of my Ubuntu 18.04 MATE systems, and after doinggit config --global pull.ff
,git pull origin main
still gave the “hint” message as you indicate. Adding the “only” resolved this issue, so:and added
to
$HOME/.gitconfig
Thanks Craig,
You’re right, I was missing the
only
in the command.I’ve updated it now. Thanks for the heads up.
git pull --merge
throws an error in git 2.30.1Thanks Esteban,
You’re right. I’ve updated the article to use
instead. Thanks for the heads up.
Thanks for the explanation!
You might want to update the last command on the Global Git Configuration Section:
git config –global pull.ff
>>
git config –global pull.ff only
Thanks Jeffrey,
You’re right, I was missing the
only
in the command.I’ve updated it now. Thanks for the heads up.
Clear explanation! Thanks
Thanks Sal! Clear, concise, and oh so helpful.
Thanks Sal.
Excelente!!
Gracias por la explicacion, todo esta super claro!.
Thanks Sal!
Very useful. 🙂
Great! Thanks Sal.
Now, I’m pretty confident about how to handle git warning. 😀
So clear! Great article
Thank you very much for the great article.
I love this. Clearly and succinctly put!!
Good job
The part
”
or create a merge commit
git pull –ff
”
should be
”
or create a merge commit
git pull –no-rebase
”
or did I miss something ?
Anyway, that’s really a great article, thanks a lot for it.
Thanks for the comment, Enzo. Based on your note, I dug into this a little bit.
It looks like in this situation, all of the following could work.
git pull --ff
git pull --no-ff
git pull --no-rebase
git pull --ff
“when possible resolve the merge as a fast-forward (only update the branch pointer to match the merged branch; do not create a merge commit). When not possible (when the merged-in history is not a descendant of the current history), create a merge commit.”while
git pull --no-ff
“create a merge commit in all cases, even when the merge could instead be resolved as a fast-forward”Since we already know a fast-forward merge isn’t possible, these two items should give the same result, however, based on your message I decided
git merge --no-ff
is clearly so I’ve updated the article to use that.git pull --no-rebase
definitely works as well, but I think in my mind--no-ff
is a little clearer.Thanks for bringing this up.
Thank you Sal for this short and clear description.