Git 2.34 has changed how you configure fast-forward only pulls and rebasing

January 31, 2022

I'll start with the conclusion and give you the story afterward. Before Git 2.34, if you were tracking upstream repositories and perhaps carrying local changes on top, it was sensible to configure 'pull.ff only' globally (in your ~/.gitconfig) to stop 'git pull' from warning you and then set 'pull.rebase true' in repositories that you had local changes in. As of Git 2.34, this causes a plain 'git pull' to abort in any repository that has local changes. The simplest fix is to also set 'git config pull.ff true' in each repository where you've set pull.rebase. This may become unnecessary in some future Git version, because the Git 2.34 behavior may be a bug.

When Git started out, 'git pull' defaulted to trying to fast-forward what had been fetched and then automatically doing a merge if it wasn't possible (basically to 'git pull -ff', although I'm not sure they're exactly the same). In theory this was what you wanted as a developer; in practice it was easy to get surprise merges that you didn't want in various situations. Soon people began suggesting that you use 'git pull --ff-only' or better yet, configure 'pull.ff only' through git config. At a certain point, the Git developers themselves decided this wasn't the greatest default and began having 'git pull' warn about it and ask you to do something about it (cf).

I started out setting 'pull.ff only' on some repositories as part of my multi-repo workflow or tracking some upstreams. When Git started warning about plain 'git pull' with nothing configured, I upgraded this to a global setting of 'pull.ff only' in my ~/.gitconfig, since I didn't have any repositories where I want to automatically pull and merge. Almost always I'm either exactly tracking an upstream (where I'm going to reset to origin/main if the upstream does something weird) or I'm carrying local changes that I want to automatically rebase on top of the new upstream. For a long time this worked fine, and then I updated to the Fedora 34 version of Git 2.34.1 and suddenly my automatic rebasing on pulls broke (you can read an example in my Fedora bug report).

As of Git 2.34, two things have changed. First, the default behavior of 'git pull' is now to abort if it can't fast-forward the upstream into your local branch (ie, 'git pull --ff-only'); basically, the previous warning has become an error. Second, the configuration setting of 'pull.ff only' now takes priority over 'pull.rebase true' (although not over an explicit --rebase on the command line). If you have both in a repository with things to rebase, you effectively wind up running 'git pull --ff-only', which fails because you have additional local changes that Git thinks would have to be merged. The behavior of 'pull.ff only' here may be an accidental bug and is certainly not historical behavior, but we have to deal with the Git release we get, not the one we'd like.

(This isn't explicitly documented in the release notes (also), although they do talk about a general overhaul of this area. The 2.34 manual pages are not the clearest about the behavior and they don't explicitly say that 'pull.ff only' takes priority, although the git pull manpage somewhat implies that --ff-only conflicts with --rebase.)

As far as I can see, I have two options to get the behavior I want. First, I can continue to set 'pull.ff only' globally, for safety, and then set 'pull.ff true' in any repository where I also set 'pull.rebase true'. I'm not sure that this is completely harmless in old versions of Git, though. The other option, if I'm only going to be using Git 2.34 and later and I trust Git not to make any surprising changes here, is to not set pull.ff globally and set only 'pull.rebase true' in my 'I have changes on top of upstream' repositories that I want to automatically rebase on pulls.

(I've tested both approaches and they appear to work. Carefully reading the Git source code confirms that 'git pull --ff-only' is basically the default, although it's not actually implemented quite that way. See builtin/pull.c for the gory details.)

PS: A nice long history of the changes in this area in 'git pull' from 2020 or so onward is here. There have been a number of steps. The history has already been updated a bit for Git 2.35 and may be updated more in the future, so check in on it for the latest news.

Written on 31 January 2022.
« A thesis: most websites are implicitly designed with a short lifetime
The likely long-term result of good on-host (host-based) firewalls »

Page tools: View Source, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Mon Jan 31 20:50:22 2022
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.