相关或不相关位基本上来自两个存储库是否共享一个共同的根,即初始变更集。
为了强行拉动,你可以对移植或移植扩展做一些邪恶的事情,但这可能会产生连锁反应,而且你似乎对这种解决方案持反对态度——我也会反对!
要了解您遇到问题的原因,您需要了解一点 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-repo
Hg-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。.git
Hg-Git 然后在您的文件夹中创建一个隐藏文件.hg
夹,执行 Git 克隆,然后将 Git 存储库转换为 Mercurial 存储库;克隆完成后,它会调用hg update
,它直接在 Mercurial 存储库上运行,而不需要对 Git 存储库一无所知。
我怀疑这就是你的情况出了问题。当您从 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 gimport [git-repo-to-import-from-if-not-local-hidden]
然而,Hg-Git 提供了一种机制来手动强制在 repos 之间同步hg gexport
(默认导出到本地隐藏副本,如果需要创建它)。强制此同步还应该为您提供一种处理您注意到的问题的方法。您可以即使用 Git 将新的变更集拉入(或在 Git 术语中,fetch
-git pull
相当于hg pull --update
; git fetch
is hg pull
,这使得 Mercurial 提取扩展名的名称真的很不幸)到 Git 存储库中,然后使用hg gimport
将这些变更集导入到 Mercurial 存储库中。
现在,如果你做了诸如编辑历史之类的事情,那么所有的赌注都没有了。我不确定 Hg-Git 会如何处理这个问题——我怀疑它最终会创造双打。Mercurial 克隆中的新提交将添加到 Git,但删除的变更集仍在 Git 存储库中,并且可能会被导入回 Mercurial 存储库。(这是 Hg-Git 离线同步变更集的方法的直接结果。)在这种情况下,我建议选择一个规范的存储库,擦除所有克隆,并进行新的推送,并向所有克隆因这场混乱而无效的人道歉. (顺便说一句,这也是 Mercurial 社区对编辑历史如此谨慎的部分原因。)
潜在的解决方案
@EmilSit 建议您hg pull git+ssh://github.com/you/githubrepo.git
直接从规范(非 GitHub 克隆)Mercurial 存储库运行。假设 Hg-Git 创建初始 Git 克隆的方法完全与时间无关且具有确定性,这很有可能奏效。(后者几乎可以肯定是正确的,但我不确定前者,请参阅上面的文字了解更多详细信息。)
您可以执行此操作的本地变体:使用git clone ssh://github.com/you/githubrepo.git
获取本地纯 Git 克隆,然后执行hg pull ../githubrepo
. (这要求您安装了 Git。)Hg-Git 应该会自动启动并进行转换。转换还依赖于 Hg-Git 以一种确定的、与时间无关的方式进行转换。
您可以直接对原始混合存储库中隐藏的 Git 存储库进行操作。使用git fetch
(您可能首先必须先cd
进入.git
隐藏在文件夹中的.hg
文件夹)。然后运行hg gimport && hg update
以从 Git 存储库导入更改并更新。(您可能已经为隐藏的 Git 存储库指定了路径或路径。我怀疑您也可以指定 GitHub 路径。gimport
).
您可以使用各种愚蠢的移植方法——导出补丁系列等——并手动提交它们。如果您想在进行手动提交时给予其他开发人员信用,那么您可以使用该-u
选项在每次提交的基础上设置用户。
您可以使用移植或移植扩展进行智能移植。首先,使用 Hg-Git 对 GitHub 存储库进行新的 Mercurial 克隆。然后使用其中一个扩展将两个 Mercurial 存储库拉到一起。
至少其中一种非移植方法应该有效,因为除非 Hg-Git 在时间依赖性方面发挥作用,否则应该可以找到共同的根。即使找到了一个共同的根,您也可能会得到两个基本上重复的(未命名的)分支,然后您必须将它们重新合并在一起。