为什么我与git rebase interactive会发生这种合并冲突?

问题描述 投票:1回答:3

我还在学习git。

我有一个名为names.txt的文件。有了这个文字。

enter image description here

这是我的提交历史

enter image description here

第一次提交添加了文件。第二次提交添加了第一行Mary。第三次提交添加了第二行John。

git show 7bdb5ef

enter image description here

git show 80384aa

enter image description here

我想重新定义并修改提交Mary以将文本更改为Mary Shelly

I do git rebase -i 4a5244b

接下来,我设置了Mary来编辑和运行rebase。

enter image description here

Rebase在这里停止。

enter image description here

现在name.txt在Mary commit中具有值。 enter image description here

我将它改为Mary Shelly并进行演出。

我跑

git commit --amend 

其次是

git rebase --continue

现在我得到了这个合并冲突。

enter image description here

enter image description here

我不明白为什么会这样。提交John只会更改文件中的第二行。当我们编辑提交Mary时,我们只更改文件的第一行。这是如何引发冲突的?

git rebase
3个回答
4
投票

问题在于存在合并冲突,而chepner's comment是理解原因的关键。好吧,那个,以及提交图,加上git rebase由重复的git cherry-pick操作组成的事实。交互式rebase允许您在每个git cherry-pick之间添加自己的命令,甚至可以将樱桃选择更改为其他内容。 (最初的命令表以all-pick命令开头,每个命令都是做一个樱桃选择。)

您的提交历史记录是您的提交图的摘要 - 实质上是访问提交图中每个提交的结果,从某个特定的结束点(当前分支的尖端)开始并向后工作。如果你使用git log --graph,你会得到一些在没有--graph的情况下遗漏的重要信息,尽管在这种特殊情况下,很容易看出图形是线性的。所以你只有三个提交:

A <-B <-C   <-- master (HEAD)

其中A实际上是4a5244bB代表7bdb5ef,而C代表80384aa(如果我正确地转录了图像)。每个提交都有一个完整的完整副本文件names.txt。在ABC中,副本当然是不同的,因为在A中,它是空的;在B,它是一行读Mary;在C,它是两行读Mary然后John

图表本身源于提交C80384aaB本身中包含commit 7bdb5efC的哈希ID。这就是为什么我画出C指向B的箭头。 Git称之为C的父提交。 Git在名称C中记录master的哈希ID,然后将特殊名称HEAD附加到名称master,以便它知道这是git log应该从哪里开始,并且提交C就是你已经拥有的,用于工作,马上。

当你运行git rebase -i 4a5244b-选择提交A作为新的基础时 - Git发现这意味着复制提交BC,因此它将它们的哈希ID放入pick命令列表中。然后它在命令表上打开您的编辑器。你将pick更改为edit,它告诉Git:在操作过程中执行樱桃选择,然后退出rebase。

你没有强迫rebase制作一个真正的副本。 (要做到这一点,使用-f--no-ff--force-rebase-都意味着同样的事情。这在这里并不重要,在大多数情况下也是如此。)所以Git看到有一条指令,复制B以便它出现在A之后,并意识到:嘿,等等,B已经在A之后了。我会把它留在那里。 Git做到了并停了下来,让你处于这种状态:

A--B   <-- HEAD
    \
     C   <-- master

请注意,HEAD不再附加到master:它现在直接指向提交B。提交C仍然存在,master仍指向它,但Git已停止在“超级HEAD”模式允许您进行编辑。

您对文件git addgit commit --amend进行了更改。这使得一个新的提交 - 我们可以称之为B'D,通常我使用B',因为通常它很像B,但这次它不同,所以让我们使用D。新的提交将A作为其父级 - 这就是--amend所做的。 Git更新HEAD以指向新提交。现有的提交B保持不变。所以现在你有:

  D   <-- HEAD
 /
A--B
    \
     C   <-- master

names.txt中的文件D有新的单行读取Mary Shelly

你现在运行git rebase --continue,所以Git继续说明表中剩下的内容。这包括pick <hash-of-C>,这使得Git运行git cherry-pick复制C。此副本需要在当前提交后进行,D。现有的提交C没有,所以Git这次必须真正做好这项工作。

A cherry-pick is a merge—merge as a verb, at least

要执行合并操作 - 要合并,action-Git需要三个输入。这三个输入是合并基础提交,当前或--ours提交(有时也称为本地,特别是git mergetool),另一个或--theirs提交(有时称为远程)。对于常规合并,基数通常有点遥远:它是两行提交分歧的地方。对于cherry-pick-for和revert,就此而言,基础就在提交旁边。这个操作的合并基础是C的父提交B

合并的实际操作包括在整个提交上运行两个git diff命令:

  • git diff --find-renames hash-of-base hash-of-ours:我们改变了什么?
  • git diff --find-renames hash-of-base hash-of-theirs:他们改变了什么?

所以Git现在差异提交B,基础,vs提交D,你当前/我们的提交。差异影响文件names.txt并说:将一行说明玛丽改为两行:一行读玛丽雪莉,一读约翰。然后Git将BC区分开来,看看他们(你,之前)做过什么。差异影响文件names.txt并说:在读取Mary的行之后,在文件末尾添加读取John的行。

这就是Git在合并冲突部分向您展示的内容:一个文件说Mary和Mary Shelly取代,另一个文件说保留Mary并添加John。如果您愿意,可以告诉Git在合并冲突部分中保留更多信息。为此,请将diff.conflictStyle设置为diff3。 (默认情况下,如果未设置,则为merge。)

使用diff3设置,您将看到由|||||||标记的基本内容是一行Mary,并且来自冲突提交的两个文件分别用Mary ShellyMary +新行John替换了该基础。我发现这种合并冲突更清晰,更容易手动合并。

在任何情况下,你的工作就是提出正确的结果 - 无论是什么 - 并将其写出并将其复制到索引槽零。通常,您只需编辑Git在工作树中留下的凌乱的names.txt,将正确的内容放入其中,然后运行git add names.txt

Resuming

解决了冲突后,运行git whatever --continue以恢复任何停止的操作 - 在这种情况下,rebase,但这也发生在cherry-pick和merge中。 Git将使用您使用git add更新的索引内容来生成C副本的新提交:

  D--C'   <-- HEAD
 /
A--B
    \
     C   <-- master

到达命令表的末尾,git rebase现在完成了将master命名为C并将其粘贴到C'上,这是它制作的最后一个副本,然后重新附加HEAD

  D--C'   <-- master (HEAD)
 /
A--B
    \
     C   [abandoned]

0
投票

文件级合并操作(即Git需要将两组更改协调到文件的操作)尝试允许您移动代码而不会导致太多冲突,因此为了尝试找到应用更改的正确位置,上下文 - 周围线的集合 - 也被考虑在内。

在这里,重新应用提交John会导致麻烦:原始提交在John行旁边添加了Mary。现在Git正在尝试重新应用提交,但是该参考行说Mary不再存在 - 所有这些都有一行说Mary Shelly ...请记住,Git不理解文件的目的和/或含义,因此,在这种情况下,它不会冒任何机会,并将此作为冲突呈现给您,以便您可以检查它。

再次尝试使用JohnMary之间的许多其他线路,你会保持不变 - 你会发现你不会发生冲突。


0
投票

问题在于,在添加的相邻行上也可能需要对原始行进行的更改,以允许合并成功,而不会涉及人为判断。我使用的例子是

<<<<<<<<<<<<<
    if ( g->tag == mark 
      || g->tag == error ) {
||||||||||||||
    if ( tag == mark
      || tag == error ) {
==============
    if ( tag == mark 
      || tag == release
      || tag == error ) {
>>>>>>>>>>>>>>

其中一个变化将g->添加到一对线,另一个变化在中间添加了release线。

© www.soinside.com 2019 - 2024. All rights reserved.