相关或不相关位基本上来自两个存储库是否共享公共根,即初始变更集。
为了强行拉动,你可以对移植物或移植延伸物做一些邪恶的事情,但这可能会产生连锁反应,而且你似乎反对这样的解决方案 - 我也会!
要了解出现问题的原因,您需要了解一些 Hg-Git 的工作原理。
Hg-Git 的工作原理
Tl;dr
真正的问题是 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 https://www.samba.org/%7Ejelmer/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://[email protected] /cdn-cgi/l/email-protection/user/some-git-repo
Hg-Git 第一次在本地文件夹中创建一个新的隐藏 Git 存储库,然后使用 Git 协议进行通信并将更改推送到远程。从此,两个存储库之间的通信应该没有问题了——从根节点和父子关系的初始转换,就可以实现两个存储库的变更集之间的一对一映射。 (这并不是 100% 正确,特别是如果您使用 Git 或 Mercurial 的更高级、更惯用的功能,但现在就足够了。)我很确定 Hg-Git 跟踪的信息比这多一点,如果没有其他原因,只是为了通过连续的推拉来加快速度。因此,当您从 Mercurial 克隆开始时,您的“原始根”就是 Mercurial 根,并且会根据需要创建和维护 Git 存储库。
现在,如果您不是从本地 Mercurial 克隆开始,而是从远程 Git 克隆开始,那么您实际上会结束从 Git 克隆创建一个 Mercurial– “proto-root”是 Git 根。更准确地说,当你跑步时hg clone ssh+git://[email protected] /cdn-cgi/l/email-protection/user/some-git-repo
,Mercurial 启动,检查以确保它可以与远程连接(可以在 Hg-Git 的帮助下实现),然后创建目录并调用必要的扩展,即 Hg-Git。然后 Hg-Git 创建一个隐藏的.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-Git 提供了一种通过以下方式手动强制存储库之间同步的机制:hg gimport [git-repo-to-import-from-if-not-local-hidden]
and hg gexport
(默认导出到本地隐藏副本,如果需要则创建它)。强制此同步还应该为您提供一种处理您注意到的问题的方法。您可以使用 Git 来拉取(或者用 Git 术语来说,fetch
– git pull
相当于hg pull --update
; git fetch
is hg pull
,这使得 Mercurial fetch 扩展的名字真的很不幸)将新的变更集放入 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 存储库导入更改并更新。 (您可能已指定路径gimport
- 任何一个.
或隐藏的 Git 存储库的路径。我怀疑你也可以指定 GitHub 路径。)
-
您可以使用各种哑移植方法(导出补丁系列等)并手动提交它们。如果您想在手动提交时给予其他开发人员信用,那么您可以使用-u
选项以每次提交为基础设置用户。
-
您可以使用以下任一方式进行智能移植graft http://www.selenic.com/hg/help/graft or 移植 https://mercurial-scm.org/wiki/TransplantExtension扩展。首先,使用 Hg-Git 对 GitHub 存储库进行新的 Mercurial 克隆。然后使用这些扩展之一将两个 Mercurial 存储库合并在一起。
至少一种非移植方法should之所以有效,是因为除非 Hg-Git 的魔力依赖于时间,否则应该有可能找到一个共同的根。即使找到了共同的根,您也可能会得到两个基本上重复的(未命名的)分支,然后您必须将它们合并在一起。