如何同步两个远程Git存储库?

时间:2013-02-24 20:33:38

标签: git version-control github dvcs

我有两个存储库网址,我希望将它们同步,以便它们都包含相同的内容。在Mercurial,我想做的是:

hg pull {repo1}
hg pull {repo2}
hg push -f {repo1}
hg push -f {repo2}

这将导致两个回购中的两个头(我知道有两个头是不常见的,但我这样做是为了同步并且它需要是非交互式的。头部将手动合并其中一个repos然后再次运行同步)。

我想在Git中做同样的事情。例如,在没有用户交互的情况下,将所有更改都记录到两个repos中,具有多个分支/头/稍后要合并的任何内容。 我试图在命令中使用url来做这个,而不是添加remotes(?),因为可能涉及到一些repos,并且为它们设置别名只会让我的脚本更复杂。

我目前正在使用git clone --bar {repo1}克隆回购,但我正在努力“更新”它。我试过了get fetch {repo1},但这似乎并没有让我的改变失败; git log仍未显示已在repo1中添加的更改集。

我还尝试在我的--mirrorpush中使用clone,但这似乎是来自repo2的远程更改集,本地不存在,而我需要保留更改来自两个repos :/

最好的方法是什么?

修改:为了让我更清楚一点,我正在尝试做...

我有两个存储库(例如BitBucket和GitHub)并且希望人们能够推送到任何一个(最终,一个将是Git,一个将是Mercurial,但让我们假设他们现在都是Git来简化事情)。我需要能够运行一个脚本,它将以两个包含两组更改的方式“同步”这两个repos,并且可能需要稍后手动合并。

最终,这意味着我可以与其中一个repos(例如Mercurial)进行交互,我的脚本将定期提取我可以合并的Git更改,然后它们将被推回。

在Mercurial中,这是琐碎的!我只是从两个回购中拉出来,并用-f/--force推动以允许推动多个头。然后任何人都可以克隆其中一个回购,合并头,并推回。我想知道如何在Git中做最接近的类似事情。它必须是100%非交互式的,并且必须保持两个repos处于可以无限重复进程的状态(这意味着没有重写历史/更改更改集等)。

6 个答案:

答案 0 :(得分:20)

Git分支在Mercurial意义上没有“头”。只有一个名为HEAD的东西,它实际上是您当前检出的提交的符号链接。对于像GitHub这样的托管存储库,没有提交签出 - 只有存储库历史本身。 (称为“裸”回购。)

这种差异的原因是Git分支名称完全是任意的;它们不必在存储库的副本之间匹配,您可以随心所欲地创建和销毁它们。[1] Git分支就像Python变量名一样,它可以随意移动并随意粘贴到任何值; Mercurial分支就像C变量,它指的是固定的预分配内存位置,然后填充数据。

因此,当您使用Mercurial时,您有两个同一分支的历史记录,因为分支名称在两个存储库中都是固定有意义的事物。每个历史记录的叶子都是“头”,你通常将它们合并成一个单独的头。

但是在Git中,获取远程分支实际上并不会影响你的分支。如果您从master获取origin分支,则会进入名为origin/master的分支。[2] git pull origin master只是两个步骤的瘦糖:将远程分支提取到origin/master,然后将其他分支合并到当前分支中。但他们不必有相同的名字;您的分支可以被称为developmenttrunk或其他任何内容。您可以将任何其他分支拉入或合并到其中,然后您可以将其推送到任何其他分支。 Git不在乎。

这让我回到你的问题:你不能将“第二”分支头推送到远程Git存储库,因为这个概念不存在。您可以推送到名称错误的分支(bitbucket_master?),但据我所知,您无法远程更新远程遥控器。

我不认为你的计划很有意义,因为将未合并的分支暴露给两个存储库,你要么必须合并它们,要么合并一个然后镜像它另一个......在这种情况下,你无缘无故地将第二个存储库置于无用状态。

有没有理由你不能这样做:

  1. 选择一个规范的存储库 - 我假设BitBucket。克隆它。它变为origin

  2. 将另一个存储库添加为名为github的远程控制器。

  3. 让一个简单的脚本定期获取两个遥控器并尝试将github分支合并到origin分支中。如果合并失败,则中止并向您发送电子邮件或其他内容。如果合并很简单,请将结果推送到两个遥控器。

  4. 当然,如果您只是在功能分支上完成所有工作,那么这一切都不会成为问题。 :)


    [1]它变得更好:您可以将来自不具有无历史记录的不同存储库的分支合并在一起。我这样做是为了整合分开启动的项目;他们使用不同的目录结构,所以它工作正常。 GitHub对其Pages功能使用了类似的技巧:Pages的历史记录存储在一个名为gh-pages的分支中,该分支位于同一个存储库中,但绝对没有与项目其余部分相同的历史记录。

    [2]这是一个白色谎言。该分支仍称为master,但它属于名为origin的远程,斜杠是引用它的语法。区别可能很重要,因为Git对分支名称中的斜杠没有任何疑虑,因此您可以拥有一个名为origin/master的本地分支,这将影响远程分支。

答案 1 :(得分:4)

对于类似的东西,我使用这两个存储库中由webhook分配的简单代码来同步GitLab和Bitbucket主分支:

git pull origin master
git pull gitlab master
git push origin master
git push gitlab master

它可能不是你需要的东西,但它可能对需要同步一个分支的其他人有所帮助。

答案 2 :(得分:3)

以下是此问题的经过测试的解决方案: http://www.tikalk.com/devops/sync-remote-repositories/

要运行的命令:

#!/bin/bash

# REPO_NAME=<repo>.git
# ORIGIN_URL=git@<host>:<project>/$REPO_NAME
# REPO1_URL=git@<host>:<project>/$REPO_NAME

rm -rf $REPO_NAME
git clone --bare $ORIGIN_URL
cd $REPO_NAME
git remote add --mirror=fetch repo1 $REPO1_URL
git fetch origin --tags ; git fetch repo1 --tags
git push origin --all ; git push origin --tags
git push repo1 --all ; git push repo1 --tags

答案 3 :(得分:0)

当您使用git clone --mirror --bare时,您可能没有看到fetch确实有效,因为默认情况下git不会列出它的远程分支。您可以使用git branch -a列出它们。

我没有为未命名的遥控器制定语法,但您可以根据网址中的某些方案自动添加遥控器......无论如何,如果您选择一些独特且一致的话,它可能会效果最佳每个仓库的名称,因此您可以知道哪些变更来自哪里

但是,您可以尝试这样的事情:

git clone --bare --mirror --origin thing1 {repo1} repo.git
cd repo.git
git fetch thing2 --mirror
git push thing1 --mirror
git push thing2 --mirror

完成此操作后,thing1会将所有thing2的分支随时可用于合并,作为远程​​分支。您可以使用git branch -a列出远程分支。

在github或bitbucket上,您将无法通过Web界面看到这些远程分支,但是如果使用--mirror进行克隆,则可以看到它们,因此它们确实存在。

答案 4 :(得分:0)

git reset --hard HEAD后尝试git fetch。但是,我不确定我到底知道你的目标是什么。在运行fetch,reset和push命令之前,您需要cd进入单独的存储库目录。

答案 5 :(得分:-1)

和其他人一样,我来这里是因为这个SO问题的标题非常适合该特定问题。
这是几年前的事。我研究了可用的答案,但没有任何方法可以解决我的情况。

我进一步走了一步,创造了几种解决方案。
我在这里发布的最终解决方案仅适用于Git存储库(对不起,我现在不使用Mercurial,因为在问题正文中被问到了)。

一开始我的处境比这里的其他人都困难。

  • 我不能使用Git-hooks,因为我始终只能访问一个或另一个远程存储库。我认为它们是负担负担。
  • 双方的队伍很大。它们也会产生大量的提交,并且也会在相同的Git分支上提交。
  • 我们需要24/7和快速同步解决方案。这大大减少了可能的Git冲突量,并将其转换为简单的本地Git合并。
  • 重要的是要有能力从任何远程存储库为任何分支机构以及两个团队的任何成员进行Git合并。
  • 有时,一个远程存储库需要完全替换为另一位置的空新存储库。而且我不想手动进行初始存储库填充。
  • CI / CD管理分支应完全任意迁移。
  • 并非所有当前的同步方法都能正确识别细微的变化。例如,回顾历史。
  • 并且某些方法有威胁性的趋势,有时会删除分支或整个存储库。

就像这个问题的作者一样,我也想拥有

  • 完全没有用户交互。也就是说,一切都应该完全自动化。
  • 所有分支机构两个仓库中的所有更改都应迁移。

我很快写了一些bash脚本,并很快意识到使用Git远远不够。
主要问题是解决冲突。
长话短说,您只能通过某些约定或要求重复发生冲突的合并(提交)来解决冲突。

因此,我的脚本成为了编译后的应用程序。

后来达到了一些要求。

  • 非常希望使某些分支对于另一侧存储库不可见。
  • 我们不允许对所有分支进行任意更新。某些分支应在一侧或另一侧以有限的方式进行更新。
  • 一些Git服务器喜欢创建util-(垃圾)分支和标签。没有人希望他们被迁移。
  • 更重要的是,某些Git服务器(例如Gitlab或Bitbucket)喜欢完全阻止某些分支和标签。这是一场灾难。

另一件事是,人们倾向于忘记他们有两个同步的远程Git存储库的事实。他们真的很快,不想记住任何规则。
有些团队可能会有一定程度的员工轮换。
大型应用程序需要更多文档和支持。等等

最后,我提出了一些复杂的冲突解决和状态分析方法。
剩下的逻辑子集非常简单,这使我可以将应用程序转换为一堆bash和gAWK脚本。

作为结果,我的工具仅同步具有定义前缀的分支。
您可以说,让我们同步以@开头的分支。
或以my-company-your-company-开头的分支。

当然,我的工具有一点学习曲线。
但是它已经相当成熟,完全有可能忘记一些同步问题。

实际上,我忘记了我的工具,两年后才来这里,因为我已经实现了愿望清单。

我的最新工具在这里- git-repo-sync 。我希望这会对其他人有所帮助。