首页 > 解决方案 > 变基与合并中的 Git 冲突

问题描述

  1. 合并到分支与重新设置分支时的冲突数量有什么区别吗?这是为什么?

  2. 进行合并时,合并更改存储在合并提交本身(与两个父项的提交)中。但是在进行变基时,合并存储在哪里?

谢谢, 奥马尔

标签: gitgithubgit-mergerebase

解决方案


在查看了 torek 的答案,然后又重新阅读了这个问题之后,我正在更新以澄清几点......

  1. 合并到分支与重新设置分支时的冲突数量有什么区别吗?这是为什么?

可能,是的,有很多原因。最简单的是合并过程只查看三个提交——“我们的”、“他们的”和合并基础。所有中间状态都被忽略。相比之下,在 rebase 中,每个提交都被转换为一个补丁并单独应用,一次一个。因此,如果第 3 次提交产生了冲突,但第 4 次提交取消了冲突,那么 rebase 将看到冲突,而 merge 则不会。

另一个区别是提交是否已在合并的两侧进行了精心挑选或以其他方式重复。在这种情况下,rebase通常会跳过它们,而它们可能会导致合并中的冲突。

还有其他原因;最终它们只是不同的过程,即使它们通常会产生相同的组合内容。

  1. 进行合并时,合并更改存储在合并提交本身(与两个父项的提交)中。但是在进行变基时,合并存储在哪里?

合并的结果存储在 rebase 创建的新提交中。默认情况下,rebase 会为每个被 rebase 的提交写入一个新的提交。

正如 torek 在他的回答中解释的那样,这个问题可能表明对合并中存储的内容存在误解。可以阅读该问题以断言导致合并结果的更改集(“补丁”)明确存储在合并中;他们不是。合并 - 就像任何提交一样 - 是内容的快照。使用它的父指针,您可以找出应用的补丁。在变基的情况下,git 不会明确保留有关原始分支点、哪些提交在哪个分支上或它们在哪里重新集成的任何信息;因此每个提交的更改都保留在该提交与其父级的关系中,但是有'


例如,假设你有

O -- A -- B -- C <--(master)
 \
  D -- ~D -- E -- B' -- F <--(feature)

其中D与 中的更改发生冲突master~D恢复为D,并且B'是樱桃采摘B到中的结果feature

现在,如果您合并featuremaster,则合并仅查看 (1)F与 的不同之处O,以及 (2)C与 的不同之处O。它没有从 中“看到”冲突D,因为~D扭转了冲突的变化。它将看到B并且B'两者都更改了相同的行;它可能能够自动解决这个问题,因为双方都进行了相同的更改,但是根据其他提交中发生的情况,这里可能会发生冲突。

但是一旦解决了任何冲突,您最终会得到

O -- A -- B -- C -------- M <--(master)
 \                       /
  D -- ~D -- E -- B' -- F <--(feature)

并且,正如您所指出的,M包含合并的结果。

回到原图……

O -- A -- B -- C <--(master)
 \
  D -- ~D -- E -- B' -- F <--(feature)

...如果您改为 rebase featuremaster则几乎就像一次将每个feature提交逐步合并master。你可以大致想象一下,你开始说

git checkout master
git merge feature~4

这会产生冲突。你解决了这个问题,然后得到

O -- A -- B -- C -- M <--(master)
 \                /
  -------------- D -- ~D -- E -- B' -- F <--(feature)

然后你可以继续下一个提交

git merge feature~3

这可能会或可能不会冲突,但当你完成后,你会得到

O -- A -- B -- C -- M -- M2 <--(master)
 \                /     /
  -------------- D -- ~D -- E -- B' -- F <--(feature)

并且,如果您正确解决了任何冲突,M2则应该具有与C. 然后你做E

git merge feature~2

B'有点不同,因为 rebase 会跳过它;所以你可以做

git merge -s ours feature~1

最后

git merge feature

你最终会得到

O -- A -- B -- C -- M -- M2 -- M3 -- M4 - M5<--(master)
 \                /     /    /     /    /
  -------------- D -- ~D -- E -- B' -- F <--(feature)

M4“我们的”合并在哪里,因此M4与 具有相同的内容M3)。

所以 rebase 很像这样,除了它不跟踪将新提交链接回feature分支的“第二个父”指针,它完全跳过B'. (而且它移动分支的方式也不同。)所以我们改为绘制

                   D' -- ~D' -- E' -- F' <--(feature)
                 /
O -- A -- B -- C <--(master)
 \
  D -- ~D -- E -- B' -- F

所以我们可以直观地指出D'“来自” D,即使它不是一个合并提交,其父指针显示它与D. 不过,那是存储合并这些更改的结果的地方。并最终F'存储两个历史的完整整合。

如上所述,repo 的最终状态(rebase 后)中没有任何内容可以清楚地说明哪些补丁与(大致等效的)合并相关联。您可以git diff O C查看其中一个,也git diff C F'可以查看另一个,但是您需要 git 不保留的信息才能知道O,CF'是相关的提交。

请注意F,在此图片中,无法访问。它仍然存在,您可以在 reflog 中找到它,但除非有其他东西指向它,gc否则最终可能会破坏它。

另请注意,变基feature不会master提前master。你可以

git checkout master
git merge feature

到ffmaster上来feature完成分支的整合。


推荐阅读