总结评论并解决所提出的问题......
寻找合并基地
-
Git 使用一种算法来计算一对提交的合并基础,该算法用于查找有向无环图的最低公共祖先。精确的算法没有在任何地方被描述,并且可能会改变,只要新的算法产生正确的结果。也可以看看在有向无环图中找到最低共同祖先的算法? https://stackoverflow.com/q/14865081/1256452
可能有多个 LCA。在这种情况下,-s resolve
合并策略选择其中之一。你无法控制它选择哪一个。这-s recursive
合并策略运行git merge
在他们身上,一次两个,就好像通过以下内容:
commits=$(git merge-base --all $left $right)
if len($commits) > 1
a=$commits[0]
for i in range(1, len(commits))
b=$commits[i]
a=$(git-merge-recursively-inner $a $b)
rof
commits=($a)
fi
(以伪代码形式)。请注意,内部递归合并本身可能会找到多个合并基;如果是,则使用此算法来合并它们。
最终结果是一次提交,$commits[0]
。这是合并基础。
无论如何,现在我们有了一个单一的合并基础提交——来自仅找到一个 LCA 的 LCA 查找算法,或者通过合并递归合并来自 LCA 查找算法的多个合并基础,或者通过合并-解决方法只是从列表中选择一个提交——我们可以看看如何git merge-(recursive|resolve)
实际上合并文件。它必须运行两个内部git diff
操作,每个操作都打开重命名检测器。
差异和文件身份/重命名检测
A 文件差异引擎比较两个文件。我们将一个文件放在左侧,另一个文件放在右侧。当两个文件匹配时,差异没有说明任何内容。当两个文件不同时,差异引擎(取决于它的好坏)会提出一些我们可以应用的更改,以使左侧的内容与右侧文件的内容匹配。
区分一对commits,Git 将一个放在左边,一个放在右边。然后它必须配对files在这两个提交中。 Git 可以在启用或不启用重命名检测器的情况下执行此操作。
当没有重命名检测器时,图片非常清晰。当且仅当左侧和右侧的文件具有相同的内容时,它们才是“同一文件”name。添加重命名检测器识别(标记为“相同”)差异左侧和右侧的某些文件,即使names已改变。
Git 现有的重命名检测器正在进行一些更改以使其变得更好。这里不需要确切的细节:我们需要知道的是,它会说某些文件被重命名,“相同”的文件也是如此,即使它们具有不同的名称。其他文件自动成为“相同”文件,因为它们具有相同的名称。
对于每个配对文件,差异引擎会生成一组更改,使左侧文件变成右侧文件。重命名检测器生成需要首先执行的重命名操作。文件是new右边的被称为added,以及左侧提交中存在但右侧提交中不存在的文件将被删除。
因此,提交对的差异导致:
- 要重命名的文件(从旧名称到新名称)
- 要添加的文件
- 要删除的文件
根据需要,加上两次提交中存在的文件的一些更改集。
合并,给定合并基础
给定一个合并基础提交,解析和递归都以相同的方式进行:
- 比较合并基础
HEAD
,启用重命名检测。这些都是our变化。
- 将合并基础与其他提交进行比较,并启用重命名检测。这些都是their变化。
- 合并更改。
“组合”需要解决单个文件中的高级更改(例如重命名、添加和删除)和低级更改。将应用组合更改的文件是来自合并基地。这保证了结果在所有情况下都有效。
例如,假设we重命名一个文件并they修改了我们重命名的文件。合并后的变化实际上表明,最后,将文件base.ext重命名为head.ext;同时,更改base.ext的第17行。因此,我们将更改第 17 行,并重命名该文件,捕获这两个操作。
高级操作可能会发生冲突!例如,如果我们重命名一个文件而他们删除它,那就是高级冲突。如果我们和他们都重命名文件,则会发生冲突,除非我们都选择相同的最终名称。如果我们和他们都删除一个文件,这与显而易见的结果结合得很好。
低水平的变化也可能发生冲突。如果我们和他们都以不同的方式修改同一行,或者如果我们的更改和他们的更改在任一边缘“接触”,就会发生冲突。例如,如果我们替换第 9 行和第 10 行(在第 8 行之后删除 2 行并在第 8 行之后插入 2 行),并且它们替换第 11 行和第 12 行,则我们的更改是相邻的。出于一般谨慎,称此为冲突。
当然,如果我们和他们能够same更改为same原来的台词,不冲突。 Git 只是获取这些更改的一份副本。
扩展选项-Xours
or -Xtheirs
通过选择一方(我们的或他们的)来解决低级冲突,而忽略另一方。这仅适用于低级别冲突。从逻辑上讲,它也可以适用于高层冲突,但事实并非如此。
结合我们和他们的所有更改后,Git 将应用combined对在中找到的快照进行的更改合并基地犯罪。如果没有冲突,生成的文件可以自动提交。这是这些合并的默认操作;使用--no-commit
抑制此默认提交。
当 merge-recursive 使用内部合并进行合并基础提交时,它会强制提交结果即使存在合并冲突。您无法看到它对这些冲突做了什么,除非合并库中显示的任何内容your(外部)合并也有冲突。 (在这种情况下,文件的基于合并的副本在索引槽 1 中可用。另外,如果您设置merge.conflictStyle
to diff3
,冲突文件的每个工作树副本都将显示合并库中的文本,并带有冲突标记。)