从Github克隆hg-git时出现“abort: repository is unrelated”的错误提示。

9
我有一个项目,其主要(Mercurial)存储库位于SourceForge上,但是在Bitbucket(Mercurial)和Github(Git)上也有克隆版本。
现在我一直在使用hg-git将Mercurial存储库推送到Github,根据我对该过程的理解,一些元数据会保留在Mercurial存储库中。
现在,当重新克隆Bitbucket存储库并重新克隆Github存储库时,如果我发出hg pull ../github-repo,我会得到:
pulling from ../github-repo
searching for changes
abort: repository is unrelated

为什么会出现这种情况,我该如何让Mercurial认识到它们确实有关联?或者我必须依赖于原始存储库,也就是我最初推送到Github的那个存储库?我仍然拥有它,但是假设我丢失了它,除了手动更改变更集移植之外,还有什么选择?
注意:由于拉取请求,Github存储库已更改(新的变更集)。但是SourceForge和Bitbucket存储库仍然相互关联。现在的任务是从Github Git存储库中拉取变更集到本地存储库,并将其分别推送回SourceForge和Bitbucket。

1
你能否在新的Mercurial bitbucket克隆中从内部执行hg pull git://github.com/you/githubrepo.git命令? - Emil Sit
@EmilSit +1,但应该是 hg pull git+ssh://github.com/you/githubrepo.git,如果我没记错的话。 - Livius
@EmilSit:不行。两个Hg仓库之间的所有交互都正常,但是当您尝试从Git仓库中拉取(最初基于Hg仓库)时,它无法使用,并显示上述错误消息。 - 0xC0000022L
你能包含 hg pull -v --debug ../github-repo 的输出吗? - Emil Sit
2个回答

10

“related”或“non-related”的区别基本上来自两个存储库是否共享一个公共根,即初始变更集。

为了强制拉取,您可以使用移植或移植扩展名进行某些恶意操作,但这可能会产生连锁反应,并且您似乎不愿意采用这种解决方案——我也是!

要理解为什么会出现问题,您需要了解一些Hg-Git的工作原理。

Hg-Git的工作原理

简而言之

实际问题在于Hg-Git基本上动态创建一个新仓库。因此,两个仓库之间没有关联,就像hg convert some-existing-hg-repo的产物与原始仓库没有关联一样。你到目前为止还没注意到这点,因为Hg-Git也会在另一个方向上这样做——当你从Mercurial仓库开始时,它会创建必要的Git仓库。当你第一次克隆到GitHub时,在他们的服务器上创建了一个裸的Git仓库,从所有意义上来说,它与每个仓库都有关联。因此,你推送由Hg-Git创建的新Git仓库是相关的,一切都能正常工作,没有问题。之后,你从同一个仓库推送,所以也没有问题——Hg-Git跟踪本地Git和Hg仓库之间的关系,因此你的关系得以保持。但是,当你重新开始时,你创建了一个新的Git和/或Hg仓库(取决于你的操作方向),联系就被断开了。
Hg-Git的工作原理是创建一个隐藏的Git存储库,并建立Git提交和Hg提交之间的对应关系。Hg-Git是一座双向桥梁,即它能够接收Git提交并生成Hg提交,反之亦然。Hg-Git通过使用Python编写的Git库(dulwich)并将其作为扩展程序链接到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-repo 时,Hg-Git在本地文件夹中创建一个新的隐藏Git存储库,然后使用Git协议通信并将更改推送到远程。从那时起,您应该没有在两个存储库之间通信的问题 - 从根节点和父子关系的初始转换开始,可以实现两个存储库之间变更集的一对一映射。(如果您使用更高级、惯用的Git或Mercurial功能,则这并不完全正确,但现在足够了。)Hg-Git跟踪的信息比这多一点,我非常确定,这是为了加快连续推送和拉取的速度。因此,当您从Mercurial克隆开始时,“proto-root”是Mercurial root,并根据需要创建和维护Git存储库。

现在,如果您不是从本地Mercurial克隆开始,而是从远程Git克隆开始,则实际上会从Git克隆中创建一个Mercurial克隆 -“proto-root”是Git根目录。更准确地说,当您运行hgclone ssh + git://git@github.com/user/some-git-repo 时,Mercurial启动,检查以确保它可以与远程接口(在Hg-Git的帮助下),然后创建目录并调用必要的扩展名(s),即Hg-Git。然后,Hg-Git在您的 .hg 文件夹中创建隐藏的 .git 文件夹,执行Git克隆,然后将Git repo转换为Mercurial repo;完成克隆后,它调用 hg update ,该命令直接在Mercurial repo上运行,而无需了解Git repo。

此处出错的原因我猜测是这样的。当你在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-Git提供了一种机制,可以通过 hg gimport [git-repo-to-import-from-if-not-local-hidden]hg gexport(默认导出到本地隐藏副本,如果需要则创建)手动强制同步存储库之间的同步。 强制进行此同步也应该为你提供处理已注意到问题的方法。你可以使用Git拉取(或在Git术语中,fetch - git pull等效于hg pull --update; git fetchhg pull,这使得Mercurial fetch扩展名真的很不幸)新的变更集到Git存储库中,然后使用 hg gimport 将这些变更集导入到Mercurial存储库中。
现在,如果您进行像编辑历史记录之类的操作,那么一切都不确定了。我不确定Hg-Git会如何处理这种情况-我怀疑它最终会创建重复提交。Mercurial克隆中的新提交将添加到Git中,但是已删除的变更集仍然存在于Git存储库中,并且可能会被重新导入到Mercurial存储库中。(这是Hg-Git离线同步变更集方法的直接结果)。在这种情况下,建议选择一个规范的存储库,擦除所有克隆版本,并对所有克隆版本被此混乱作废的人道歉后再进行新的推送。(顺便提一下,这正是为什么Mercurial社区对编辑历史如此谨慎的原因之一)

潜在解决方案

  1. @EmilSit建议您直接从Mercurial存储库中运行hg pull git+ssh://github.com/you/githubrepo.git。这有很大的机会能够工作,假设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 到在 .hg 文件夹中隐藏的 .git 文件夹),然后运行hg gimport && hg update来导入Git存储库中的更改并更新。(您可能需要指定gimport的路径-.或隐藏Git repo的路径。我怀疑您也可以指定GitHub路径。)

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

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

至少一种非移植方法应该奏效,因为除非 Hg-Git 在时间上施展其魔力,否则应该能够找到共同的根。即使找到了共同的根,你可能最终会得到两个基本相似(未命名)的分支,然后必须将它们合并在一起。

3
我想补充一下,当你将一个hg repo推到git时,甚至在从hg克隆git repo并尝试从原始hg repo中拉取更改时,您甚至可能会遇到“与仓库无关”的错误。由于现在我们在本地创建了一个hg repo,它是从创建自原始hg repo的git repo创建的,因此我认为本地和原始hg repo应该是相关的,但有时却不是。

由于hg和git处理作者名称和电子邮件的方式不同,如果您的原始hg repo中的作者和Name <mail@example.com> -style 不同,则会出现此问题。原因是hg-git尝试将作者转换为严格的git-style(使用上述名称-电子邮件对),如果不是这种情况,它将填充空白(请参见hg-git's Readme中的说明:https://bitbucket.org/durin42/hg-git)。

因此,可能会发生原始hg repo中更改集的作者与git repo中的不完全相同的情况;结果,从git repo创建的hg repo中的作者将与原始hg repo中的作者不匹配,例如:

  1. 原始hg repo中的更改集A将作者设置为mail@example.com
  2. 由于这不符合git的标准,hg-git将在git repo中将其转换为mail@example.com <mail@example.com>
  3. 现在,当你将git repo克隆到hg时,更改集的作者将是mail@example.com <mail@example.com>

由于两个相关的repo的初始提交应完全匹配,即使哈希值、提交消息、日期时间相同,但作者不同,您也会遇到“与仓库无关”的错误。这种经历可能很痛苦(啊,现在我因为三年前忘记正确设置作者而被惩罚了!),但完全合理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接