从概念上讲,'git revert'与三路合并有何关系?

时间:2019-01-23 21:01:03

标签: git

我正在尝试了解git revert如何从 https://stackoverflow.com/a/37151159

假设当前分支为B,命令git revert C是否创建提交D,因此B是{{1}的三向合并的结果}和C相对于D吗?

2 个答案:

答案 0 :(得分:1)

我不知道你如何看待它...但是我是这样看的:

git revert C,当您站在B上时,要求git在B和C〜之间进行三向合并,假设(迫使git认为)两个修订版的分支点为C。

定义:分支点在正常情况下,这是两个分支的历史记录中存在的最后一个修订。修订后,两个分支的历史不再共享另一个共同祖先。

答案 1 :(得分:1)

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:樱桃选择C2 还原C2 。您的当前提交是L,可通过HEAD找到。 Git现在继续执行三向合并操作。与此不同的是,以下称为 B merge base C1C2,而另一个提交是 R 也为C1C2-并非合并基础。对于git cherry-pick B = C1 R = C2。对于git revert B = C2 R = C1

三路合并的工作方式,简而言之但相当完整

所有Git合并都以相同的方式实现。 1 我们从三个提交开始 2

  • 有一个合并基础提交 B
  • 存在左侧或本地提交或--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

第一个差异查找在底部和左侧之间不同的文件(包括该间隙中所有重命名的文件)。第二个差异查找的是底部和右侧之间不同的文件,同样包括任何重命名的文件。

任何在 端均未更改的文件在所有三个提交中均相同。合并结果是所有三个提交共享的文件的(单个)版本。

任何仅在一个一侧进行过更改的文件,Git都会从该侧获取文件的版本。

任何在双方上都更改过但内容相同的文件,Git可以使用 L R 复制。根据定义,这两个副本是相同的,因此Git选择一个(实际上总是 L ,因为它更方便-Git直接在索引中完成所有这些工作,并且这避免了移动 L 文件首先从插槽0移出!)。

最后,对于在两端进行了任何更改的文件,Git都会尝试-可能成功或失败-组合两个更改集。合并的更改将应用​​于来自基本提交 B 的文件副本。如果合并成功,那就是合并的结果。否则,Git会尽最大努力在工作树中进行合并,并因合并冲突而停止。 3 添加-X ours-X theirs会告诉Git:而不是停止发生冲突时,请通过从差异中选择我们的请求来解决此冲突。请注意,这是 only 情况,实际上必须填充文件的三个索引槽,并且然后调用低级合并代码(或设置为.gitattributes的合并驱动程序)。

成功的结果会自动由git merge提交为合并提交,或者由git cherry-pickgit revert作为普通提交,除非您告诉Git不要提交结果。失败(由于冲突)合并将停止,在索引和工作树中留下一团糟,您必须清除它们。


1 Git所谓的章鱼合并仍然可以像这样工作,但是它是迭代的,反复将多个分支提示合并到索引中,而没有提交结果。这使得它有点特殊,因为在索引中ours状态仅是 ,而不是实际的提交。其他Git命令通常检查索引和HEAD提交是否匹配,除了git cherry-pick -ngit revert -n只是使用索引就像是提交一样,章鱼合并的方式相同。在上面的主要答案文本中,您可以将索引的内容视为ours提交:在内部,Git只是将所有处于阶段0的条目都移到了阶段2来实现此目的。

2 对于git merge -s recursivegit merge调用的递归合并,Git首先为您查找合并基础。这可能会出现多个提交。如果发生这种情况,Git将使用git merge(的内部版本)合并合并基础。这是“递归合并”的递归部分:合并合并基础,以便提出单个提交。这样,该一次提交就是最外面的git merge的合并基础。

如果您使用git merge -s resolve,并且Git找到多个合并基础,则Git选择一种更简单的方法:它以(看起来像)随机(不是真正随机)选择一个合并基础。哪一个最容易从其合并基础查找算法中得出,但并没有受到仔细控制;没有内在的理由偏爱任何一个候选合并基础。

3 对于在合并两个合并库时发生的递归(内部)合并期间的合并冲突,Git只会提交冲突的文本。结果不是很漂亮。