“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存储库的结构。这里有两件有趣的事情:
Mercurial 在与 Git 远程通信时,对其进行的额外翻译基本上是毫不在意的。
有一个完整的 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 fetch
是
hg pull
,这使得Mercurial fetch扩展名真的很不幸)新的变更集到Git存储库中,然后使用
hg gimport
将这些变更集导入到Mercurial存储库中。
现在,如果您进行像编辑历史记录之类的操作,那么一切都不确定了。我不确定Hg-Git会如何处理这种情况-我怀疑它最终会创建重复提交。Mercurial克隆中的新提交将添加到Git中,但是已删除的变更集仍然存在于Git存储库中,并且可能会被重新导入到Mercurial存储库中。(这是Hg-Git离线同步变更集方法的直接结果)。在这种情况下,建议选择一个规范的存储库,擦除所有克隆版本,并对所有克隆版本被此混乱作废的人道歉后再进行新的推送。(顺便提一下,这正是为什么Mercurial社区对编辑历史如此谨慎的原因之一)
潜在解决方案
@EmilSit建议您直接从Mercurial存储库中运行hg pull git+ssh://github.com/you/githubrepo.git
。这有很大的机会能够工作,假设Hg-Git创建初始Git克隆的方法是完全独立于时间的并且确定性的。(后面这一点几乎可以肯定是正确的,但我不确定前面一点,请参见上面的文本了解更多细节)
你可以使用本地版本:使用git clone ssh://github.com/you/githubrepo.git
获取本地纯Git克隆,然后执行hg pull ../githubrepo
(这要求您已安装Git)。Hg-Git应自动启动并进行转换。转换也取决于Hg-Git以确定性、独立于时间的方式进行转换。
您可以直接操作原始混合存储库中隐藏的Git存储库。使用git fetch
(您可能需要先 cd
到在 .hg
文件夹中隐藏的 .git
文件夹),然后运行hg gimport && hg update
来导入Git存储库中的更改并更新。(您可能需要指定gimport
的路径-.
或隐藏Git repo的路径。我怀疑您也可以指定GitHub路径。)
您可以使用各种愚蠢的移植方法-导出补丁系列等-并手动提交它们。如果您想在手动提交时给其他开发人员信用,那么您可以使用-u
选项在每个提交的基础上设置用户。
您可以使用graft或transplant扩展进行智能移植。首先,使用Hg-Git对GitHub存储库进行新的Mercurial克隆。然后使用其中一种扩展程序将两个Mercurial存储库合并在一起。
至少一种非移植方法应该奏效,因为除非 Hg-Git 在时间上施展其魔力,否则应该能够找到共同的根。即使找到了共同的根,你可能最终会得到两个基本相似(未命名)的分支,然后必须将它们合并在一起。
hg pull git://github.com/you/githubrepo.git
命令? - Emil Sithg pull git+ssh://github.com/you/githubrepo.git
,如果我没记错的话。 - Liviushg pull -v --debug ../github-repo
的输出吗? - Emil Sit