Search code examples
git

Fetching all refs in an existing Git repo


I can clone a repo with all of its refs by using the following:

git clone --mirror ssh://some.remote.server/some-repo.git cloned-repo/.git
cd cloned-repo
git config --bool core.bare false
git checkout some-branch

Is it possible to fetch all refs from a remote, in a repo that has already been cloned?

Note that "all refs" means refs/*, which includes but isn't limited to branches and tags. For example, some remotes use refs/contrib/$merge_request_id/versions/$version or refs/pull/$pull_request_id/head to store merge requests.


Solution

  • Change the refspec in the remote.origin.fetch config option.

    Keep in mind though, that non-bare repositories are configured transform the fetched refs so that a remote-fetched branch intentionally does not use the same ref name as a local branch, e.g. what was refs/heads/master is instead fetched into refs/remotes/origin/master, so that a fetch will not update your working tree without a deliberate merge step (thus pull being fetch+merge).

    If you change your fetch refspec to +refs/*:refs/*, then this behavior will change and every fetch will directly overwrite any local branches you may have had (and a fetch --prune will outright delete them). Removing the + would make it somewhat safer in that a fetch won't roll back diverging branches, but it will still leave you with no way to fetch-and-merge anything.

    So instead of doing that, you should add a second refspec for pulls or whichever refs you specifically want – e.g. +refs/pull/*/head:refs/remotes/pull/* which will result in what Git thinks are remote-tracking branches like "pull/123" in the same style as "origin/main". Similarly, fetch refs/contrib/* to refs/mr/* or to refs/remotes/mr/*, whichever results in a more convenient result.