If using git fetch
updates all the remote tracking branches under refs/remotes/<remote>
with the SHA-1 of the latest commit for their upstream branches, what is the point of storing the SHA-1 of the latest fetched commit for the currently checked out branch in FETCH_HEAD
if the remote tracking branches already have this?
CodePudding user response:
What other ref names get updated when you fetch is a matter of fetch options. The factory default config tells it to update refs/remotes/origin/*
if you don't supply explicit options, but you can, you can have it fetch anything you want and update whatever refs you say. But it always logs what it did in FETCH_HEAD
. Only fetch updates that log.
CodePudding user response:
Putting the "backwards" in "backwards compatible"
I've referred to this joke before, but it applies here as well:
If using
git fetch
updates ... remote tracking branches ... [then] what is the point of storing the SHA-1 of the latest fetched commit for the currently checked out branch inFETCH_HEAD
...?
Somewhere between "very little" and "none" at this point, but Git has always done this, including before the very invention of "remote-tracking [branch] names" in the first place. To maintain backwards compatibility, Git must keep on doing this.
As recently as Git version 2.5, git pull
was still a shell script, and that shell script would read .git/FETCH_HEAD
to extract the data to pass to git merge
. Git 2.6 (released in Sep 2015) had the code in C, by which point that wouldn't necessarily be required, but it would take some historical sleuthing to find out at what point it actually stopped happening (often some of the C rewrites initially just emulate the scripts to avoid making too many wholesale, untested code changes).
Finally, the if clause above is still a bit overpowered. It is true that if and when git fetch
updates remote-tracking names, it's mostly redundant to keep this data in .git/FETCH_HEAD
as well, and now that git pull
is C code that has no logical reason to fish information out of a file that it can just pass around in memory, there's no git pull
-oriented logic either. But git fetch
doesn't update a remote-tracking name in several special cases:
- when you use a raw URL, there is no remote involved, so there are no remote-tracking names; or
- if the default fetch refspecs (when using a remote R, the result of
git config --get-all remote.R.fetch
) do not have a standard-style map in them,1 fetch won't update any remote-tracking names opportunistically.
If Git would forbid these two cases, that would eliminate all reasons except for "backwards compatible", but the "backwards compatible" clause will probably persist for another decade beyond that point.
1By "standard-style map" I mean something that translates refs/heads/foo
into refs/remotes/whatever
. Single-branch clones have refs/heads/somebranch:refs/remotes/origin/somebranch
for instance, while normal clones have refs/heads/*:refs/remotes/origin/*
, for remote origin
. The remote name and the mapped name don't have to match, in these default fetch refspecs, which is weird and smells like someone overgeneralized back in the day, but now there's "backwards compatibility"...