如何获取所有分支 - 包括 remotes 远程分支?

How to fetch all branches - including the remotes remote branches?

这里有几个 upvoted 问题 "how to fetch all the remote branches",简短的(upvoted)答案似乎是 git clonegit fetch 应该默认这样做,并且可以看到运行 git branch -r 的远程分支。不过,我还是有点缺。

我克隆了一个 "upstream" 中央存储库,然后我再次克隆了这个克隆。比如说,我们在 github 上有存储库 A,B 是 A 的克隆,C 是 B 的克隆。问题是 C 只包含 B 的本地分支。我想 clone/pull A - > B -> C,C 拥有 A 的所有分支。

我部分期待评论中出现 "why would you want to do that?"。我认为鉴于 git 的分布式特性,它应该是可能的。然而,考虑到带宽很昂贵,存储库很大,B 和 C 位于同一个文件系统或 LAN 上,并且大部分工作要在分支机构中完成——在这种情况下,克隆 A->B 和A->C,既不是从 A->B 也不是从 A->C 拉取,因为这意味着两倍的网络流量。也可能有其他原因,即防火墙无法从 A->C 直接拉取。

您似乎(还)不能为远程配置多个默认引用规范,但您可以在命令行上指定它们:

git fetch origin '+refs/heads/*:refs/remotes/origin/*' \
                 '+refs/remotes/*:refs/remotes/upstream/*'

这会将 origin 的远程跟踪分支映射到您的远程跟踪分支到 "upstream" 以及执行普通的获取。只要有这些分支就足够了,你不必定义遥控器,除非你有一些理由直接使用它。

为了避免使用不熟悉的命令行,您可以使用源的 url 和间接源跟踪参考规范定义 upstream 遥控器:

git remote add upstream `git config --get remote.origin.url`
git config remote.upstream.fetch '+refs/remotes/*:refs/remotes/upstream/*'

如果您还想用一个命令执行两个提取,请添加

git config remotes.origin-all 'origin upstream'

然后是

git fetch origin-all

在 v2.2.2 上测试:

~/sandbox/38$ ls -a
.  ..
~/sandbox/38$ git clone ~/src/git .
Cloning into '.'...
done.
~/sandbox/38$ git --version
git version 2.2.2
~/sandbox/38$ git remote add upstream `git config --get remote.origin.url`
~/sandbox/38$ git config remote.upstream.fetch '+refs/remotes/*:refs/remotes/upstream/*'
~/sandbox/38$ git config remotes.origin-all 'origin upstream'
~/sandbox/38$ git fetch origin-all
Fetching origin
Fetching upstream
From /home/jthill/src/git
 * [new ref]         origin/HEAD -> upstream/origin/HEAD
 * [new ref]         origin/maint -> upstream/origin/maint
 * [new ref]         origin/master -> upstream/origin/master
 * [new ref]         origin/next -> upstream/origin/next
 * [new ref]         origin/pu  -> upstream/origin/pu
 * [new ref]         origin/todo -> upstream/origin/todo
~/sandbox/38$ git checkout -b lala -t upstream/origin/master
Previous HEAD position was fdf96a2... Git 2.2.2
Branch lala set up to track remote ref refs/remotes/origin/master.
Switched to a new branch 'lala'
~/sandbox/38$ 

一直想要类似的 - 保持工作或内部驱动器存储库 B 和同一远程存储库 A 的 USB 备份驱动器存储库 C 同步,而无需分别从 A 重新获取网络。只有在开始写下我自己的问题后,这个问题的 link 才出现了: How can I convert all the remote branches in a local git repo into local tracking branches 这对于发现您的第二种解决方法更可取的人可能有用;和你一样,我也不喜欢那样,乱七八糟。

阅读您的第一个解决方法让我思考并阅读 git 手册页 - git clone --mirror 非常接近我们/我想要的,因为正如手册页所说:
Set up a mirror of the source repository. This implies --bare. Compared to --bare, --mirror not only maps local branches of the source to local branches of the target, it maps all refs (including remote-tracking branches, notes etc.) and sets up a refspec configuration such that all these refs are overwritten by a git remote update in the target repository.

所以 git clone --mirror 确实在本地克隆(某处)上设置了我们需要的所有分支等,以便有效地制作其他本地克隆/回购(例如 B 和 C)。 "mirror" 在 not bare repo 中的问题是它很乱;所以看起来我们确实想要至少一个裸仓库 "D" 用于我们的 "primary mirror",但是使用 git clone --mirror(而不是 --bare)。

最后一块拼图是让我们的 "working clone" B 尽可能多地共享此 "primary" 存储库 D 的 objects/ 存储,因为它们在相同的文件系统"mount space"(如果是的话)意味着你不能跨挂载点来工作。

从头开始创建 D(请参阅下面的 links 以转换现有存储库):
git clone --mirror git://blah.com/A.git D.git

从 D 创建 B:
git clone ../D.git/ B.git
并且 git 会自动做正确的事情 - 暗示 git clone --local 如果 D 和 B 没有越过安装屏障。

不幸的是,据我所知,这仍然需要两个步骤来更新 "local working clone" B - D 中的 git fetch --all,然后是 B 中的 git fetch --allgit pull。如果适合您选择的工作流程,您当然可以编写一个 git-fetch 包装器脚本来将两个步骤合二为一。

在我的情况下,C 也是一个 --mirror 存储库(至少从现在开始大约 5 分钟后 - 它只是远程存储库的备份,换句话说,它是第二个 "primary mirror").

Repo C 也可以配置为从 B(不仅是 D)复制对象。首先创建 C,然后添加适当的遥控器,例如类似于:
从 D 创建 C,作为镜像:
git clone --mirror blah://my.work.pc/my/mirrors/D.git C.git
或普通克隆:
git clone blah://my.work.pc/my/mirrors/D.git C.git
其次是:
git remote add B blah://my.work.pc/my/work/B.git
或者在您已经拥有 C 的特定情况下,只需添加 D 作为新遥控器。

然后 C 上的 git fetch --all 应该从 B 和 D 获取所有更新。

您可能需要 How do I make existing non-bare repository bare? 中的一些魔法以及...

另一种从 B 创建 D 的方法,使用类似的东西:
git clone --bare B.git D.git
结合 How to change a git repository cloned with --bare to match one cloned with --mirror? 的一些魔法 - 还适当地更新 D 的遥控器,当然还有 B.

请注意,git clone --local 创建的硬 link 会被 git gc 破坏,这会不时自动发生。您可能更喜欢 git clone -s ...,它使用符号 git link,因此知道如何跨本地文件系统 "mount barriers",或 git-new-workdir 命令(在 git repo 的 contrib/ dir - 这只适用于非裸回购),它们都在这里提到:git as an alternative to unison. See also Single working branch with Git and the new Git 2.5 (Q2 2015) git checkout --to=path 选项,用于对 git-new-workdir 及其最终 git 官方替换进行一些很好的讨论。

为避免在使用 git clone -s 时出现意外情况,请务必阅读其工作原理,例如在 man git-clone 中,您可能希望按如下方式配置 D(并阅读 man git-prune):
git config gc.pruneExpire never

关于这两种方式的比较,请参阅 Brandon Casey 在 this discussion on git-new-workdir 中的评论,即:

"如果您希望 _multiple_different_ 个分支从 相同 存储库,并在所有存储库中进行开发,然后 git-new-workdir 是正确的选择。"

"如果您希望 _same_branch_ 在多个工作中检出 目录,然后用 -s 克隆就是你想要的。在这种情况下 我假设开发将在原始回购中进行,并且 克隆将拉动更新。"

这表明 clone -s 可能只对 "testing" 工作目录有用,对开发工作目录没有用。

有两个 "local master" 回购,假设 D 和 C 配置为彼此的远程和回购 A,为了加分,编辑 git 配置文件并剪切和粘贴本地回购条目(例如 D 或 C)将其移动到远程(回购 A)条目之上 - 这样 git fetch --all 通过首先在本地(如果可能)获取新的更改和分支来做正确的事情,而不必先手动获取本地存储库,然后是 A。现在这是我为所有克隆的 public 存储库设置的,它很有用!

祝你好运。