11

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!

4
  • 3
    Have you read kernel.org/pub/software/scm/git/docs/git-fetch.html ? If so, please edit the question to clarify what wasn't clear after reading it (if anything). Otherwise, please answer the question yourself (and post the answer here)! Commented Oct 14, 2014 at 0:22
  • 2
    Of course I've read it, otherwise how could I quote a sentence from git help fetch? Commented Oct 14, 2014 at 0:39
  • 2
    normally you would want to have a line like "fetch = refs/heads/*:ref/heads/*" in the origin section of your config, then it will make git fetch behave as you expect Commented Oct 14, 2014 at 2:56
  • Argh, I should have noticed that you quoted the help. Sorry for the noise. Commented Oct 14, 2014 at 17:21

1 Answer 1

17

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).

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for this complete answer! See the "Edit2".
Apparently adding fetch = refs/heads/*:refs/heads/* does not work as I expect in my case. Because I also used a --depth 6000 during the clone, adding this line to my config file makes git fetch to try to get the entire repository. But if in put fetch = master:master, then everything works fine!
I have never experimented with shallow clones, but presumably when you bring over refs/heads/* you're getting a branch that causes git to deepen the repo; but with master:master you bring over only master (git automatically qualifies this to refs/heads/master, on both sides of the refspec) and it stops when it hits a commit that you have, so it will only deepen the clone if you merge a commit that is not already in your 600.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.