我试图从https://stackoverflow.com/a/37151159git revert
了解如何使用三路合并
假设当前分支是B
,command 是否git revert C
创建了一个提交D
,那么这是和的B
三向合并的结果?C
D
C~
我试图从https://stackoverflow.com/a/37151159git revert
了解如何使用三路合并
假设当前分支是B
,command 是否git revert C
创建了一个提交D
,那么这是和的B
三向合并的结果?C
D
C~
git revert
所做的是用一个异常选择的合并基础进行三向合并。这与所做的相同git cherry-pick
,只是选择的合并基础不同。
在所有情况下,我们都可以画出提交图:
...--o--o--C1--C2--...--o <-- somebranch
\
o--o--L <-- our-branch (HEAD)
或者:
...--o--C1--C2--o--...--L <-- our-branch (HEAD)
或类似的(画出你的图表的样子)。
你告诉 Git:cherry-pickC2
或revertC2
。您当前的提交是L
,通过HEAD
. Git 现在继续进行三向合并操作。不寻常的是,下面称为B的合并基础是or ,而下面称为R的另一个提交也是or ——无论哪个不是合并基础。对于, B =和R = 。对于, B =和R = 。C1
C2
C1
C2
git cherry-pick
C1
C2
git revert
C2
C1
所有 Git 合并都以相同的方式实现。1 我们从三个提交开始2 :
--ours
提交L。代码将其git mergetool
称为“本地”,但大多数 Git 命令只是将其称为HEAD
或--ours
.--theirs
提交R。git mergetool
代码称它为“远程”,而它git merge
本身使用MERGE_HEAD
.从图中可以明显看出许多实际合并的合并基础:
o--...--L <-- our-branch (HEAD)
/
...--o--B
\
o--...--R <-- their-branch
对于挑选或恢复,提交B和R被迫进行某些特定的提交。例如,如果你运行git revert <hash>
,B是你确定的提交,R是它的父提交:
...--o--R--B--o--...--L <-- our-branch (HEAD)
现在,有了三个提交B、L和R ——或者更确切地说,它们的哈希 ID——在手,Git 实际上将运行两个 git diff
操作:
git diff --find-renames B L
git diff --find-renames B R
第一个 diff 查找基础和左侧之间不同的文件(包括跨越该间隙的任何重命名文件)。第二个差异查找基础和右侧之间不同的文件,同样包括任何重命名的文件。
任何一方未更改的任何文件在所有三个提交中都是相同的。合并结果是所有三个提交共享的该文件的(单个)版本。
任何仅在一侧更改的文件, Git 都会从该侧获取文件的版本。
任何双方都更改但内容相同的文件, Git可以获取L或R副本。这两个副本在定义上是相同的,所以 Git 选择一个(实际上总是L,因为它更方便——Git 直接在索引中完成所有这些工作,这可以避免将L文件从插槽 0 中移出! )。
最后,对于双方更改的任何文件, Git 会尝试(可能成功,也可能失败)合并两组更改。合并的更改将应用于来自基本提交B的文件副本。如果合并成功,那就是合并的结果。否则,Git 会尽最大努力在工作树中进行合并,并因合并冲突而停止。3 添加-X ours
或-X theirs
告诉 Git:不要因冲突而停止,而是通过从 diff 中选择 ours 或 theirs hunk 来解决此冲突。 请注意,这是唯一的实际上必须填充文件的三个索引槽的情况,然后调用低级合并代码(或您的合并驱动程序.gitattributes
,如果您设置了一个)。
成功的结果会自动提交为 的合并提交,或或git merge
的普通提交,除非您告诉 Git 不要提交结果。失败的(由于冲突)合并停止,在索引和工作树中留下一团糟,您必须清理它们。git cherry-pick
git revert
1 Git所谓的章鱼合并仍然是这样工作的,但是是迭代的,重复地将多个分支提示合并到索引中而不提交结果。这使它有点特别,因为ours
状态仅在索引中,而不是实际提交。其他 Git 命令通常会检查索引和HEAD
提交是否匹配,只是git cherry-pick -n
简单git revert -n
地使用索引,就像它是提交一样,就像章鱼合并一样。在上面的主要答案文本中,您可以将索引的内容视为ours
提交:在内部,Git 只是将所有阶段 0 的条目转移到阶段 2 以实现这一点。
2git merge -s recursive
对于由or调用的递归合并git merge
,Git 首先为您找到合并基础。这可能会出现不止一个提交。如果确实发生了这种情况,Git 会使用(的内部版本)合并合并基础git merge
。这是“递归合并”的递归部分:合并合并基础以便提出单个提交。那个单一的提交就是最外层的合并基础git merge
。
如果您使用git merge -s resolve
,并且 Git 找到多个合并基,Git 会选择一种更简单的方法:它会随机(看起来)选择一个合并基(它不是真正随机的——它只是从它的合并基查找算法——但它没有被仔细控制;没有内在的理由更喜欢任何一个候选合并基而不是其他任何一个)。
3对于在合并两个合并基础时发生的递归(内部)合并期间的合并冲突,Git 只需提交冲突的文本。结果不是很漂亮。
我不知道您是如何将其可视化的……但这就是我的看法:
git revert C
当您站在 B 上时,要求 git 在 B 和 C 之间进行 3 路合并〜假设(迫使 git 思考)两个修订版的分支点都是 C。
定义:在正常情况下,分支点是两个分支历史上存在的最后一个修订版本。在那次修订之后,两个分支的历史不再共享另一个共同的祖先。