以mercurial(或git)编程预合并

时间:2018-02-27 18:26:58

标签: git merge mercurial

我们经常会遇到一些常见的,无信息的合并冲突,特别是在合并回归测试输出时。我希望能够以编程方式忽略与某些模式匹配的行(例如,"如果行或区域与此正则表达式匹配,只需从其他&#34中获取补丁;) ,但是我仍然希望交出一个交互式合并工具来处理文件中的其他冲突(如果有的话)(无需手动重新合并这些第一行。)

如果我预先合并任何工具(如:merge3或我可能设计的任何工具),然后再与其他工具合并,它似乎总是从头开始。有没有办法去保存我的工作"对于我已经解决的冲突?

我们使用mercurial,但如果git中的流程类似,请告知我们。

1 个答案:

答案 0 :(得分:2)

Git中的进程不同,但您应该能够在任一系统中实现所需。

TL; DR是你必须编写自己的合并工具(或合并驱动程序,在Git中)。此合并工具应比较三个输入文件并执行您想要的任何合并,然后使用一组新的基本和输入文件运行普通的低级合并驱动程序。

首先,请注意Mercurial有自己的定义" premerge"。有关更完整的说明,请参阅https://www.mercurial-scm.org/wiki/MergeToolConfiguration。我将你的问题解释为根本不涉及这种预合并;相反,您想要编写Mercurial所指的合并工具。 (Git称之为合并驱动程序。)

让我们有一些定义,以便我们都同意术语。运行hg mergegit merge时,选择两个要合并的特定提交。一个是您当前的提交,我们将其称为本地,因为Mercurial使用该名称。出于同样的原因,我们将调用另一个提交其他。 (Mercurial有时会调用第二个远程,但主要是在内部.Git可变地调用这些--ours--theirs,或本地远程,或本地其他:Git对于保持一致并不是一件好事。)

您签出本地提交并运行hg merge othergit merge other,Mercurial或Git将找到合并基础提交,它们都称为合并基础或只是基地。

在两个系统中,所有三个有趣的提交都表示为快照:以下是基本提交的文件。在本地提交中,这些文件是相同的(可能是一些新的,一些删除的,一些重命名的)文件。这是与其他提交相同的文件。 嘿,先生 Tambourine Man 版本系统,为我合并。

高级合并

VCS必须做的第一件事就是将每个base-commit文件与每个本地提交文件和每个其他提交文件配对。在整个文件(创建,重命名或删除)上可能有两个这样的操作。特别是,如果文件已在一次或两次提交中重命名,则VCS必须处理此问题。如果在一次或两次提交中删除了文件,则VCS必须处理该文件。如果在两个提交中都创建了基础中不存在的文件,则VCS也必须处理该文件。其中一些是直截了当的:如果文件 F 在两个提交的一个中重命名,那么,我们只是在最终结果中重命名它,否则像往常一样组合更改。但是其他变化相互冲突:VCS使用哪个名称,如果两个提交都重命名了文件?我将这些冲突称为高级冲突。 1

在这里,Git比Mercurial更具优势。 Mercurial让你立即为每个高级别冲突选择一个解决方案,以便它知道每个文件的最终命运。 2 Git让你推迟这个决定(尽管底层存在问题)实现,在Git中)。我稍后会提到更多相关内容。这部分,你不能在Mercurial中很好地自动化(或者至少我最后一次尝试不能)。幸运的是,对于这两种VCS来说,这种冲突往往很少见。

低级别合并

现在我们知道了所有文件的命运(或者已经在Git中推迟了这个决定),Mercurial特别会使用你选择的工具(通过--tool$HGMERGE)来合并每个文件。我称之为低级别合并,以区别于配对文件和确定其名称的高级过程。 this answerHow does Mercurial merge internally?

概述了此低级别合并流程

请记住,我们有三个输入:base,local和other。最终合并将有两个父母:本地和其他。我们可以将每个低级文件视为"已更改"在父母任何一方,与基地相比。或者,该文件可以与父母中的任何一个或两个中的文件相同。如果文件根本没有被触及 - 如果它在所有三个提交中完全相同 - 那么无事可做:最终文件应该与基本文件匹配。如果该文件仅在本地或其他地方的一个中进行了修改,则相对于基础中的内容 - 那么 无所事事,真的;我们可以使用修改后的那个。

如果文件在父母的中以完全相同的方式进行了更改,那么我们使用哪一个父母并不重要。但请注意,this wiki page表示此流程适用:

  

对于父母双方都改变的每个文件......

在这里,有一些微妙之处,值得看看Git和Mercurial之间的另一个区别。

在Git中,当我们有三个来自B(基本),L(本地)和O(其他)的文件时,我们真正拥有的是三个哈希ID 。哈希ID唯一标识内容,因此我们可以立即告知哪些文件匹配,哪些文件不匹配。如果L = O,则父母双方都拥有相同版本的文件,我们只选择其中一个版本,无论B(两者都做了相同的更改,或者没有做出任何更改)。否则,如果B = L或B = O,我们采用匹配的那个,因为那是具有变化的父级。否则(B≠L,B≠O,L≠O)我们必须进行真正的合并。

Mercurial不会按散列ID存储文件。相反,它知道文件是否在从B到L的提交的某个地方被改变,并且在从B到O的提交的某个地方被改变。所以它只是看看是否父母和提交序列修改了B以来的文件。

所有这一切的结果是,在Git中,只有当所有三个输入不同时,您的合并驱动程序才会运行。在Mercurial中,如果父母双方都触及该文件,您的合并工具就可以运行,但是两个甚至全部三个输入可能匹配。在大多数情况下,这没有区别,但请记住特殊的角落情况。 Mercurial内置于合并前(你不会谈论的那个)为你处理这个特殊情况,所以除非你禁用预合并,否则你实际上看不到< / em>它。

当您的合并驱动程序运行时,您将三个输入和输出文件的名称传递给它,如我在第一个链接到Mercurial wiki的示例中所示:

mymergetool.args = $local $other $base -o $output

(这来自您的.hgrc或同等产品)。在Git中,除了您在.gitconfig或类似内容中定义合并驱动程序之外,它是相似的:

driver = filfre %O %A %B

然后从.gitattributes文件引用此驱动程序,三个输入文件中的一个也是输出文件(有关详细信息,请参阅the gitattributes documentation)。

您的合并工具/合并驱动程序必须读取三个输入文件,并使用它来计算和编写正确的输出文件 - 一步,如版本控制系统所示。您可以根据需要在内部中使用任意数量的步骤。完成后,如果输出文件是完全合并的正确结果,则应退出状态为0;如果合并需要手动编辑或进一步工作,则应退出非零(通常为1)。

在您的情况下,您将分析差异,自己组合一些更改,创建三个输入文件,并在文件上运行一些其他文件合并工具。 Mercurial似乎没有一个好的方法来运行自己的内部低级文件合并; this wiki page建议使用GNU diff3来完成工作,并包含一个运行diff3的脚本,如果它指示冲突,请在生成的文件上运行vi或其他编辑器-with-冲突。

Git包含git merge-file命令,该命令对任意三个输入文件进行三向合并(事实上,您可以直接从Mercurial使用git merge-file)。请注意,如果git merge-filediff3能够成功合并文件,则{{1}}和{{1}}都已退出,如果不能,则为非零。

1 Mercurial允许在此处选择不同的合并算法,并且有多种产品:请参阅Consensus MergeBid Merge。 Git也允许使用不同的合并算法,它称之为策略,它的默认值是它所谓的递归策略。它可以在这个高级别的合并阶段选择文件来配对低级合并过程,或者在Git的递归合并 - 构造文件的情况下,在某些情况下。

2 源代码表明Mercurial可以将此推迟到以后,保存合并状态下的高级别冲突。我没有继续戳它,但却无法找到办法做到这一点。