Page MenuHomePhabricator

Respect "Fetch Refs" when cloning a new repository
Closed, ResolvedPublic

Description

See PHI1533, where an install reports "Fetch Refs" not being respected when creating a new repository via observation from GitHub.

When we create a new repository for the first time, we currently git clone it without passing a ref list. On the face of it, this looks like it's complex, since git clone does not take a ref list and there isn't an obvious way to clone a specific list of refs.

However, I think we don't actually need to clone. We only fetch by URI and we don't care about refs/remotes or origin or anything else that clone sets up: we should be able to just init an empty repository, then fall through to the normal fetch logic to actually bring refs into the working copy.

Event Timeline

epriestley created this task.

I stopped the daemons, then created a new repository observed from GitHub with "Fetch Refs" set to "refs/heads/master". I ran bin/repository pull X --trace to update it, and saw it clone:

...
>>> [18] (+24) <exec> $ git clone --bare -- '********' '/Users/epriestley/dev/core/repo/local/18'
...

...and build a working copy full of refs other than refs/heads/master:

$ git for-each-ref | wc -l
      89

I pulled it again with bin/repository pull X --trace --verbose and it didn't do anything. This is expected, since we compare local and remote refs before deciding to pull, and they're now identical:

>>> [26] (+32) <exec> $ git ls-remote '********' 'refs/heads/master'
<<< [26] (+393) <exec> 361,426 us
>>> [27] (+394) <exec> $ git ls-remote '********' 'refs/heads/master'
<<< [27] (+413) <exec> 19,305 us
Skipping fetch because local and remote refs are already identical.

One change that would probably improve things here is to exclude the "fetch only" ref list from the local ls-remote command. Currently, we won't delete all the extra local refs until refs/heads/master is updated in the remote, even though the configuration tells us we should. I'll come back to this.

For now, I short circuited the "don't do anything" code and ran bin/repository pull X --trace --verbose again.

>>> [28] (+324) <exec> $ git fetch --prune --update-head-ok -- '********' '+refs/heads/master:refs/heads/master'
<<< [28] (+608) <exec> 283,749 us

This is the correct fetch. However, the working copy still has extra refs:

$ git for-each-ref | wc -l
      89

A careful reading of git help fetch suggests the --prune flag is no longer really effective if we're passing an explicit ref list. When we pass an explicit ref list, we should prune the repository ourselves (and maybe in all cases if this is simply easier). We already have the local and remote ref lists so this is not conceptually difficult.

These issues are somewhat entangled with each other and more difficult to fix after resolving the clone issue, so I'll deal with them first.