你什么时候会使用不同的git合并策略?

Ott*_*tto 415 git merge git-merge

从git-merge的手册页中,您可以使用许多合并策略.

  • resolve - 这只能使用3向合并算法解析两个头(即当前分支和你从中拉出的另一个分支).它试图仔细检测纵横交错的合并模糊,并且通常被认为是安全和快速的.

  • 递归 - 这只能使用3向合并算法解析两个磁头.当有多个可用于3向合并的共同祖先时,它会创建共同祖先的合并树,并将其用作3向合并的参考树.据报道,这可以减少合并冲突,而不会因为从Linux 2.6内核开发历史记录中进行的实际合并提交而导致错误合并.此外,这可以检测和处理涉及重命名的合并.这是拉动或合并一个分支时的默认合并策略.

  • 章鱼 - 这解决了两个以上的案例,但拒绝进行需要手动解决的复杂合并.它主要用于将主题分支头捆绑在一起.这是拉动或合并多个分支时的默认合并策略.

  • 我们的 - 这解决了任意数量的头,但合并的结果始终是当前的分支头.它旨在用于取代侧枝的旧发展历史.

  • 子树 - 这是一个修改后的递归策略.当合并树A和B时,如果B对应于A的子树,则首先调整B以匹配A的树结构,而不是读取相同级别的树.这种调整也是对共同的祖先树进行的.

我什么时候应该指定不同于默认值的东西?哪些场景最适合?

Dus*_*tin 297

我不熟悉决心,但我用过其他人:

递归

递归是非快进合并的默认值.我们都熟悉那一个.

章鱼

当我有几棵树需要合并时,我已经使用了章鱼.你可以在大型项目中看到这一点,在这些项目中,许多分支机构已经进行了独立开发,并且它们已经准备好整合到一个单独

章鱼分支在一次提交中合并多个头,只要它可以干净地完成.

为了说明,假设您有一个具有master的项目,然后是三个要合并的分支(称为a,b和c).

一系列递归合并看起来像这样(注意第一次合并是快进,因为我没有强制递归):

一系列递归合并

但是,单个章鱼合并将如下所示:

commit ae632e99ba0ccd0e9e06d09e8647659220d043b9
Merge: f51262e... c9ce629... aa0f25d...
Run Code Online (Sandbox Code Playgroud)

章鱼合并

我们的

我们==我想拉另一个头,但扔掉所有引入的变化.

这样可以保留分支的历史记录而不会产生任何分支的影响.

(阅读:甚至没有看到这些分支之间的变化.分支刚刚合并,没有对文件进行任何操作.如果你想在另一个分支中合并,每次有问题"我们的文件版本或他们的版本"你可以使用git merge -X ours)

子树

当您想要将另一个项目合并到当前项目的子目录中时,子树很有用.当您拥有一个库时,您不希望将其包含为子模块.

  • 您不需要指定**章鱼**合并策略:如果您合并两个以上的分支(`git merge AB ...`),它会自动使用. (58认同)
  • 图像链接坏了 (5认同)
  • [gitg](https://wiki.gnome.org/action/show/Apps/Gitg)适用于linux环境下的用户. (4认同)
  • 这个"-X our"的提示非常棒,只为我节省了一个小时的工作量. (2认同)

Jak*_*ski 48

实际上,如果你想放弃分支带来的变化,你要选择的两个策略是我们的,但是将分支保留在历史记录中,如果要将独立项目合并到超级项目的子目录中(如'git-gui'in',则为子树) git'存储库).

合并两个以上的分支时会自动使用章鱼合并. 解决方法主要是出于历史原因,以及何时受到递归合并策略极端情况的影响.

  • @WattsInABox:自动选择所有其他人. (8认同)
  • 我没有对你投票,但你没有真正解释为什么这些是你想要选择的唯一策略.你刚才说它就像是事实. (5认同)
  • @SebTu:没有`theirs`合并策略(即`--strategy = theirs`),但默认的`recursive`合并策略有`theirs`选项(即`--strategy = recursive --strategy -option = theirs`,或者只是`-Xtheirs`). (2认同)

tha*_*smt 21

"解决"与"递归"合并策略

递归是当前默认的双头策略,但经过一些搜索后我终于找到了一些关于"解析"合并策略的信息.

取自O'Reilly的书籍版本控制与Git(亚马逊)(转述):

最初,"resolve"是Git合并的默认策略.

在交叉合并的情况下,有多个可能的合并基础,解决策略的工作方式如下:选择一个可能的合并基础,并希望最好.这实际上没有它听起来那么糟糕.事实证明,用户一直在处理代码的不同部分.在这种情况下,Git检测到它正在重新出现已经存在的一些更改并跳过重复的更改,从而避免冲突.或者,如果这些是导致冲突的轻微变化,至少冲突应该很容易让开发人员处理..

我使用默认递归策略失败的"resolve"成功合并了树.我收到了fatal: git write-tree failed to write a tree错误,感谢这篇博文(镜像),我试过"-s resolve",这很有用.我仍然不确定为什么......但我认为这是因为我在两棵树上都有重复的变化,并且正确地"跳过"了它们.


Von*_*onC 7

在 Git 2.30(2021 年第一季度)中,将有一个新的合并策略:ORT(“表面上递归的双胞胎”)。

git merge -s ort
Run Code Online (Sandbox Code Playgroud)

这来自Elijah Newren 的帖子:

现在,我将其称为“表面递归的双胞胎”,简称“ort”。> 首先,人们不应该注意到它和当前的递归策略之间有任何区别,除了我认为我可以让它更快一点(特别是对于大型回购)。

但它应该允许我修复一些(诚然是极端情况)在当前设计中更难处理的错误,并且我认为合并不会触及$GIT_WORK_TREE$GIT_INDEX_FILE将允许一些有趣的新功能。
无论如何,这就是希望。

问题

在理想的世界中,我们应该:

  • 要求unpack_trees()做“ read-tree -m”而不做“ -u”;

  • 在核心内进行所有合并递归计算并准备结果索引,同时保持当前索引不变;

  • 比较当前的核心索引和生成的核心索引,并注意到工作树中需要添加、更新或删除的路径,并确保更改反映到工作树时不会丢失信息树;
    例如,结果想要创建一个文件,其中工作树当前有一个包含非消耗内容的目录,结果想要删除一个工作树文件有本地修改的文件,等等;
    然后最后

  • 执行工作树更新,使其与生成的核心索引所示的内容相匹配。

结果:

请参阅Elijah Newren提交的提交 14c4586(2020 年 11 月 02 日)、提交 fe1a21d(2020 年 10 月 29 日)和提交 47b1e89提交 17e5574(2020 年 10 月 27 日)(由Junio C Hamano 合并 -- --提交 a1f9595中,2020 年 11 月 18 日)newren
gitster

merge-ort:新合并策略的准系统 API,具有空实现

签署人:伊利亚·纽伦

这是新合并策略的开始。

虽然存在一些 API 差异,并且实现在行为上也存在一些差异,但它本质上是作为merge-recursive.c.

然而,它的构建是为了与合并递归并存,这样我们就有足够的时间来了解这些差异在现实世界中是如何产生的,同时人们仍然可以回退到合并递归。
(另外,我打算避免在此过程中修改合并递归,以保持其稳定。)

这里值得注意的主要区别是工作树和索引的更新不是与合并算法同时完成的,而是一个单独的后处理步骤。
新 API 的设计使得人们可以进行重复合并(例如,在 rebase 或cherry-pick 期间),并且仅在最后更新一次索引和工作树,而不是使用每个中间结果更新它。

此外,可以在两个分支之间执行合并,这两个分支都不匹配索引或工作树,而不会破坏索引或工作树。

和:

请参阅提交 848a856提交 fd15863提交 23bef2e、提交c8c35f6提交 c12d1f2提交 727c75b提交 489c85f提交 ef52778提交 f06481f(2020 年 10 月 26 日),作者:Elijah Newren ( newren)
(由Junio C Hamano 合并 -- gitster--提交 66c62ea中,2020 年 11 月 18 日)

t6423, t6436:注意改进了脏文件的 ort 处理

签署人:伊利亚·纽伦

“递归”后端依赖于unpack_trees()检查未暂存的更改是否会被合并覆盖,但unpack_trees()不理解重命名 - 一旦返回,它已经向工作树和索引写入了许多更新。
因此,“递归”必须进行特殊的四向合并,其中还需要将工作副本视为额外的差异源,我们必须小心避免覆盖并导致将文件移动到新位置以避免冲突。

相比之下,“ort”后端在内存中进行完整的合并,并且仅更新索引和工作副本作为后处理步骤
如果有脏文件挡住,它可以简单地中止合并。

t6423:预计 ort 后端的冲突标记标签会得到改进

签署人:伊利亚·纽伦

冲突标记带有 REF-OR-COMMIT:FILENAME 形式的额外注释,以帮助区分内容的来源,:FILENAME如果历史记录双方相同,则该部分将被保留(因此,只有内容冲突的重命名才带有内容)注释的那部分)。

然而,在某些情况下:FILENAME,由于合并递归的每个代码路径需要所有特殊情况代码的副本格式,注释被意外遗漏。

t6404, t6423:预计 ort 后端的重命名/删除处理得到改进

签署人:伊利亚·纽伦

当文件被重命名并且存在内容冲突时,合并递归在索引中没有旧文件名的某些阶段和新文件名的某些阶段;相反,它将与旧文件名相对应的所有阶段复制到新文件名的相应位置,以便存在三个与新文件名相对应的更高阶阶段。

以这种方式进行操作可以使用户更轻松地访问不同版本并解决冲突(无需手动“” git rmman 版本以及“ git addman新版本)。

重命名/删除应该以类似的方式处理——重命名的文件应该有两个阶段,而不仅仅是一个阶段。
我们现在不想破坏合并递归的稳定性,因此而是更新相关测试,根据是否使用“ recursive”或“ ”合并策略来获得不同的期望。ort


使用 Git 2.30(2021 年第一季度),准备新的合并策略。

请参阅提交 848a856提交 fd15863提交 23bef2e、提交c8c35f6提交 c12d1f2提交 727c75b提交 489c85f提交 ef52778提交 f06481f(2020 年 10 月 26 日),作者:Elijah Newren ( newren)
(由Junio C Hamano 合并 -- gitster--提交 66c62ea中,2020 年 11 月 18 日)

merge tests:期望ort中改进目录/文件冲突处理

签署人:伊利亚·纽伦

merge-recursive.cunpack_trees()是建立在跑步然后“做一些小的修饰”以获得结果的想法之上的。
不幸的是,unpack_trees()它是在“即用即更新”模式下运行的,导致merge-recursive.c效仿并最终得到立即评估和“即用即修”设计。

有些事情(例如目录/文件冲突)在索引数据结构中不能很好地表示,并且需要特殊的额外代码来处理。
但后来发现目录/文件冲突也可能涉及重命名/删除冲突,必须将特殊的目录/文件冲突处理代码复制到重命名/删除代码路径中。
...然后必须复制它以进行修改/删除,以及重命名/重命名(1to2)冲突,...但它仍然错过了一些。
此外,当发现还存在文件/子模块冲突和子模块/目录冲突时,我们需要将特殊的子模块处理代码复制到整个代码库中的所有特殊情况。

然后发现我们对目录/文件冲突的处理不是最理想的,因为它会创建未跟踪的文件来存储冲突文件的内容,如果有人运行 ' ' git merge --abort( man )' git rebase --abort' ,这些文件将不会被清除男人

考虑到索引中的目录/文件冲突,尝试添加或删除与这些文件对应的索引条目也很困难或可怕。
但是,更改merge-recursive.c以正确处理这些问题是一个巨大的痛苦,因为代码中有很多站点具有相似但不相同的代码来处理目录/文件/子模块冲突,所有这些都需要更新。

我努力通过单个代码路径推动 merge-ort 中的所有目录/文件/子模块冲突处理,并避免创建用于存储跟踪内容的未跟踪文件(它确实在备用路径上记录内容,但确保它们具有更高阶的阶段)在索引中)。


使用 Git 2.31(2021 年第一季度),“正确完成”的合并后端开始出现。
例子:

请参阅Junio C Hamano ( )的提交 6d37ca2(2020 年 11 月 11 日)。 请参阅提交 89422d2提交 ef2b369提交 70912f6提交 6681ce5、提交9fefce6 、提交 bb470f4 、提交 ee4012d 、提交 a9945bb提交8adffaa 提交6a02dd9 提交291f29c 提交98bf984提交34e557a 提交885f006提交 d2bc199提交 0c0d705提交c801717提交 e4171b1提交 231e2dd提交 5b59c3d(2020 年 12 月 13 日),作者:Elijah Newren ( )(由Junio C Hamano 合并 -- --提交 f9d29da,2021 年 1 月 6 日)gitster
newren
gitster

merge-ort:添加实现record_conflicted_index_entries()

签署人:伊利亚·纽伦

之后checkout(),工作树具有适当的内容,并且索引与工作副本匹配。
这意味着所有未修改且干净合并的文件都具有正确的索引条目,但需要更新冲突的条目。

我们通过循环冲突的条目,用 标记路径的现有索引条目CE_REMOVE,在索引末尾添加为路径暂存的新的更高顺序(忽略正常索引排序顺序),然​​后在循环末尾添加新的更高顺序的条目来实现此目的删除CE_REMOVED-marked缓存条目并对索引进行排序。


在 Git 2.31(2021 年第一季度)中,重命名检测已添加到“ORT”合并策略中。

请参阅提交 6fcccbd提交 f1665e6提交 35e47e3提交 2e91ddd提交 53e88a0提交 af1e56c(2020 年 12 月 15 日)和提交 c2d267d提交 965a7bc提交 f39d05c提交 e1a124e提交 864075e (1 2020 年 12 月 4 日)作者:Elijah Newren ( newren)
(由Junio C Hamano 合并 -- gitster--提交 2856089中,2021 年 1 月 25 日)

例子:

merge-ort:添加正常重命名处理的实现

签署人:伊利亚·纽伦

实施正常重命名的处理。
此代码替换以下代码merge-recurisve.c

  • RENAME_NORMAL相关的代码process_renames()
  • 的情况RENAME_NORMALprocess_entry()

此外,还有一些用于merge-recursive.c多个不同重命名案例的共享代码,我们在本案例(或其他重命名案例)中不再需要这些代码:

  • handle_rename_normal()
  • setup_rename_conflict_info()

通过设计的改变,可以将四个独立的代码路径合并为一个:process_renames()调整conflict_info其中的条目opt->priv->paths,以便process_entry()可以正交地处理所有非重命名冲突类型(目录/文件、修改/删除等)。

这意味着我们不太可能错过某种冲突类型组合的特殊实现(请参阅66c62ea引入的提交(“合并分支 'en/merge-tests'”,2020-11-18,Git v2.30.0) -rc0 --批处理 #6中列出的合并),特别是提交 ef52778(“合并测试:期望在 ort 中改进目录/文件冲突处理”,2020-10-26,Git v2.30.0-rc0 --批处理#中列出的合并6)了解更多详情)。

  • @UndefinedBehavior 确实如此。将于下周一发布!(2021 年 11 月 15 日) (2认同)