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

时间:2019-05-07 18:33:44

标签: git github clone mirroring

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

我已经尝试按照您的典型git clone --mirror URL进行操作,然后按照git push --mirror URL将其推送到存储库1。但是,这似乎仅将镜像推送到存储库1的主分支。看到有人引用可能在做git push branch1 --mirror URL,但是为此,我遇到了致命的问题:--mirror不能与refspecs结合使用。

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

2 个答案:

答案 0 :(得分:0)

您问了两个问题:

  • repo1:branch1镜像到repo2:master
  • repo2:master镜像到repo1:branch1

您可以通过基本相同的步骤来完成这两项操作。


repo1:branch1镜像到repo2:master

  1. 克隆repo1

    git clone <git URL for repo1>
    
  2. 输入新克隆。

    cd repo1
    
  3. repo2的{​​{1}}分支设置新的遥控器。

    master
  4. 结帐git remote add -t master repo2 <git URL for repo2>

    repo1:branch1
  5. git checkout branch1 推到repo1:branch1

    repo2:master

git push repo2 +branch1:master 镜像到repo2:master

  1. 克隆repo1:branch1

    repo2
  2. 输入新克隆。

    git clone <git URL for repo2>
    
  3. cd repo2 设置新的遥控器。

    repo1
  4. git remote add -t branch1 <git URL for repo1> 检出master(可能已经检出)

    repo2
  5. git checkout master 推到repo2:master

    repo1:branch1

答案 1 :(得分:0)

您可以在这里做任何您想做的事情。真正的诀窍是首先弄清楚您想要什么。在那之后,这只是 refspecs 的问题,我将在稍后讨论。不过,鉴于上述内容,您想要任何--mirror选项。

获取,推送,命名和提交哈希

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

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

相比之下,

分支名称特定于一个Git存储库或另一个。即使我们的两个Git存储库要交换提交,您的master也可能与 my master完全无关。我让我的Git叫你的Git;您的Git告诉我您的 master是提交a123456...;我从您那里得到了提交,但仍然是a123456...,但是我在Git中使用的名称完全是其他名称,例如origin/masterdinh/masteryourbranch或其他名称想称呼它。如果您说a123456...,而我说a123456...,我们可以说我们都拥有那个提交。您的名称(如果有的话)和我的名称无需同意,并且对于git fetch,他们通常不同意

git push命令不是很对称,但是它的工作原理非常相似:我的Git调用了您的Git,并通过原始哈希ID为您提供了一些提交(您的Git和我的同意像往常一样)。然后,我要求(常规推送)或命令(强制推动)您的 Git设置一个或多个您的名称以标识一些特定的提交,我将其通用命名为,商定的哈希ID。

我在您的存储库中运行git fetch时,我的Git可以看到您的名字。 git fetch的默认设置是让我的Git复制您的名字,对它们进行重命名:这就是为什么在拥有Git之后,您的Git中通常会有一个origin/master从另一个您正在调用的origin的Git复制内容。同时,我让我的Git告诉您的Git在我从存储库运行git push 时要使用的名称。为了方便起见,我通常在git push时告诉您的Git my 分支名称。这就是为什么您通常将master推到原始master的原因:这里没有默认的重命名。

哈希ID与引用

如果哈希ID是Git查找提交的方式(实际上是),那么,为什么我们根本没有分支名称?好吧,考虑一下实际的Git存储库中的一些实际的哈希ID:

83232e38648b51abbcbdb56c94632b6906cc85a6
aa8c8d914e4ae709e4fd025f359594f62653d9e5
061ed420ec2dc97e2a922a6f02992869089cefb3

这是三个提交,按照提交的顺序(最新的)。你能记住这些数字吗? (也许,但是我不想想要。)Git 需要记住所有的 ,但是Git的设置是为了使{ {1}}自己记住数字83232e...aa8c8d...记住数字aa8c8d...。因此,我们只需要记住那个大而丑陋的061ed4...即可。我们可以写下来;但是如果让Git为我们写下,那可能会更好。

我们可以使用分支名称,标签名称或任何其他此类名称来执行此操作。 名称将保留83232e...,现在我们只需要记住名称。这些名称统称为Git称为 refs references 。分支名称是以83232e...开头的名称,标记名称以refs/heads/开头,远程跟踪名称以refs/tags/开头并以远程名称开头(例如{{1} }。在最后一个斜杠之后,您将拥有分支,标记或远程跟踪名称本身。 Git倾向于隐藏前缀,但有时会出现,特别是在使用完整引用时。

因此,我们有引用(分支名称,标记名称,远程跟踪名称等等),这些引用会记住一个哈希ID。 Git从那个哈希ID中找到其余的哈希ID。关于分支名称的唯一特殊之处在于,我们可以使用refs/remotes/进入分支“ refs/remotes/origin/会说git checkout”,然后,一旦为此,我们可以进行 new 提交。我们进行的新提交将为我们记住git status,而Git将自动将新提交的哈希ID(将是新的且唯一的,不同于以往的其他所有提交)填充到on branch master中,该{记住最近的提交。

因此,分支名称只是自动指定分支中的 last 提交。从该提交中,Git通过其哈希ID(在名称下找到)找到了它,然后找到了 previous 哈希ID,这使Git进入了提交,并获得了另一个先前的哈希ID,依此类推。结果就是历史记录:一系列在指定尖端结束的提交,其哈希ID存储在分支名称中。

83232e...master交换提交时,它们通过哈希ID进行,但是他们也看到并复制了名称。这是 refspecs 出现的地方。

参考规格

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

git push

您可以在任何一侧放置任何参考。在这里,我们在左侧有:refs/heads/master:refs/remotes/origin/master 被标记为分支名称,而在右边,master被标记为远程跟踪名称。左边的名称是 source ,右边的名称是 destination refs/heads/使用的是这种refspec,因为源引用是分支名称,而目标引用是远程跟踪名称。这告诉您的Git:使用他们的origin/master分支,使用我的git fetch获得他们没有的任何提交,并记住他们的master所表示的提交。

使用master,您可以编写origin/master。您的Git会将其翻译为git push-扩展全名-并接受您没有的所有提交,将其发送过来,然后要求他们设置他们的 git push origin master:master分支到该链中的最后一个提交。或者,您可以refs/heads/master:refs/heads/master,告诉Git接受master(位于左侧的源),并发送所有您没有的提交,然后要求他们设置分支{{ 1}}。

您可以-至少在脚本中应该-拼写全名,并在开头加上git push origin master:bren。这可以确保,如果由于某种原因有人意外创建了名为master tag ,则不会产生歧义:您是说 branch bren,而不是标签 refs/heads/

任何refspec之前都可以带有单个加号master字符。这样做会告诉Git:即使您通常会反对,也要执行此操作。也就是说,它会为此一个特定的参考更新设置refs/heads/master标志。例如,如果更改分支名称会丢失一些提交,则Git通常会提出反对。也就是说,如果分支名称当前显示为“ commit a123456”,其中显示为“从此处,返回一步到fedcba9”,而您要求他们将名称设置为“ fedcba9”,则他们将找不到“ a123456”。 ”-提交中的定向链接仅指向向后,指向较旧的提交,而永不转发至较新的提交。 (有关更多信息,请参见Think Like (a) Git。)

Refspec匹配和refs/tags/master选项

您可以并且通常+使用特殊的--force元字符来匹配所有分支:

--mirror

此面向提取的refspec说:获取所有分支名称-git fetch下的所有内容-并获取所有提交信息,然后强行更新{{1中的所有我的远程跟踪名称}}进行匹配。这是*用于与名为+refs/heads/*:refs/remotes/origin/* 的远程设备进行对话的常规refspec。这样,您可以所有它们的分支,作为您的远程跟踪名称。

但是,您可以使用refs/heads/将此引用规范更改为:

refs/remotes/origin/

这个人说不管他们是什么名字,都拿他们的所有引用,并用他们各自的哈希ID覆盖所有 my 引用。表示每个git fetch都会替换 all 您的分支和标记名称。您拥有的任何承诺,现在都已丢失。他们现在拥有的所有您没有的承诺,您的克隆现在是他们的镜像。

origin命令意味着您将把自己的Git命令(如git clone --mirror或加号前缀)用于:

  • 创建您没有的任何名称;
  • 更新所有已更改的名称;和
  • 删除所有您没有的名字。

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

这里要记住的主要事情是,在Git文档中, mirror 一词的意思是从其他事物中夺取一切:一个Git存储库从不授权任何东西,而另一个总是对一切都有权威。那显然不是您想要的!您只希望一个存储库对一个分支具有权威性。

您可以在任一方向上执行此操作:您可以+refs/*:refs/* git fetch上的最新版本替换分支git push --mirror,也可以--force替换分支git fetch repoA +refs/heads/theirs:refs/heads/mine,其中包含mine的最新信息。除了运行命令的位置和数据流的方向外,它们几乎是对称的:唯一的实际区别是,使用theirs时,它们可以拒绝(通过预先接收) ,更新或接收后挂钩)。