有没有办法将另一个存储库的主分支仅镜像到非主分支?

bre*_*inh 4 git clone github mirroring

我有两个存储库 - 存储库 1 和存储库 2,存储库 1 有两个分支 - 主分支和分支 1。我希望能够将存储库 2 的主分支仅镜像到分支 1,并保留存储库 1 的主分支不变。有没有办法做到这一点?还有一种方法可以将特定分支镜像到另一个存储库的主分支(即将存储库 1 的分支 1 镜像到存储库 2)吗?

我尝试遵循典型的 git clone --mirror URL ,然后 git push --mirror URL 到 repo 1 。但是,这似乎只将镜像推送到 repo 1 的 master 分支。我也见过一些人参考可能会执行 git Push Branch1 --mirror URL,但为此,我得到一个 fatal: --mirror can't be merged with refspecs 。

在与此类似的其他一些问题中,解决方案不一定是镜像到特定分支或从特定分支进行镜像。不幸的是,我面临着这个特定的用例,并且无法与主服务器进行镜像,但需要从特定的分支进行镜像。

tor*_*rek 7

你可以在这里做任何你想做的事。真正的技巧是首先弄清楚你想要什么。之后,这只是refspecs的问题,我稍后会谈到。不过,鉴于您上面所说的,您不需要任何选项--mirror

\n\n

获取、推送、命名和提交哈希值

\n\n

首先,请记住 Git 实际上就是关于提交的。提交由其哈希 ID 唯一标识。这些哈希 ID 是通用的:各地的每个 Git 都同意某个特定提交具有哈希 ID。任何地方的其他 Git 都不能对不同的提交使用相同的哈希 ID,并且任何地方的每个 Git(如果有该提交)都必须使用哈希 ID。

\n\n

换句话说,哈希 ID 具有真实、具体的含义。重要的是哈希 ID。它们是 Git 到 Git 交换的货币。

\n\n

相比之下,分支名称特定于一个或另一个 Git 存储库。master可以与 master完全无关,即使我们的两个 Git 存储库要交换提交。我让我的 Git 调用你的 Git;你的 Git 告诉我你的 masteris commit a123456...;我从你那里得到了那个提交,它仍然是a123456...,但是我的 Git 使用的名称完全是别的东西,比如origin/master或者dinh/master或者yourbranch或者任何想称呼它的名字。如果你说a123456...,我说,a123456...我们就可以看出我们都有这样的承诺。你的名字(如果有的话)和我的名字不需要一致,而git fetch他们通常不会一致。

\n\n

git push命令不太对称,但它的工作原理非常相似:我让我的 Git 调用你的 Git,通过原始哈希 ID 为你提供一些提交(并且你的 Git 和我的 Git 像往常一样在这些方面达成一致)。然后我会要求(常规推送)或命令(强制推送)你的Git设置一个或多个名称来标识一些特定的提交,我通过它们通用的、商定的哈希 ID 来命名这些提交。

\n\n

当我运行到你的存储库时,我的 Git 可以看到你的名字git fetch。默认情况下git fetch,我让我的 Git 复制你的名字,并重命名它们:这就是为什么当你让origin/master你的 Git 从你正在调用的另一个 Git 复制内容后,你通常会在你的 Git 中添加一个origin。同时,当我git push 我的存储库运行时,我让我的 Git 告诉你的 Git 一些要使用的名称。为了方便起见,当git push. 这就是为什么你通常将你的推master到原点master:这里没有发生默认的重命名。

\n\n

哈希 ID 与引用

\n\n

如果哈希 ID 是 Git 查找提交\xe2\x80\x94 的方式,并且它们是\xe2\x80\x94,那么到底为什么我们有分支名称呢?好吧,考虑一下实际 Git 存储库中的一些实际哈希 ID:

\n\n
83232e38648b51abbcbdb56c94632b6906cc85a6\naa8c8d914e4ae709e4fd025f359594f62653d9e5\n061ed420ec2dc97e2a922a6f02992869089cefb3\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是三个提交,按提交顺序排列(最新的在前)。你能记住这些数字吗?(也许\xe2\x80\x94,但我不想这样。)Git需要记住所有这些,但 Git 的设置是为了让提交83232e...本身记住数字aa8c8d...,并aa8c8d...记住数字061ed4...。所以我们只需要记住那个又大又丑的83232e...东西就可以了。我们可以把它写下来;但如果让 Git帮我们写下来可能会更好。

\n\n

我们可以使用分支名称、标签名称或任何其他此类名称来完成此操作。该名称将保留83232e...,现在我们只需要记住该名称即可。这些名称统称为 Git 所说的refsreferences。分支名称以 开头refs/heads/,标记名称以 开头refs/tags/,远程跟踪名称refs/remotes/以远程名称开头并继续(例如,refs/remotes/origin/)。在最后一个斜杠之后,您就有了分支、标签或远程跟踪名称本身。Git 倾向于隐藏前缀,但它偶尔会出现,特别是在使用完整引用时。

\n\n

因此,我们有引用 \xe2\x80\x94 分支名称、标记名称、远程跟踪名称等 \xe2\x80\x94 来记住一个哈希 ID。Git 从该哈希 ID 查找剩余的哈希 ID。分支名称的唯一特别之处在于,我们可以使用git checkout“on”分支 \xe2\x80\x94git status来表示on branch master\xe2\x80\x94,然后,一旦我们这样做了,我们就可以进行新的提交。我们所做的新提交将会83232e...为我们记住,并且 Git 会自动将新提交的哈希 ID\xe2\x80\x94 填充到其中,这将是新的且唯一的,与以往的每个其他提交不同\xe2\x80\x94 master,现在自动记住最新的提交。

\n\n

因此,分支名称只是自动指定分支中的最后一次提交。从该提交中,Git 通过其哈希 ID\xe2\x80\x94(如在名称 \xe2\x80\x94 下找到的那样)查找到前一个哈希 ID,从而使 Git 到达该提交,从而获取另一个先前的哈希 ID,依此类推。结果是历史记录:在指定提示处结束的一系列提交,其哈希 ID 存储在分支名称中。

\n\n

git fetchgit push交换提交时,他们通过哈希 ID 来执行此操作,但他们也会看到(有时会复制)名称。这就是refspec发挥作用的地方。

\n\n

参考规格

\n\n

refspec 本质上是一对用冒号分隔的引用:,例如:

\n\n
refs/heads/master:refs/remotes/origin/master\n
Run Code Online (Sandbox Code Playgroud)\n\n

您可以在任一侧放置任何参考。这里,我们master在左侧\xe2\x80\x94refs/heads/将其标记为分支名称\xe2\x80\x94,origin/master在右侧将其标记为远程跟踪名称。左边的名称是,右边的名称是目的地。这是使用的一种 refspec git fetch,因为源引用是分支名称,目标引用是远程跟踪名称。这告诉你的 Git: 使用他们的master分支,获取他们拥有但我没有的提交,并记住他们master所表示的提交,使用我的origin/master.

\n\n

有了git push,你就可以写git push origin master:master. 你的 Git 会将其翻译为refs/heads/master:refs/heads/master\xe2\x80\x94,并在适当的位置扩展全名 \xe2\x80\x94,从而获取你没有的任何提交,将它们发送过来,然后要求他们将分支 master设置为该链中的最后一次提交。或者你可以git push origin master:bren告诉你的 Git 获取你的master\xe2\x80\x94 左侧的源 \xe2\x80\x94 并发送你拥有但他们没有的任何提交,然后要求他们设置他们的分支bren

\n\n

您可以\xe2\x80\x94并且至少在脚本中,可能应该\xe2\x80\x94拼写出全名,并refs/heads/在前面填写。这可以确保,如果由于某种原因有人意外创建了名为 的标签master,则不会出现歧义:您指的是分支 refs/heads/master,而不是标签 refs/tags/master

\n\n

任何引用规范前面都可以有一个加号+字符。这样做告诉 Git:即使您通常会反对,也要执行此操作。 也就是说,它--force为这一特定参考更新设置标志。例如,如果更改分支名称会丢失一些提交,Git 通常会反对。也就是说,如果分支名称当前显示“commit a123456”,即“从这里,返回一步到 fedcba9”,并且您要求他们将名称设置为“fedcba9”,他们将无法找到“ a123456" 不再\xe2\x80\x94提交中的定向链接仅向后指向较旧的提交,从不转发到较新的提交。(有关此内容的更多信息,请参阅像 (a) Git 一样思考。)

\n\n

Refspec 匹配和--mirror选项

\n\n

git fetch通常可以使用特殊*元字符来匹配所有分支:

\n\n
+refs/heads/*:refs/remotes/origin/*\n
Run Code Online (Sandbox Code Playgroud)\n\n

这个面向获取的参考规范说:获取所有分支名称\xe2\x80\x94 refs/heads/\xe2\x80\x94下的所有内容,并获取所有提交等,然后强制更新我的所有远程跟踪名称以refs/remotes/origin/匹配。 这是git fetch用于与名为 的远程通信的正常引用规范origin。这样,您就可以获得他们的所有分支,作为您的远程跟踪名称。

\n\n

但是,您可以将此refspec 更改为git clone --mirror

\n\n
+refs/*:refs/*\n
Run Code Online (Sandbox Code Playgroud)\n\n

这个说的是获取他们所有的引用,无论它们是什么类型的名称,并用他们每个引用中的哈希 ID覆盖我的所有引用。 这意味着 everygit fetch会替换您的所有分支和标签名称。任何你拥有而他们没有的承诺,你现在都已经失去了。他们拥有而你没有的任何提交,你现在拥有\xe2\x80\x94,并且你的克隆现在是他们的镜像。

\n\n

git push --mirror命令意味着您将拥有自己的 Git 命令(如--force或 加号前缀):

\n\n
    \n
  • 创建任何你有而他们没有的名字;
  • \n
  • 更新您已更改的任何名称;和
  • \n
  • 删除他们拥有而你没有的任何名称。
  • \n
\n\n

这会完全清除他们的所有分支、标签和其他名称,并将其替换为您的名称。(当然,您将首先发送任何提交以及完成此操作所需的其他对象。)这确实取决于它们遵守强制操作,但这是通常的默认设置。

\n\n

这里要记住的主要一点是,在 Git 文档中,“镜像”一词意味着从另一个 Git 存储库获取一切:一个 Git 存储库对任何事物都不具有权威性,而另一个 Git 存储库对所有事物始终具有权威性。这显然不是你想要的!您只希望一个存储库对一个分支具有权威性。

\n\n

您可以从任一方向执行此操作:您可以git fetch repoA +refs/heads/theirs:refs/heads/mine将您的分支替换mine为 上的最新分支theirs,并且您可以将分支git push repoB +refs/heads/mine:refs/heads/theirs替换为 上的最新分支。除了运行命令的位置和数据流的方向之外,这些几乎是对称的:唯一真正的区别是,它们可以拒绝(通过预接收、更新或后接收挂钩)。theirsminegit push

\n