我有两个存储库-存储库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结合使用。
在与此类似的其他一些问题中,解决方案不一定要镜像到特定分支或从特定分支镜像。不幸的是,我面临着这个特殊的用例,无法与主服务器进行镜像,但是需要在特定的分支中进行。
答案 0 :(得分:0)
您问了两个问题:
repo1:branch1
镜像到repo2:master
repo2:master
镜像到repo1:branch1
您可以通过基本相同的步骤来完成这两项操作。
将repo1:branch1
镜像到repo2:master
克隆repo1
。
git clone <git URL for repo1>
输入新克隆。
cd repo1
为repo2
的{{1}}分支设置新的遥控器。
master
结帐git remote add -t master repo2 <git URL for repo2>
repo1:branch1
将git checkout branch1
推到repo1:branch1
。
repo2:master
将git push repo2 +branch1:master
镜像到repo2:master
克隆repo1:branch1
。
repo2
输入新克隆。
git clone <git URL for repo2>
为cd repo2
设置新的遥控器。
repo1
为git remote add -t branch1 <git URL for repo1>
检出master
(可能已经检出)
repo2
将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/master
或dinh/master
或yourbranch
或其他名称我想称呼它。如果您说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是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。)
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
时,它们可以拒绝(通过预先接收) ,更新或接收后挂钩)。