如何处理多个遥控器和分支机构

How to deal with multiple remotes and branches

我想要多个远程服务器,它们应该为所有分支相互镜像。

开始这次冒险,我发现: pull/push from multiple remote locations

这告诉我:

$ git remote set-url origin --push --add <a remote>
$ git remote set-url origin --push --add <another remote>

如果我做一个

$ git pull --all
Fetching origin
Fetching willi

看起来不错!

但是推送没有用:

$ git push --all
To ssh://gitmini@localhost/home/gitmini/gitrepos/repo1
f974ce2..c146f0a  master -> master

没有推送到我的第二个遥控器!为什么?

所以如果我尝试不同的方法,例如:

$ git remote add mirroring ssh://gitmini@localhost/home/gitmini/gitrepos/repo2
$ git remote set-url mirroring --push --add ssh://gitmini@localhost/home/gitmini/gitrepos/repo1
$ git remote -vv
mirroring   ssh://gitmini@localhost/home/gitmini/gitrepos/repo2 (fetch)
mirroring   ssh://gitmini@localhost/home/gitmini/gitrepos/repo1 (push)

这真是出乎我的意料!

还有一个额外的选项 --mirror=fetch|pull 但这也会导致错误配置的结果。

正如某些评论中提到的,如果命令重复,则可以添加 url。但我永远不能添加超过一个回购协议来获取。作为例子,我可以得到这个结果,在我看来充其量是错误的:

 $ git remote -vv
 mirroring  ssh://gitmini@localhost/home/gitmini/gitrepos/repo1 (fetch)
 mirroring  ssh://gitmini@localhost/home/gitmini/gitrepos/repo2 (push)
 mirroring  ssh://gitmini@localhost/home/gitmini/gitrepos/repo1 (push)
 mirroring  ssh://gitmini@localhost/home/gitmini/gitrepos/repo2 (push)

作为下一次尝试我 运行:

 $ git config -e

并添加了以下部分:

[remote "mirroring"]
    url = ssh://gitmini@localhost/home/gitmini/gitrepos/repo1
    fetch = +refs/heads/*:refs/remotes/mirroring/*
    pushurl = ssh://gitmini@localhost/home/gitmini/gitrepos/repo1
    url = ssh://gitmini@localhost/home/gitmini/gitrepos/repo2
    fetch = +refs/heads/*:refs/remotes/mirroring/*
    pushurl = ssh://gitmini@localhost/home/gitmini/gitrepos/repo2     

但是

$ git remote -vv
mirroring   ssh://gitmini@localhost/home/gitmini/gitrepos/repo1 (fetch)
mirroring   ssh://gitmini@localhost/home/gitmini/gitrepos/repo1 (push)
mirroring   ssh://gitmini@localhost/home/gitmini/gitrepos/repo2 (push)

获取 repo2 的行被忽略了!

事实上,我无法设置配置。我的任务很简单:同步两个遥控器。

编辑:对 torek 给定答案的一些评论:

看来可以设置:

[remote "mirroring"]
    url = ssh://gitmini@localhost/home/gitmini/gitrepos/repo1
    fetch = +refs/heads/*:refs/remotes/mirroring/*
    pushurl = ssh://gitmini@localhost/home/gitmini/gitrepos/repo1
    url = ssh://gitmini@localhost/home/gitmini/gitrepos/repo2
    fetch = +refs/heads/*:refs/remotes/mirroring/*
    pushurl = ssh://gitmini@localhost/home/gitmini/gitrepos/repo2

有了这个配置

$ git push mirroring 
...
To ssh://gitmini@localhost/home/gitmini/gitrepos/repo1
...
To ssh://gitmini@localhost/home/gitmini/gitrepos/repo2

结果推送到两个遥控器。

我不知道这个配置是否有效。

托雷克写道:

If you configure more than one, only one of them works, all the others are ignored.

好像不是真的。在我给定的配置中,所有遥控器都将按照我上面的示例通过推拉访问。

事实上,配置为 [remotes] 的组看起来对我的用例非常有用!

远程和获取

Git 可以有很多 "remote",所以当你做 git config -e 时,你会看到这样的东西:

[remote "r1"]
    url = ...
    fetch = +refs/heads/*:refs/remotes/r1/*
[remote "r2"]
    url = ...
    fetch = +refs/heads/*:refs/remotes/r2/*

然而,任何一个遥控器只能有一个 url(最多一个pushurl)。如果您配置了多个,则只有一个有效,其他的都将被忽略。

(有点奇怪,任何一个遥控器都可以有许多 fetch 条目,并且所有条目都被遵守。此外,您可以在此处设置 push = 以设置默认推送 refspec,尽管我有我自己从来没有用过这个。)

当你 运行 git fetch 你可以命名 一个特定的 远程:

$ git fetch r1
[fetches from r1]
$ git fetch r2
[fetches from r2]

或名称 多个 遥控器使用 --multiple:

$ git fetch --multiple r1 r2
[fetches from r1 and r2]

或来自所有 遥控器:

$ git fetch --all
[fetches from r1, r2, and any other defined remote]

或来自 "groups",稍后我将对其进行定义。 --multiple 标志使 git fetch 将其所有参数视为远程或组名称。否则,远程名称之后的每个参数都被视为 refspec(例如,git fetch r1 r2,没有 --all,意味着从远程 r1 获取 ref r2)。

A "group" 是定义的东西,例如:

[remotes]
    somegroup = r1 r2

您在左侧列出了组,在右侧列出了它表示的一组遥控器。请注意,这是 remotes,复数形式,您可以使用 git config remotes.somegroup r1 r2 设置它,尽管当事情变得如此复杂时,我更喜欢只使用 git config -e 和我的编辑器,这样我就可以一起看到所有的东西了。

使用此集合,您可以 运行 git fetch somegroup,它将从 r1r2.

中获取

您还可以 运行 git remote update,默认情况下从您的所有遥控器中获取,但可以配置(通过 remotes.groups 项目以及 remotes.default)获取来自特定组或单个远程。

推送

使用git push时,您只能推送到一个远程。要推送到多个遥控器,您必须 运行 几个 git pushes。

git push --all 并不意味着推送到所有遥控器,而是推送所有参考,就好像您将 refs/*:refs/* 作为参考规范一样。)

Refspecs,或者获取或推送的内容

fetchpush 都使用 "refspecs" 来确定他们的工作方式。

完整的 refspec 看起来像您在 remotes 下的 fetch = 行中看到的那些,例如:

+refs/heads/*:refs/remotes/r1/*

有一个可选的前导 + 符号,它设置强制标志(您可以使用 --force 设置相同的标志),然后是两个引用(例如 refs/heads/masterrefs/tags/v1.1) 以冒号 : 字符分隔。星号 * 可能会出现,它的工作方式有点像 shell 通配符,只是当它位于 右侧 一侧时,它表示 "use whatever the one on the left side matched"。 (它也不能出现在任意位置;通常你希望它紧跟在 / 之后,如 refs/heads/*refs/*。)

提取和推送命令不太对称。当做fetch时,左边的名称或模式是为遥控器的参考,右边的名称是必要的1,因为它告诉git如何重塑本地存储库的名称。这就是为什么远程 origin 的获取行在右边读取 refs/remotes/origin/* 的原因,例如:我们想重新塑造他们所有的 refs/heads/* 引用——他们所有的分支——成为我们的refs/remotes/ 中的远程跟踪分支,并放置在 origin/.

git push 但是, 左侧的名称或模式 供您自己参考 - 您的分支、标签、注释或其他任何内容 - 以及名称右边是遥控器。如果省略右侧名称,通常表示 "use the same name on the remote"。因此 refs/heads/master(没有加号也没有冒号)表示 "push my branch master to the remote's master".

(我认为——仅代表我的意见,并非技术要求——最好在定义推送规范的配置文件中使用冒号并在左右两边都明确,即使你可以省略右侧。)

镜像

"Mirroring" 在 git 中有特定的含义,尽管事实证明它有两个不同的含义(fetch 镜像 vs push 镜像)。具体意思就是"set the fetch or pull refspec to copy all references"。例如:

$ git remote add --mirror=fetch foo ssh://foo.foo/foo/foo.git

导致 git 放置:

[remote "foo"]
    url = ssh://foo.foo/foo/foo.git
    fetch = +refs/*:refs/*

进入配置文件。 (奇怪的是,在这个配置下不会设置 prune = true。它可能应该设置,但是你可以 运行 git remote update foo --prunegit fetch -p foo 来获得相同的效果。)或者:

$ git remote add --mirror=push foo ssh://foo.foo/foo/foo.git

配置:

[remote "foo"]
    url = ssh://foo.foo/foo/foo.git
    mirror = true

(请参阅 git push 文档)。

请注意,为多个远程设备设置 mirror=fetch 意义不大。例如,假设您为遥控器 r1r2 设置了此项。当您从远程 r1 获取作为获取镜像时,您会清除所有分支和标签,并用来自 r1 的分支和标签替换它们。然后,你从 r2 获取作为获取镜像,清除从 r1 复制的所有分支和标签,用来自 r2 的分支和标签替换它们。 r1 fetch 在这种情况下有什么好处?


1如果您省略右侧,fetch 将无法更新您的任何参考资料。这将是完全无用的,除了有一个历史模式,仍然被 pull 脚本使用,其中获取的引用存放在 FETCH_HEAD 文件中。