27

我有两个存储库 url,我想同步它们,使它们都包含相同的东西。在 Mercurial 中,我想做的是:

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

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

我想在 Git 中做同样的事情。例如,在没有用户交互的情况下,将所有更改都放入两个存储库中,并带有多个分支/头/以后要合并的任何内容。我正在尝试在命令中使用 url 来执行此操作,而不是添加遥控器(?),因为可能涉及许多存储库,并且为它们设置别名只会使我的脚本更加复杂。

我目前正在使用克隆存储库,git clone --bar {repo1}但是我正在努力“更新”它。我已经尝试过get fetch {repo1},但这似乎并没有降低我的更改;git log仍然没有显示 repo1 中添加的变更集。

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

最好的方法是什么?

编辑:为了让我更清楚我想要做什么......

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

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

在 Mercurial 中,这是微不足道的!我只是从两个存储库中提取,然后推动-f/--force以允许推动多个头。然后任何人都可以克隆其中一个存储库,合并头部,然后推回。我想知道如何在 Git 中做最接近的类似事情。它必须是 100% 非交互式的,并且必须将两个存储库保持在可以无限重复该过程的状态(这意味着不重写历史记录/更改变更集等)。

4

6 回答 6

30

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 存储库,因为这个概念不存在。您可以使用名称混乱(?)推送到分支bitbucket_master,但据我所知,您无法远程更新遥控器的遥控器。

不过,我认为您的计划没有多大意义,因为由于未合并的分支都暴露于两个存储库,您要么必须将它们合并,要么合并一个,然后将其镜像到另一个之上。 .. 在这种情况下,您无缘无故地使第二个存储库处于无用状态。

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

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

  2. 将另一个存储库添加为远程,例如github.

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

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


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

[2] 这是善意的谎言。分支仍然被调用master,但它属于远程被调用origin,并且斜杠是引用它的语法。区别很重要,因为 Git 对分支名称中的斜杠没有疑虑,因此您可以有一个名为 的本地分支origin/master,这会影响远程分支。

于 2013-02-24T22:33:15.687 回答
10

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

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

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

于 2015-09-23T05:49:01.083 回答
6

这是针对该问题的经过测试的解决方案: 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
于 2015-11-21T10:30:06.000 回答
2

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

我还没有为未命名的遥控器制定语法,但是您可以根据 url 中的某些方案自动添加遥控器...无论如何,如果您为每个遥控器选择一些唯一且一致的名称,它可能会工作得最好repo,所以你可以知道什么变化来自哪里

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

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 克隆,则可以看到它们,因此它们确实存在。

于 2013-02-24T20:38:10.623 回答
1

git reset --hard HEAD之后试试git fetch。但是,我不确定我是否完全理解您的目标。cd在运行 fetch、reset 和 push 命令之前,您需要进入单独的存储库目录。

于 2013-02-24T20:47:01.733 回答
1

git-repo-sync
它精确地同步远程 Git 存储库对,用于远程双方的持续团队开发工作。
就像您有两个指向单个存储库的入口点,而您的两个远程 Git 存储库的行为几乎就像一个存储库一样。

我是git-repo-sync的作者,我在开发过程中的主要想法是安装、定期自动运行并忘记这个工具的存在
它实际上做得很好。

git-repo-sync具有自动冲突解决策略、不同的灾难保护和许多功能。你最好看一下项目的README

唯一的问题是,它不同步 Git 标签,但这是故意的。

抱歉,对于这个 SO 问题的 Mercurial 方面,我无能为力。但我的工具可能对那些只为 Git 遥控器寻求解决这个问题的人有所帮助。

于 2020-04-14T22:49:26.227 回答