【问题标题】:Conceptually how is `git revert` related to three way merge? [closed]从概念上讲,`git revert` 与三路合并有何关系? [关闭]
【发布时间】:2019-06-17 12:50:20
【问题描述】:

我想了解git revert 如何使用三路合并 https://stackoverflow.com/a/37151159

假设当前分支是B,那么命令git revert C是否会创建一个提交D,所以BCD相对于@987654329的三路合并的结果@?

【问题讨论】:

    标签: git git-merge git-revert git-merge-conflict 3-way-merge


    【解决方案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:cherry-pick C2revert C2。你的当前提交是L,通过HEAD找到。 Git 现在继续进行三向合并操作。不寻常的是,合并基数,下面称为B,要么是C1,要么是C2,而另一个提交,称为R下面的 em> 也是 C1C2 — 以不是合并基础为准。对于git cherry-pickB = C1R = C2。对于git revertB = C2R = C1

    三路合并是如何工作的,简而言之但相当完整的形式

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

    • 有一个合并基础提交B
    • 有一个左侧或本地或--ours 提交Lgit mergetool 代码将其称为“本地”,但大多数 Git 命令只是将其称为 HEAD--ours
    • 有一个右手边或远程或--theirscommit Rgit mergetool 代码将其称为“远程”,而 git merge 本身使用 MERGE_HEAD

    从图中可以明显看出许多实际合并的合并基础:

              o--...--L   <-- our-branch (HEAD)
             /
    ...--o--B
             \
              o--...--R   <-- their-branch
    

    对于挑选或恢复,提交 BR 被强制执行某些特定的提交。例如,如果你运行git revert &lt;hash&gt;B 是你确定的提交,R 是它的父提交:

    ...--o--R--B--o--...--L   <-- our-branch (HEAD) 
    

    现在,有了三个提交 BLR——或者更确切地说,它们的哈希 ID——在手,Git 将,实际上,运行 两个 git diff 操作:

    • git diff --find-renames <em>B</em> <em>L</em>
    • git diff --find-renames <em>B</em> <em>R</em>

    第一个 diff 查找在底部和左侧之间不同的文件(包括跨越该间隙的任何重命名文件)。第二个 diff 查找基础和右侧之间不同的文件,同样包括任何重命名的文件。

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

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

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

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

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


    1Git 所谓的octopus merge 仍然是这样工作的,但是是迭代的,重复地将多个分支提示合并到索引中而不提交结果。这使它有点特别,因为ours 状态在索引中是only,而不是实际的提交。其他 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 只需提交冲突的文本。结果不是很漂亮。

    【讨论】:

      【解决方案2】:

      我不知道你是怎么想象的……但我是这样看的:

      git revert C 当你站在 B 上时,它要求 git 在 B 和 C 之间进行 3 路合并,假设(迫使 git 思考)两个修订版的 分支点都是 C。

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

      【讨论】:

      • 谢谢。什么是“分流点”?和merge base有关吗?
      • 希望现在更清楚了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-04-06
      • 1970-01-01
      • 1970-01-01
      • 2013-11-26
      • 1970-01-01
      • 1970-01-01
      • 2015-10-08
      相关资源
      最近更新 更多