Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between these `git fetch` syntaxes?

Tags:

git

syntax

fetch

I've bare-cloned a repository (git clone --bare) and apparently git fetch doesn't update it, but git fetch origin master:master does. I don't understand all the nuances between these syntaxes:

  • git fetch
  • git fetch origin
  • git fetch origin master
  • git fetch origin master:master

origin is my only remote and master is my only branch, and the help says:

When no remote is specified, by default the origin remote will be used

So why these four lines aren't the same?

Edit: the three first commands seems to fetch in a temporary branch called FEATCH_HEAD. But since I'm using a bare clone, I can't use git merge to get the fetched results.

Edit2: From @torek's answer, I made a small test and diff'ed a --bare and a --mirror clone directories. Here is the result:

diff -ru mesa.bare.git/config mesa.mirror.git/config
--- mesa.bare.git/config    2014-10-14 20:01:42.812226509 -0400
+++ mesa.mirror.git/config  2014-10-14 20:00:53.994985222 -0400
@@ -4,3 +4,5 @@
    bare = true
 [remote "origin"]
    url = git://anongit.freedesktop.org/mesa/mesa
+   fetch = +refs/*:refs/*
+   mirror = true
Only in mesa.bare.git/objects/pack: pack-17005b7e1020d291eb86d778a174ecf0d60d92a9.idx
Only in mesa.bare.git/objects/pack: pack-17005b7e1020d291eb86d778a174ecf0d60d92a9.pack
Only in mesa.mirror.git/objects/pack: pack-c08b44b7f290ef0bc9abe3a0b974695c85a69342.idx
Only in mesa.mirror.git/objects/pack: pack-c08b44b7f290ef0bc9abe3a0b974695c85a69342.pack

Thanks!

like image 542
Creak Avatar asked Oct 14 '14 00:10

Creak


1 Answers

Andrew C's comment contains the key here, but I'll expound a bit:

  • git fetch, with no additional arguments, chooses a remote name by looking at the current branch, or uses origin (see the documentation for details). Having chosen a remote, it then proceeds as for the next form.

  • git fetch remote, again with no additional arguments, uses the given remote, and extracts the fetch = lines for that remote to obtain a set of "refspecs". It then proceeds similarly to the last case.

  • git fetch remote refspec uses the given remote and the given refspec (you may give multiple refspecs here) to choose what references to update.

Once git fetch has a remote or URL—given the name of a remote, it extracts the url = line to obtain the URL—it contacts the other git commands on the remote server and asks them for a list of all of the remote repository's references (branches, tags, and other references, all in the refs/* name-spaces, with a special addition for HEAD which is also obtainable but not generally used here—it's there for the initial clone step).

For each reference thus obtained, git fetch sees whether you have asked it to bring that reference over, and if so, what name you asked git to use in your repository.

Again, the names available are obtained from the remote. The names wanted are obtained from your refspecs, and the names they will be given in your repository are also obtained from your refspecs.

A refspec of the form a:b means "take reference a, but call it b locally."

A refspec missing the b part means "take reference a, but put it into the special FETCH_HEAD file." (FETCH_HEAD then becomes like a normal reference, like MERGE_HEAD and ORIG_HEAD and so on, except that it has some extra text written to it meant for the pull script, so it only sometimes works the way you might expect.)

A refspec may contain a wildcard character: refs/heads/* means "take all branches" (branches being, by definition, references that begin with refs/heads/). Normally the fetch = line in your git configuration says refs/heads/*:refs/remotes/origin/*.1 As before, this means to rename the matched branch, with the * on the right expanding to whatever the * on the left of the colon matched. So this brings all branches over, but renames them as origin/master and the like. That's normally want you want for a non---bare repository.

Sometimes that's also what you want for a --bare repository, and sometimes it's not. In particular, sometimes you want a "mirror" repository, which is a bare clone that simply slave-copies some other repository. To change an ordinary bare repository into such a mirror, you simply need to modify the fetch = line: instead of refs/heads/*:refs/remotes/origin/* the line should read fetch = refs/heads/*:refs/heads/*. In fact, you may want to bring over everything (tags and even notes) with fetch = refs/*:refs/*. Whether you actually want this is something only you can decide, of course.

Note that this is common enough that git clone has a flag to set it up automatically: clone with --mirror and you get a bare clone with the modified fetch = line.


1Actually the line reads +refs/heads/*:refs/remotes/origin/*, i.e., there is also a leading + character. This plus-sign sets the "force flag", as if you had used git fetch --force, for that particular reference-update. This is not particularly relevant to the spelling issues here, but I'll note that usually you want a forced update for remote branches like the one listed here, and also for pure mirror repositories.

If you're mirroring tags, you probably want those to do forced-update. Ideally, of course, tags never change (nor are deleted) so in an ideal world this would not matter, but in the real world they do sometimes change, or get deleted.

To handle reference deletion, you must tell git fetch to --prune (or, similarly, supply --prune to git remote update). There is no syntax in refspecs for automatic pruning (although it would be reasonable, I have not seen any proposal for it).

like image 70
torek Avatar answered Oct 26 '22 04:10

torek