【发布时间】:2019-06-17 12:50:20
【问题描述】:
我想了解git revert 如何使用三路合并
https://stackoverflow.com/a/37151159
假设当前分支是B,那么命令git revert C是否会创建一个提交D,所以B是C和D相对于@987654329的三路合并的结果@?
【问题讨论】:
标签: git git-merge git-revert git-merge-conflict 3-way-merge
我想了解git revert 如何使用三路合并
https://stackoverflow.com/a/37151159
假设当前分支是B,那么命令git revert C是否会创建一个提交D,所以B是C和D相对于@987654329的三路合并的结果@?
【问题讨论】:
标签: git git-merge git-revert git-merge-conflict 3-way-merge
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 C2 或 revert C2。你的当前提交是L,通过HEAD找到。 Git 现在继续进行三向合并操作。不寻常的是,合并基数,下面称为B,要么是C1,要么是C2,而另一个提交,称为R下面的 em> 也是 C1 或 C2 — 以不是合并基础为准。对于git cherry-pick,B = C1 和 R = C2。对于git revert,B = C2 和 R = C1。
所有 Git 合并都以相同的方式实现。1我们从三个提交开始2:
--ours 提交L。 git mergetool 代码将其称为“本地”,但大多数 Git 命令只是将其称为 HEAD 或 --ours。--theirscommit 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 <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-pick 或git revert 的普通提交,除非你告诉Git 不要提交结果。失败的(由于冲突)合并停止,在索引和工作树中留下一团糟,您必须清理它们。
1Git 所谓的octopus merge 仍然是这样工作的,但是是迭代的,重复地将多个分支提示合并到索引中而不提交结果。这使它有点特别,因为ours 状态在索引中是only,而不是实际的提交。其他 Git 命令通常检查索引和 HEAD 提交是否匹配,除了 git cherry-pick -n 和 git revert -n 只是简单地使用索引 好像 它是一个提交,就像章鱼合并一样.在上面的主要答案文本中,您可以将索引的内容视为ours 提交:在内部,Git 只是将所有阶段 0 的条目转移到阶段 2 来实现这一点。
2对于由git merge -s recursive 或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。
定义:分支点在正常情况下是两个分支历史上存在的最后一个修订。在那次修订之后,两个分支的历史不再共享另一个共同的祖先。
【讨论】: