Lei*_*ell 1 git git-merge merge-conflict-resolution
我删除了功能分支中的一个文件,因为我在其他地方重构了它的代码。
开发分支已更改此文件中的代码以修复错误。
我将开发分支合并到我的功能分支中,以在继续开发该功能的同时保持其最新状态。
我收到相关文件的“已被我们删除”冲突。
如何获取开发分支上文件更改的差异,以便我可以将这些更改重新实现到功能分支上的重构代码中?
您想要查看索引槽 #1,并将其与索引槽 #3 中的路径进行比较:
\n\ngit diff :1:path :3:path\nRun Code Online (Sandbox Code Playgroud)\n\n您还可以使用提取各种索引槽版本,git checkout-index然后使用普通文件操作(而不仅仅是使用 Git 工具)检查它们。该git mergetool程序执行后者,因此如果您使用,git mergetool您将拥有该文件的两个版本。(我从不使用git mergetool自己。)
根据定义,任何合并都具有三个输入。三个输入是commits 1,其中包含文件的快照,因此通常这三个输入会生成每个文件的三个版本。这三个文件版本在索引中结束了这些编号的槽:也就是说,对于path/to/file.ext正在合并的某些文件,有 a :1:path/to/file.ext、 a:2:path/to/file.ext和 a :3:path/to/file.ext。(当\xe2\x80\x94如本例中的\xe2\x80\x94其中一个分支删除文件,这三个条目之一不存在。)
让我们按倒序列出索引槽,因为最后一个是这里的关键:
\n\n插槽 3(他们的)保存着他们的文件,位于分支的顶端develop:你跑了git merge develop。请记住,每次提交都是所有文件的完整快照。这不是一组更改,而只是一个快照。但不知何故,Git 知道他们更改了某些特定文件。改变了,改变了什么?
插槽 2(我们的)保存您当前提交的文件(也称为HEAD)。在这种情况下,其中的某些文件HEAD已被删除。但是:Git 如何知道你删除了该文件?删除了,针对什么?
插槽 1(基础)保存来自合并基础的文件。这就是这两者的内容。
合并基础是(单个/最佳)公共提交,您的不同分支提示就是从该提交开始的。Git 将此提交\xe2\x80\x94 作为一个整体进行比较,实际上\xe2\x80\x94 与两个分支提示提交中的每一个(作为一个整体)进行比较,以便匹配文件。2 然后,在匹配了三个提交中的所有文件后,Git 将开始整个“将一些文件放入特殊插槽”的过程。
\n\n也就是说,如果我们在启动 时绘制提交图git merge,它看起来像这样\xe2\x80\x94,尽管确切的细节会有所不同,并且通常该图很难扫描找到B并且R(HEAD或者提交L通常非常困难)容易找到):
o--...--L <-- our-branch (HEAD)\n /\n...--A--B\n \\\n o--...--R <-- develop\nRun Code Online (Sandbox Code Playgroud)\n\n提交A和B(以及早于 A 的所有内容)都在两个分支上,因此是共享的,但它B是最好的,因为它是最后一个共享的。
为了进行合并,Git 比较了BvsL来看看我们改变了什么,又比较了BvsR来看看他们改变了什么。没有人更改的文件在所有三个提交中都是相同的,因此 Git 使用此类文件的任何版本。对于只有他们更改的文件,Git 从 commit 中获取该文件的版本R。对于只有我们更改的文件,Git 从 commit 中获取该文件的我们的版本L。
对于我们都更改的文件,在某种程度上 \xe2\x80\x94 包括“完全删除文件”\xe2\x80\x93Git 必须更加努力。现在,了解索引\xe2\x80\x94(Git 从中进行新提交\xe2\x80\x94)在合并过程中具有扩展的作用变得很重要。
\n\n通常,索引对于每个文件只有一个槽位。该槽已编号,但它是零号槽,因此您通常不需要执行任何特殊操作来引用它:您只需告诉 Git 将文件从工作树复制到索引中,使其成为索引准备好提交。git add somefilesomefile
不过,对于合并情况,我们和他们都对文件做了一些操作,Git 需要三个\xe2\x80\x94well,每个文件最多三个 \xe2\x80\x94 副本。因此,对于这种特殊情况,Git 将提交的文件的合并基础B版本放入索引槽 #1 中。Git 将我们的文件版本(来自提交L且已位于索引槽 #0 中)移动到索引槽 #2 中,并将文件的其他版本从提交中放入R索引槽 #3 中。
对于已删除的文件(这种特殊情况),Git 将槽 #2 或 #3 留空,具体取决于删除文件的人。对于添加/添加冲突,\xe2\x80\x94 中文件不存在,B但两者都存在L,并且R\xe2\x80\x94Git 将插槽 #1 留空。(不存在删除/删除冲突这样的事情:如果我们都删除了该文件,Git 只会删除该文件并继续前进。但是有一些重命名情况,这比较棘手。)
当合并因合并冲突而停止时,这些索引槽仍填充有文件的三个版本(在本例中为两个版本)。因此,您可以检查索引,查看更高阶段(非零)槽,并了解哪些文件存在冲突。各种 Git 工具(包括git status和git diff)都可以执行此操作。
当你通过任何方式修复了冲突\xe2\x80\x94后\xe2\x80\x94,你必须告诉Git清除较高阶段的槽并将文件的良好副本放入索引槽#0中。最简单的方法是使用git add正确的文件版本。(如果没有正确的版本\xe2\x80\x94,如果它应该消失\xe2\x80\x94,则可以将该git rm文件从所有索引槽和工作树中删除。一般来说,如果它不在work-tree,git add也将其从索引中删除,尽管我习惯于git rm删除应该消失的冲突文件,所以我还没有测试git add在这里删除更高阶段的条目是否一致。如果它\不在工作树中,git rm将其从索引中删除,抱怨它不在工作树中,然后一切都很好。)
1在偷偷摸摸的特殊情况下,可以合并尚未提交的文件。例如,这会在使用git checkout -m或 时发生。git stash apply在这种情况下,Git 通常只是根据需要将项目从槽 0 移动到槽 2……而工作树中但从未提交的更改可能会被期望槽 #2 的工具破坏和/或丢失。成为工作树中内容的安全副本!(这是我不喜欢的原因之一git stash。)但是运行git merge不会调用这个奇怪的路径,事实上,如果您的存储库未处于良好的准备启动合并状态,它会抱怨并中止。
2这就是重命名检测的用武之地。如果您在提交中重命名了某个文件,那么现在可能是 或。这些被检测为同一个文件,尽管它现在有两个不同的名称。这里有一个小缺陷,因为在合并的整个过程中插槽没有链接在一起。例如,如果合并因冲突而停止,则很难恢复与 相关的事实。Git打印信息,因此它可能仍然在您可以在笔记本电脑或其他设备上看到的窗口中,但不会在其他地方记录。:1:path/to/file.ext:2:path/different/file.ext:2:path/to/different.ext2:path/to/different.ext:1:path/to/file.ext
这是带有一些重复合并的分支的示例图:
\n\n...--A--B--C--D--G--H--K <-- branch1\n \\ \\ \\\n E-----F--I--J--L <-- branch2\nRun Code Online (Sandbox Code Playgroud)\n\n这里,F和J都是合并提交,有两个父级:和F的两个父级,以及和的两个父级。如果您运行,您将尝试进行新的合并提交,其第一个父级是,第二个是。这里的合并基础是提交,因为从 开始并向后工作,我们得到,而从 开始并向后工作,我们同时得到和 然后\xe2\x80\x94,就像 \xe2\x80\x94 到和,并且我们已经到了EDJIHgit checkout branch2; git merge branch1MLKHKHLJIHH顶部,那就是合并基地。
请注意,合并基础计算是对称的。如果我们这样做git checkout branch1 && git merge branch2,Git 仍然选择H作为合并基础。但是,如果合并基础不明显,您可以运行:
git merge-base --all branch1 branch2\nRun Code Online (Sandbox Code Playgroud)\n\n这将产生“最佳合并基础”的所有候选者。
\n\n理想情况下,只有一个。然而,历史看起来像这样:
\n\n...--o--o---A--... <-- branch1\n \\ /\n X\n / \\\n...--o--o---B--... <-- branch2\nRun Code Online (Sandbox Code Playgroud)\n\n其中A和B都是与两个分支提示“同样接近”的合并提交,有两个合并基础。这是通过从branch1 合并到branch2 并立即执行从branch2 到branch1 的合并(git merge --no-ff有时称为纵横合并)来实现的。
在这个模糊的合并基本情况中,没有单一的最佳候选者。由于无论如何合并通常都是对称的,所以提交和此处的内容很可能是相同的。在这种情况下,选择 Git 中的哪一个并不重要。但如果内容不同,那就很重要了,这就是 Git 的递归合并的用武之地。当有多个合并基础时,Git 首先会作为内部合并,合并两个合并基础(使用它们的最佳共同祖先) ,无论是什么)并根据结果进行新的提交。Git 将使用新提交的内容作为外部合并的合并基础。ABAB
合并策略-s resolve(不是默认的)选择两者之一,A或B,以明显随机的方式(实际上,只是算法中更方便的)。如果A和B具有相同的内容,则效果很好;如果不是,则递归“首先合并 A 和 B”的内部合并可能会产生更好的结果。如果递归合并有冲突,它会产生一些混乱。(通常明智的做法是避免交叉合并。)