随着 Git 2.30(2021 年第一季度),将会有一个新的合并策略:ORT(“ Ostensibly Recursive's Twin ”)。
git merge -s ort
这来自以利亚纽伦的这个帖子:
现在,我称它为“表面上递归的双胞胎”,或简称为“ort”。> 起初,人们不应该注意到它与当前递归策略之间的任何区别,除了我认为我可以让它更快一点(尤其是对于大回购)。
但它应该允许我修复一些在当前设计中更难处理的(不可否认的极端情况)错误,我认为合并不会触及$GIT_WORK_TREE
或$GIT_INDEX_FILE
允许一些有趣的新功能。
无论如何,这就是希望。
问题:
在理想的世界中,我们应该:
要求unpack_trees()
做“ read-tree -m
”没有“ -u
”;
在内核中执行所有合并递归计算并准备结果索引,同时保持当前索引不变;
比较当前的in-core index和生成的in-core index,注意工作树中需要添加、更新或删除的路径,保证变化反映到工作树时不丢失信息树;
例如,结果想要创建一个文件,其中工作树当前有一个目录,其中包含不可消耗的内容,结果想要删除工作树文件具有本地修改的文件等;
然后最后
执行工作树更新以使其与生成的核心索引所显示的内容相匹配。
结果:
请参阅Elijah Newren ( ) 的提交 14c4586( 2020 年 11 月 2 日)、提交 fe1a21d(2020 年 10 月 29 日)和提交 47b1e89、提交 17e5574 (newren
2020 年 10 月 27 日)。
(由Junio C Hamano 合并 -- gitster
--在提交 a1f9595中,2020 年 11 月 18 日)
merge-ort
:具有空实现的新合并策略的准系统 API
签字人:以利亚·纽伦
这是新合并策略的开始。
虽然存在一些 API 差异,并且实现在行为上也存在一些差异,但它本质上是作为merge-recursive.c
.
但是,它被构建为与合并递归并存,因此我们有足够的时间来找出这些差异在现实世界中是如何出现的,而人们仍然可以退回到合并递归。
(另外,我打算避免在此过程中修改合并递归,以保持稳定。)
这里值得注意的主要区别是工作树和索引的更新不是与合并算法同时完成的,而是一个单独的后处理步骤。
新的 API 被设计成可以重复合并(例如在 rebase 或cherry-pick 期间),并且只在最后更新一次索引和工作树,而不是用每个中间结果更新它。
此外,可以在两个分支之间执行合并,这两个分支都不匹配索引或工作树,而不会破坏索引或工作树。
和:
请参阅Elijah Newren ( ) 的提交848a856、提交fd15863、提交23bef2e、提交 c8c35f6、提交 c12d1f2、提交 727c75b、提交 489c85f、提交 ef52778、提交 f06481f(2020 年 10 月 26 日)。(由Junio C Hamano 合并——在提交 66c62ea中,2020 年 11 月 18 日)newren
gitster
签字人:以利亚·纽伦
“递归”后端依赖于unpack_trees()
检查未暂存的更改是否会被合并覆盖,但unpack_trees()
不理解重命名 - 一旦它返回,它已经向工作树和索引写入了许多更新。
因此,“递归”必须进行特殊的 4 路合并,它还需要将工作副本视为额外的差异来源,我们必须小心避免覆盖并导致将文件移动到新位置以避免冲突。
相比之下,“ort”后端在内存中进行完整的合并,并且仅在后处理步骤中更新索引和工作副本。
如果途中有脏文件,它可以简单地中止合并。
t6423
:期望在 ort 后端改进冲突标记标签
签字人:以利亚·纽伦
冲突标记带有 REF-OR-COMMIT:FILENAME 形式的额外注释,以帮助区分内容的来源,:FILENAME
如果历史的双方相同,则忽略该片段(因此只有带有内容冲突的重命名带有注释的那一部分)。
但是,在某些情况下:FILENAME
,由于合并递归的每个代码路径需要所有特殊情况代码格式的副本,注释被意外遗漏了。
签字人:以利亚·纽伦
当文件被重命名并且有内容冲突时,merge-recursive 在索引中没有旧文件名的一些阶段和新文件名的一些阶段;相反,它将与旧文件名对应的所有阶段复制到新文件名的相应位置,因此三个更高阶阶段都对应于新文件名。
这样做使用户更容易访问不同的版本并解决冲突(无需手动' git rm
' (man)旧版本以及' git add
' (man)新版本)。
重命名/删除应该以类似方式处理——重命名文件应该有两个阶段,而不仅仅是一个阶段。
我们现在不想破坏合并递归,因此更新相关测试以根据“ recursive
”或“ ort
”合并策略是否正在使用而具有不同的期望。
使用 Git 2.30(2021 年第一季度),为新的合并策略做准备。
请参阅Elijah Newren ( ) 的提交848a856、提交fd15863、提交23bef2e、提交 c8c35f6、提交 c12d1f2、提交 727c75b、提交 489c85f、提交 ef52778、提交 f06481f(2020 年 10 月 26 日)。(由Junio C Hamano 合并——在提交 66c62ea中,2020 年 11 月 18 日)newren
gitster
签字人:以利亚·纽伦
merge-recursive.c
建立在跑步的想法上unpack_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,提交7681CE5,提交9fefce6,提交BB470F4,提交EE4012D,提交A9945BB,Commit 8ADFFAA,提交6A02DD9,Commit 291F29C,提交98BF984,Commit 34E557A,提交D2BC199,提交0C0D705,提交0C0D705,提交D2BC199,提交885595BB,Commit 8ADFFCE6,Commit 9fefce6,Commit 9fefce6,Commit 9fefce6,Commit 9fefce6,Commit 9fefce6,Commit 。 c801717gitster
,提交 e4171b1,提交 231e2dd,提交 5b59c3d(2020 年 12 月 13 日),作者Elijah Newren ( newren
)。
(由Junio C Hamano 合并 -- gitster
--在提交 f9d29da中,2021 年 1 月 6 日)
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(14 月 14日)(由Junio C Hamano 合并 -- --在提交 2856089中,2021 年 1 月 25 日)newren
gitster
例子:
签字人:以利亚·纽伦
实现对正常重命名的处理。
此代码替换以下内容merge-recurisve.c
:
RENAME_NORMAL
与in相关的代码process_renames()
- 的
RENAME_NORMAL
情况process_entry()
此外,还有一些来自merge-recursive.c
多个不同重命名案例的共享代码,我们将不再需要这种情况(或其他重命名案例):
handle_rename_normal()
setup_rename_conflict_info()
通过改变设计将四个独立的代码路径合并为一个成为可能:process_renames()
调整其中的conflict_info
条目opt->priv->paths
,以便process_entry()
可以正交处理所有非重命名冲突类型(目录/文件、修改/删除等)。
这意味着我们不太可能错过某种冲突类型组合的特殊实现(参见66c62ea引入的提交(“Merge branch 'en/merge-tests'”,2020-11-18,Git v2.30.0 -rc0 --批处理中列出的合并#6),尤其是提交 ef52778(“合并测试:期望在 ort 中改进目录/文件冲突处理”,2020-10-26,Git v2.30.0-rc0 --批处理中列出的合并# 6)了解更多详情)。
这与让工作树/索引更新在merge_switch_to_result()
函数中正交处理一起,极大地简化了各种特殊重命名情况的代码。
(公平地说,处理正常重命名的代码之前并没有那么复杂,但现在它仍然简单得多。)
而且,仍然使用 Git 2.31(2021 年第一季度),使用 Git 2.31(2021 年第一季度),oRT 合并策略学习了对合并冲突的更多支持。
请参阅Elijah Newren ( ) 提交的 4ef88fc 、commit 4204cd5、commit 70f19c7 、 commit c73cda7、commit f591c47、commit 62fdec1、commit 991bbdc、commit 5a1a1e8、commit 23366d2、commit 0ccfa4e (01 Jan 2021 )。(由Junio C Hamano 合并——在提交 b65b9ff中,2021 年 2 月 5 日)newren
gitster
merge-ort
: 在同一路径添加对不同类型文件的处理
签字人:以利亚·纽伦
添加一些明确考虑以下类型冲突的处理:
- 文件/子模块
- 文件/符号链接
- submodule/symlink> 将它们作为冲突留在同一路径上对于用户来说很难解决,因此将它们中的一个或两个移到一边,以便他们各自获得自己的路径。
请注意,在递归处理(即
call_depth > 0
)的情况下,我们可以只使用两个合并基的合并基作为合并结果,就像我们对修改/删除冲突、二进制文件、冲突子模块值等所做的那样。