9

我有一个项目,其主要(Mercurial)存储库位于 SourceForge 上,但 Bitbucket(Mercurial)和 Github(Git)上有克隆。

现在我一直在使用hg-git将 Mercurial 存储库推送到 Github,据我了解该过程,一些元数据在此过程中保存在 Mercurial 存储库中。

现在,当重新克隆 Bitbucket 存储库并重新克隆 Github 存储库时,如果我发出 ahg pull ../github-repo我得到:

pulling from ../github-repo
searching for changes
abort: repository is unrelated

为什么会这样?我怎样才能让 Mercurial 相信它们确实是相关的?还是我必须依赖我最初推送到 Github 的原始存储库?我仍然拥有它,但假设我丢失了它,除了手动变更集移植之外,我还有哪些选择?


注意:由于拉取请求,Github 存储库已更改(新变更集)。但是 SourceForge 和 Bitbucket 存储库仍然认为彼此是相关的。现在的任务是将变更集从 Github Git repo 拉到本地,并将它们分别推回 SourceForge 和 Bitbucket。

4

2 回答 2

10

相关或不相关位基本上来自两个存储库是否共享一个共同的根,即初始变更集。

为了强行拉动,你可以对移植或移植扩展做一些邪恶的事情,但这可能会产生连锁反应,而且你似乎对这种解决方案持反对态度——我也会反对!

要了解您遇到问题的原因,您需要了解一点 Hg-Git 的工作原理。

Hg-Git 的工作原理

Tl;博士

真正的问题是 Hg-Git 基本上动态地创建了一个新的 repo。因此,这两个存储库不相关的原因与hg convert some-existing-hg-repo与原始存储库无关。到目前为止,您还没有注意到它,因为 Hg-Git 在另一个方向上也是这样做的——当您从 Mercurial 存储库开始时,它会创建必要的 Git 存储库。当您第一次开始克隆到 GitHub 时,您在他们的服务器上创建了一个裸 Git 存储库,从所有意图和目的来看,它都与每个存储库相关。因此,您推送由 Hg-Git 创建的新 Git 存储库是相关的,并且一切正常,没问题。之后,您从同一个 repo 推送,所以再次没有问题 - Hg-Git 跟踪本地 Git 和 Hg 存储库之间的关系,因此您的关系得以维持。但是当您重新开始时,您会创建一个新的 Git 和/或 Hg 存储库(取决于您要进入的方向)并且对应关系会中断。

稍微不那么简单

Hg-Git 通过创建一个隐藏的 Git 存储库并在 Git 的提交和 Hg 存储库之间建立对应关系来工作。Hg-Git 是一个双向的桥梁,也就是说,它能够接受 Git 提交和生成 Hg 提交,反之亦然。Hg-Git 通过使用用 Python ( dulwich )编写的 Git 库并链接到 Mercurial 作为扩展来实现其双语功能。这意味着Hg-Git 无需git安装二进制文件/Git 参考实现即可读取和写入 Git 存储库. 然而,Hg-Git 是一个 Mercurial 扩展,因此依赖于系统 Mercurial 用于交易的 Mercurial 端以及用户界面。这就是为什么努力创建反向接口(Git-Hg 等)以便 Git 用户可以使用 Git 与 Mercurial 交互的原因。

现在,是否创建 Git 或 Hg 存储库取决于最初是如何创建混合存储库的。由于您来自规范的 Mercurial 方面,我们将从那里开始。

当您在 GitHub 或 Bitbucket 上创建存储库时,它最初是裸露且无提交的,因此与每个存储库相关 - 这是默认在存储库创建时不进行初始提交的动机的一部分。(这对于 Git 和 Mercurial 都是如此。)存储库相关性基于根节点。因此,任何存储库都可以推送到这个新的存储库。当你跑hg push ssh+git://git@github.com/user/some-git-repoHg-Git 第一次在您的本地文件夹中创建一个新的隐藏 Git 存储库,然后使用 Git 协议进行通信并将更改推送到远程。从那时起,两个存储库之间的通信应该没有问题——从根节点的初始转换和父子关系,可以实现两个存储库的变更集之间的一对一映射。(这不是 100% 正确的,特别是如果您使用 Git 或 Mercurial 的更高级、惯用的功能,但现在就足够了。)Hg-Git 跟踪的信息比这多一点,我很确定,如果没有其他原因,就是通过连续的推拉来加快速度。因此,当您从 Mercurial 克隆开始时,您的“原始根”

现在,如果您不是从本地 Mercurial 克隆开始,而是从远程 Git 克隆开始,那么您实际上最终会从 Git 克隆创建 Mercurial 克隆——“原始根”是 Git 根。更准确地说,当你运行时hg clone ssh+git://git@github.com/user/some-git-repo,Mercurial 启动,检查以确保它可以与远程接口(它可以在 Hg-Git 的帮助下),然后创建目录并调用必要的扩展,即 Hg-Git。.gitHg-Git 然后在您的文件夹中创建一个隐藏文件.hg夹,执行 Git 克隆,然后将 Git 存储库转换为 Mercurial 存储库;克隆完成后,它会调用hg update,它直接在 Mercurial 存储库上运行,而不需要对 Git 存储库一无所知。

我怀疑这就是你的情况出了问题。当您从 GitHub 进行新的克隆时,您实际上创建了一个新的 Mercurial 存储库,它当然与您的原始存储库无关 - 就像产品与原始存储库无关一样hg convert即使变异的提交不包括最初的提交. (这有点像当您将某些内容翻译成另一种语言并再次返回时,您并不总是能得到原始形式。)出于各种原因,我怀疑 Hg-Git 以与时间无关且确定性的方式执行其转换(几乎肯定是后者,但它可能会添加关于转换本身的额外元数据,这意味着不是前者)。如果是这种情况,那么您应该能够从规范的 Hg 克隆开始并重新创建与 Git 存储库的连接。(是的,初始转换的方向性会有所不同,这有点成问题,但是最好与开发人员自己讨论导致该设计决策的利弊。)

回到混合 Hg-Git 存储库的结构。这里有两件有趣的事情:

  1. Mercurial 在与 Git 远程通信时或多或少完全没有注意到额外的翻译

  2. 有一个完整的 Git 存储库隐藏在视图中,并且偶尔会同步到 Mercurial 存储库。

重要的是,您实际上可以通过系统 Git 直接对隐藏的 Git 存储库进行操作。如果您使用 Hg-Git,则 Git 存储库仅在远程 Git 克隆的推送和拉取时同步,这意味着那些本地直接 Git 更改将与 Mercurial 存储库不同步——在最坏的情况下,您提交一个几次到 Git,然后在没有同步的情况下提交到 Mercurial 并有效地创建两个单独的分支,因为 Hg 提交和 Git 提交共享一个共同的祖先,但不建立在彼此之上。hg gimport [git-repo-to-import-from-if-not-local-hidden]然而,Hg-Git 提供了一种机制来手动强制在 repos 之间同步hg gexport(默认导出到本地隐藏副本,如果需要创建它)。强制此同步还应该为您提供一种处理您注意到的问题的方法。您可以即使用 Git 将新的变更集拉入(或在 Git 术语中,fetch-git pull相当于hg pull --update; git fetchis hg pull,这使得 Mercurial 提取扩展名的名称真的很不幸)到 Git 存储库中,然后使用hg gimport将这些变更集导入到 Mercurial 存储库中。

现在,如果你做了诸如编辑历史之类的事情,那么所有的赌注都没有了。我不确定 Hg-Git 会如何处理这个问题——我怀疑它最终会创造双打。Mercurial 克隆中的新提交将添加到 Git,但删除的变更集仍在 Git 存储库中,并且可能会被导入回 Mercurial 存储库。(这是 Hg-Git 离线同步变更集的方法的直接结果。)在这种情况下,我建议选择一个规范的存储库,擦除所有克隆,并进行新的推送,并向所有克隆因这场混乱而无效的人道歉. (顺便说一句,这也是 Mercurial 社区对编辑历史如此谨慎的部分原因。)

潜在的解决方案

  1. @EmilSit 建议您hg pull git+ssh://github.com/you/githubrepo.git直接从规范(非 GitHub 克隆)Mercurial 存储库运行。假设 Hg-Git 创建初始 Git 克隆的方法完全与时间无关且具有确定性,这很有可能奏效。(后者几乎可以肯定是正确的,但我不确定前者,请参阅上面的文字了解更多详细信息。)

  2. 您可以执行此操作的本地变体:使用git clone ssh://github.com/you/githubrepo.git获取本地纯 Git 克隆,然后执行hg pull ../githubrepo. (这要求您安装了 Git。)Hg-Git 应该会自动启动并进行转换。转换还依赖于 Hg-Git 以一种确定的、与时间无关的方式进行转换。

  3. 您可以直接对原始混合存储库中隐藏的 Git 存储库进行操作。使用git fetch(您可能首先必须先cd进入.git隐藏在文件夹中的.hg文件夹)。然后运行hg gimport && hg update以从 Git 存储库导入更改并更新。(您可能已经为隐藏的 Git 存储库指定了路径或路径。我怀疑您也可以指定 GitHub 路径。gimport.

  4. 您可以使用各种愚蠢的移植方法——导出补丁系列等——并手动提交它们。如果您想在进行手动提交时给予其他开发人员信用,那么您可以使用该-u选项在每次提交的基础上设置用户。

  5. 您可以使用移植移植扩展进行智能移植。首先,使用 Hg-Git 对 GitHub 存储库进行新的 Mercurial 克隆。然后使用其中一个扩展将两个 Mercurial 存储库拉到一起。

至少其中一种非移植方法应该有效,因为除非 Hg-Git 在时间依赖性方面发挥作用,否则应该可以找到共同的根。即使找到了一个共同的根,您也可能会得到两个基本上重复的(未命名的)分支,然后您必须将它们重新合并在一起。

于 2013-06-30T18:06:08.237 回答
3

我要补充一点,当您将 hg 存储库推送到 git,然后从 hg 克隆 git 存储库,然后您尝试从原始 hg 存储库中提取更改时,您甚至会收到“与存储库无关”的错误。从现在开始,我们在本地创建了一个从原始 hg 存储库创建的 git 存储库的 hg 存储库,我想本地和原始 hg 存储库应该相关,但有时不相关。

由于 hg 和 git 处理作者姓名和电子邮件的方式不同,如果您的原始 hg 存储库对作者有任何不同于Name <mail@example.com>-style 的内容,您将看到此问题。原因是 hg-git 试图将作者转换为严格的 git 样式(使用提到的名称-电子邮件对),如果不是这种情况,则会填写空白(参见 hg-git 自述文件中的解释:https ://bitbucket.org/durin42/hg-git)。

因此,原始 hg 存储库中变更集的作者可能与 git 存储库中的作者不完全相同;因此,从 git repo 创建的 hg repo 中的作者将与原始 hg repo 中的作者不匹配,例如:

  1. 原始 hg 存储库中的变更集 A 将作者设置为mail@example.com.
  2. 由于这不符合 git 的标准,因此 hg-git 会将其转换mail@example.com <mail@example.com>为 git repo 中的。
  3. 现在,当您将 git 存储库克隆到 hg 时,变更集的作者将为mail@example.com <mail@example.com>.

因为对于两个相关的存储库,初始提交应该完全匹配,即使哈希、提交消息、日期时间匹配,您也会收到“存储库无关”错误,但作者不同。经历相当痛苦(啊,现在我受到了惩罚,因为三年前我忘记正确设置作者了!)但完全合理。

于 2015-11-29T19:55:16.703 回答