强化冲突合并算法

强化冲突合并算法,第1张

强化冲突合并算法

您的担心是对的:Git不懂任何语言,它的内置合并算法严格基于时线比较。 您不必使用此内置的合并算法
,但是大多数人都可以这样做,因为(a)它大部分都可以工作,并且(b)没有太多选择。

注意,这取决于您的 合并策略

-s
参数);以下文字是默认
recursive
策略。该
resolve
策略与
recursive
;
octopus
策略不仅适用于两次提交;而
ours
策略则完全不同(并且与完全不同
-Xours
)。您还可以使用
.gitattributes
和“合并驱动程序”
为特定文件选择替代策略或算法。而且,这都不适用于Git决定认为是“二进制”的文件:对于这些文件,它甚至不尝试合并。(我不会在这里讨论任何内容,而是默认
recursive
策略如何处理文件。)

如何
git merge
工作(使用默认时
-s recursive
  • 合并以两次提交开始:当前一次(也称为“我们的”,“本地”和
    HEAD
    ),以及一些“其他”一次(也称为“其”和“远程”)
  • 合并查找这些提交之间 的 合并基础
    • 通常,这只是另一个提交:隐含分支1联接的第一点上的提交
    • 在某些特殊情况下(多个合并基础候选对象),Git必须发明一个“虚拟合并基础”(但在这里我们将忽略这些情况)
  • 合并运行两个差异:
    git diff base local
    git diff base other
    • 这些已打开重命名检测
    • 您可以自己运行这些diff来查看合并内容

您可以将这两个差异视为“我们做了什么”和“他们做了什么”。 合并的 目的 “我们所做的”和“他们所做的” 结合起来
差异是基于行的,来自最小编辑距离算法2,实际上只是Git 对我们做了什么以及他们做了什么的 猜测

一个 diff(base-vs-
local)的输出告诉Git哪些基础文件与哪些本地文件相对应,即,如何从当前提交到基础遵循名称。然后,Git可以使用基本名称在其他提交中发现重命名或删除。在大多数情况下,我们可以忽略重命名和删除问题以及新文件创建问题。请注意,默认情况下,Git2.9版会为 所有
差异(不仅是合并差异)打开重命名检测。(您可以通过配置

diff.renames
为来在Git早期版本中启用此功能
true
;另请参见的
gitconfig
设置
diff.renameLimit
。)

如果 仅在一侧 (基础到本地或基础到另一方)更改了文件,则Git会简单地进行这些更改。混帐只有当一个文件时,改变做了三路合并 两个 方面。

为了执行三向合并 ,Git本质上遍历了两个差异(基础到本地和基础到另一个),一次比较一个“差异块”,比较更改的区域。如果每个块都影响原始基础文件的
不同部分 ,则Git会接受该块。如果某些块影响基本文件的 一部分,则Git会尝试获取该更改的一份副本。

例如,如果本地更改显示为“添加一条右括号”,而远程更改显示为“添加(在同一位置,相同的缩进)封闭括号”,则Git将仅获取该副本的一个副本。如果两个都说“删除右括号”,Git只会删除该行一次。

仅当两个差异 发生冲突时
,例如,一个说“添加一个缩进的括号内缩进12个空格”,另一个说“添加一个闭合的括号内缩进11个空格”,Git才会声明冲突。默认情况下,Git将冲突写入文件中,显示两组更改-
如果设置

merge.conflictstyle
diff3
显示 文件的基于合并基础的版本中的代码

任何不冲突的差异大块,Git均适用。如果存在冲突,Git通常会使文件处于“冲突合并”状态。但是,两个

-X
参数(
-X ours
-Xtheirs
)对此进行了修改:使用
-X ours
Git在冲突中选择“我们的” diff大块,然后将该更改放入,而忽略“其”更改。使用
-Xtheirs
Git选择“他们的”差异块,然后将更改放入,而不考虑“我们的”更改。这两个
-X
参数保证Git最终不会声明冲突。

如果Git能够自行解决此文件的所有问题,它就可以做到:您将在工作树和索引/临时区域中获取基本文件,以及本地更改以及其他更改。

如果Git不能自行解决所有问题,它将使用三个特殊的非零索引插槽将文件的基础版本,其他版本和本地版本放入索引/临时区域。工作树版本始终是“
Git能够解决的问题,以及各种可配置项指示的冲突标记”。

每个索引条目都有四个插槽

像这样的文件

foo.java
通常在插槽0中暂存。这意味着现在就可以进行新提交了。根据定义,其他三个插槽为空,因为存在零插槽条目。

在发生冲突的合并期间,插槽零保留为空,并且插槽1-3用于保存合并的基本版本,“本地”或

--ours
版本以及另一个或
--theirs
版本。工作树保存正在进行的合并。

您可以

git checkout
用来提取任何这些版本,或
git checkout -m
重新创建合并冲突。所有成功的
gitcheckout
命令都会更新文件的工作树版本。

一些

git checkout
命令使各个插槽不受干扰。一些
gitcheckout
命令写入插槽0,清除插槽1-3中的条目,以便文件可以提交。(要知道哪些人在做什么,您只需要记住它们即可。在很长一段时间内,我就把它们弄错了。)

git commit
在清除所有未合并的插槽之前,您无法运行。您可以
git ls-files--unmerged
用来查看未合并的插槽,也可以查看
git status
更人性化的版本。(提示:使用
git status
。请经常使用!)

成功合并并不意味着好的代码

即使

git merge
成功将所有内容自动合并,也不意味着结果正确!
当然,当它因冲突而停止时,这也意味着Git无法自动合并所有内容,而不是它自己自动合并的内容是正确的。我喜欢设置
merge.conflictstyle
为,
diff3
以便在合并之前将“基本”代码替换为合并之前,可以看到Git认为
基本 是什么。经常发生冲突是因为diff选择了错误的基数(例如某些匹配的大括号和/或空行),而不是因为必须存在实际的冲突。

至少在理论上,使用“耐心”差异可能会导致基本选择不佳。我自己还没有尝试过。 Git
2.9中新的“压缩启发式”很有希望,但是我也没有尝试过。

您必须始终检查和/或测试合并的结果。 如果已经提交了合并,则可以编辑文件,构建和测试

git add
更正的版本,并用于
git commit--amend
推开先前的(错误的)合并提交,并使用相同的父项进行不同的提交。(该
--amend
部分
git commit--amend
是虚假广告它不会改变当前的承诺本身,因为它。 不能,而是它与同一个新的提交 的父类标识
为当前犯,而不是使用目前的常规方法提交的ID作为新提交的父级。)

您也可以禁止自动提交与的合并

--no-commit
。在实践中,我对此几乎没有什么需要:大多数合并大多只是工作
git show-m
而已,并且快速眼球和/或“它可以编译并通过单元测试”就可以发现问题。但是,在发生冲突或
--no-commit
合并时,简单的方法
gitdiff
会为您提供组合的差异(提交合并后,
gitshow
如果不带
-m
,则为相同的排序),这可能会有所帮助,也可能会更加令人困惑。您可以运行更特定的
gitdiff
命令和/或检查三个(基本,本地,其他)插槽条目

看看Git会看到什么

除了

diff3
用作之外
merge.conflictstyle
,您还可以看到将要看到的差异
git merge
。您所需要做的就是运行两个
gitdiff
命令-将要运行的两个命令
git merge

为此,您必须找到(或至少告诉

git diff
要找到) 合并基础 。您可以使用
git merge-base
,从字面上找到(或所有)合并基础并打印出来:

$ git merge-base --all HEAD foo4fb3b9e0570d2fb875a24a037e39bdb2df6c1114

这表示在当前分支和分支之间

foo
,合并基础是提交
4fb3b9e...
(并且只有一个这样的合并基础)。然后
git diff 4fb3b9eHEAD
,我可以跑步和
git diff 4fb3b9e foo
。但是有一种更简单的方法,只要我可以 假设 只有一个合并基础:

$ git diff foo...HEAD   # note: three dots

这将告诉

git diff
(且
gitdiff
)在
foo
和之间找到合并基础
HEAD
,然后比较该提交(该合并基础)以进行提交
HEAD
。和:

$ git diff HEAD...foo   # again, three dots

做同样的事情,发现

HEAD
和之间的合并基数
foo
-““合并基数””是可交换的,因此它们应该与其他方法相同,例如7 + 2和2 +
7均为9,但这次将合并基数与提交
foo
。1个

(对于其他命令(不是这样

git diff
的命令),三点语法会产生 对称的差异
:位于两个分支但不在两个分支上的所有提交的集合。对于具有单个合并基础提交的分支,这是“每个提交 后,
合并的基础上,对每个分支“:换句话说,这两个分支的联合,不包括合并基础本身和任何早期提交对于具有多个合并基地分支,这个减去了。 所有
的合并基准站的。
git diff
我们只是假设只有一个合并基数,我们将其用作差异的左侧或“之前”,而不是将其及其祖先相减。


1在Git中,分支 名称 标识一个特定的提交,即分支的 尖端
。实际上,这就是分支的实际工作方式:一个分支名称命名一个特定的提交,然后为了向该分支添加另一个提交- 分支 在这里意味着 提交链
-Git做出了一个新的提交,其父作为当前的分支提示,然后将分支名称指向新提交。“分支”一词可以指分支名称,也可以指整个提交链。我们应该根据上下文找出哪一个。

在任何时候,我们都可以命名一个特定的提交,并通过将该提交 及其所有祖先
:其父代,其父代的父代等等,将其视为分支。在此过程中,当我们执行合并提交时(与两个或多个父项进行的提交),我们将接受 所有
父项提交,以及他们父母的父母,依此类推。

2该算法实际上是可选的。默认

myers
值基于EugeneMyers的算法,但是Git还有其他一些选择。



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5561307.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-14
下一篇 2022-12-14

发表评论

登录后才能评论

评论列表(0条)

保存