保持2个git仓库同步

时间:2018-06-29 12:35:31

标签: git synchronization repository

我面临以下问题,并且没有主意:

我的公司不允许开发人员直接访问Internet。因此,我们非常需要自己的git存储库。到目前为止,一切正常。我们的开发人员正在从事的项目得到了也为我们开发的外部公司的支持。该公司拥有自己的git存储库。他们没有直接访问我们的git repo的权限,我们也没有直接访问他们的git仓库的权限。 仅通过能够访问其存储库的僻静服务器提供访问权限。

为了更好地理解:

  

我的公司回购= A,   外部公司回购= B

这两个存储库都需要保持同步。两者具有相同的分支,对A所做的更改应结转到B,反之亦然。两家公司同时在所有分支机构工作。我告诉他们保持分离的分支继续工作,但是他们没有听。反正...

到目前为止,我的解决方案是可以在这里找到以下脚本:

$ORIGIN_URL=EXTERNAL REPO B
$REPO1_URL=INTERNAL REPO A

/usr/bin/git clone -c http.sslVerify=false --bare $ORIGIN_URL
/usr/bin/git remote add --mirror=fetch repo1 $REPO1_URL
/usr/bin/git -c http.sslVerify=false fetch --all
/usr/bin/git fetch repo1 --tags
/usr/bin/git push origin --all
/usr/bin/git push origin --tags
/usr/bin/git push repo1 --all
/usr/bin/git push repo1 --tags

问题是,由于两家公司都在同一个分支机构(即A / fix1和B / fix1)工作,所以我经常遇到冲突(更新被拒绝,因为推送的分支机构提示位于其远程对象后面(非快进) ))。

我正在尝试查找一些脚本,这将为我和两家公司解决此问题。

我将不胜感激,希望能就如何解决我一遍又一遍的这一冲突提出一些建议。

谢谢您的帮助

问候

2 个答案:

答案 0 :(得分:1)

听起来好像您在想他们的分支与您的分支具有相同的名称(如果其名称相同)。不一定是真的。一种看待它的方式是,git从来没有将两个存储库中的分支视为“同一分支”。它只是有关于如何整合存储库之间的更改的规则。根据配置这些规则的方式,您可能会将它们视为“同一分支”。

因此,第一件事是不同地配置规则。实际上,git的默认行为在这里还不错。但是在--mirror=fetch遥控器上设置repo1可能会无济于事,从而覆盖默认设置。如果我们不这样做,事情就会简单一些。我们还可以通过手动添加两个遥控器来简化事情,而不用克隆其中一个仓库。 (这不是必须的;我只是认为这使事情变得更加清楚。)

git init --bare
git remote add external $ORIGIN_URL
git remtoe add internal $REPO1_URL
git fetch --all

现在假设每个回购都有一个branch1和一个branch2,并且两者都发散了,那么您的新回购看起来就很像

       E <--(remotes/external/branch2)
      /
o -- x -- D <--(remotes/internal/branch2)
      \
       x -- A -- B <--(remotes/internal/branch1)
        \
         C <--(remotes/external/branch1)

在这里,您可以通过命名分支间距来将外部分支共享到内部仓库,而不必担心分支名称冲突。

git push internal refs/remotes/external/*:refs/heads/external/*

现在您的内部仓库看起来像

       E <--(external/branch2)
      /
o -- x -- D <--(branch2)
      \
       x -- A -- B <--(branch1)
        \
         C <--(external/branch1)

当然,外部更改不会与内部更改集成在一起,但这与根据原始建议它们使用不同的分支名称的情况相同。可以预见-有时必须将外部更改合并到内部分支中(反之亦然),然后必须解决冲突。

(当然,您可以使用某些做法来使合并冲突解决尽可能轻松地进行,例如偏爱寿命短的分支和频繁的增量集成。但是您不能完全消除它们。)

您可以类似地与外部存储库以未集成的形式共享内部更改;例如通过做类似的事情

git push external refs/remotes/internal/*:refs/heads/internal/*

但是,这留下了一些关于谁整合变更以及如何进行整合的问题,特别是因为听起来外部公司在这方面没有按照要求进行变更。因此,您可能希望在内部集成他们的更改,然后使用他们已经知道的分支名称共享集成的更改。

诀窍是,您必须使用“获取,集成,推送”模型来避免像您已经看到的“非快进”错误。当您的工作克隆能够直接与远程通信时,通常可以这样做

git pull
# resolve conflicts
git push

因为您必须使用此网桥存储库,但是可能不想在该存储库中完成所有集成工作,因此需要执行额外的步骤。这可能会很烦人,因为完成获取/集成/推送周期所需的时间越长,在获取之后但在推送之前出现新更改的机会就越多,这需要您执行另一个获取/集成/推送周期。当然,推送是在逐个引用的基础上接受或拒绝的,因此,随着时间的推移,推送应该可以解决(尝试1成功推送分支A,尝试2成功推送分支B和C,依此类推)。

因此集成工作流可能如下所示:

在网桥存储库上

fetch --all
git push external refs/origins/internal/*:refs/heads/*

这将尝试直接更新其分支。一些裁判可能会被拒绝;没关系,您希望在下一个周期获得它们。

git push internal refs/origins/external/*:refs/heads/external/*

这应该总是成功的。为了确保它总是成功,您应该确保永远不要对external/*分支进行内部提交。因此,您可能要使用非分支引用(即,将外部引用保留在refs/heads层次结构之外),但不清楚将它们放在何处。您可以像对待远程跟踪参考一样继续对待他们

git push internal refs/origins/external/*:refs/origins/external/*

这有点阴暗,因为内部存储库实际上没有名为external的远程设备...

无论如何,开发人员现在可以以一种或另一种方式查看更改并将其集成到分支的本地版本中,从而解决了冲突。然后,在下一个集成周期中,当您fetch时,您将获得合并提交,可以尝试将其提交到远程。视需要重复。

当然,这是基于“他们似乎没有按照要求做的事情”,以协调内部和外部变更。您可以让每个人在同一页面上使用回购协议的次数越多,您遇到的麻烦就越少。 (在这种情况下,必须在内部进行所有集成,并且可能会延迟外部对内部更改的可见性。)

从这个意义上讲,我喜欢将内部引用推送到外部仓库,将外部引用推送到内部仓库的想法,以便两家公司的开发人员都可以看到两组变更。但是,您不希望外部开发人员致力于内部分支机构,反之亦然,因为这样,集成就会变得很奇怪,就像rsfs / heads / internal / external / master之类的分支机构一样。

答案 1 :(得分:0)

要使这一切正常进行,您(公司A)和他们(公司B)需要有一个约定的共享点。这个Git仓库克隆不必是“主”或“所有真相的来源”。也就是说,你们两个-我们目前假装的两家公司并不由许多个人和/或单个克隆组成-可以采用各种不同的方式来对待它,这取决于你们两个;但您需要它作为协调站点。您可以将它托管在任何喜欢的位置,只要两个人都可以阅读它,则至少有一个可以修改它,并且如果只有一个可以修改它,则一个(再次为A或B)至少具有“已读” ”访问另一个发布的存储库。

(但是,如果将共享克隆视为“主人”或“所有真相的来源”,事情就更简单了,因为众所周知,当给定多个不同的观点时,人类将无法确定现实。)

为简单起见,我主要假设A和B中的每个人都有对共享存储库的写入(推送)访问权限。我们将此共享存储库称为SR。其余的只是一种方法;另请参见Mark Adelsberger's answer

为使事情井井有条,共享存储库SR中的分支名称可以简单地加上前缀:SR可以使用masterdevelop等来代替具有名为A/masterB/masterA/developB/develop的分支,依此类推。公司A的代表(人工操作git push或通过从SR到A中某个暴露点的操作进行机器驱动的更新)将A的master传递给SR的A/master,依此类推。在Git中这很容易做到,因为Git具有分支重命名的概念,尤其是在提取方向。

(如果您确实使用push来更新这些名称,请考虑安装一个预接收或更新钩子,以验证是否允许经过身份验证的push源更新相关名称。也就是说,您将给代表一个不同的登录名A和B,然后检查是谁在进行推送:是A用户还是B用户?如果是A用户,则所有分支名称都必须以refs/heads/A/开头,这样可以避免意外覆盖)

(如果A和B都使用标签,则您都需要使用一些相当严格的自律来确保您不会踩踏其他标签。明智的做法是,完全禁止在SR中使用标签,永远不要从A或B推送它们。这是因为虽然Git很乐意重命名分支名称,但各种--tags提取或推送操作中的任何都不重命名标记名,因此,如果A处的某人叫v1.2,而B处的某人叫v1.2,则标记名冲突。使用--no-tags可以避免这种头痛,但代价是完全不会在SR上放置任何标签。)

在此特定设置中,这允许每个公司具有共享存储库SR的内部镜像。内部镜像告诉您,无论您是在A还是B工作,他们都会看到什么:如果您在A,则检查B/masterB/develop以查看他们的最新版本是。此内部镜像仅复制SR中的内容。但是,即使您没有直接访问权限,它也使您可以访问共享数据,甚至不能访问共享存储库SR。

要将某物从A发送到B,A的工作人员提出了提交,然后A中具有适当权限的某人将这些提交集成到某个内部存储库中—可能是充当镜像的存储库,或者可能是另一个资料库。 Git的使用鼓励了许多这样的存储库重复,并且实际上效果很好。现在,具有权限的A人将提交推送到SR。如果他们到达那里,那么A处的人员也会更新A可访问的镜像,以便A处的所有程序员都可以看到B处的程序员可以使用这些提交。此时A/branch与{{1}不同}。现在由B的人员将这些集成到他们的存储库中。一旦这样做,他们将经历同样的舞蹈(见下文),而SR将再次匹配B/branchA/branch

当B处的程序员进行一些更新时,如果A处的程序员喜欢该更新,则他们可以将新提交合并到自己的存储库中,然后通过相同的授权人技术将它们作为更新发送。现在,两者已经同步,而不是B/branchB/branch之前。