我还在学习git。
我有一个名为names.txt
的文件。带有此文字。
这是我的提交历史记录
第一次提交已添加文件。 第二个提交添加了第一行Mary。 第三次提交添加了第二行约翰。
git show 7bdb5ef
git show 80384aa
我想以此为基础并编辑提交Mary以将文本更改为Mary Shelly
。
I do git rebase -i 4a5244b
接下来,我将提交Mary设置为编辑并运行变基。
调整停在这里。
我将其更改为Mary Shelly
并进行登台。
我跑步
git commit --amend
跟着
git rebase --continue
现在我遇到了合并冲突。
我不明白为什么会这样。提交John
仅更改文件的第二行。当我们编辑提交Mary
时,我们仅更改文件的第一行。
这怎么引起冲突?
答案 0 :(得分:3)
问题在于存在 合并冲突,而chepner's comment是理解原因的关键。好了,还有 commit graph ,还有git rebase
由重复的git cherry-pick
运算组成的事实。交互式rebase允许您在每个git cherry-pick
之间添加自己的命令,甚至可以将Cherry-pick更改为其他内容。 (最初的命令表以全{pick
命令开始,每个命令都表示进行摘樱桃。)
您的提交历史记录是您的提交图的摘要-本质上是从某个特定终点(当前分支的尖端)开始并工作的访问该提交图 中的每个提交的结果向后。如果您使用git log --graph
,则会得到一些潜在的重要信息,而这些信息在没有--graph
的情况下会被忽略,尽管在这种特殊情况下,很容易看到图形是线性的。因此,您只有三个提交:
A <-B <-C <-- master (HEAD)
其中A
实际上是4a5244b
,B
代表7bdb5ef
,而C
代表80384aa
(如果我已经转录了图片正确)。每次提交都具有文件names.txt
的完整副本。当然,副本在提交A
,B
和C
中是不同,在A
中是空的。在B
中,它是一行读取Mary
的行;在C
中,两行分别显示Mary
和John
该图本身源自以下事实:提交C
或80384aa
在{{内的中包含提交B
或7bdb5ef
的哈希ID。 1}}本身。这就是为什么我从C
拔出一个指向C
的箭头。 Git将此称为B
的 parent 提交。 Git以名称C
记录C
的哈希ID,然后在名称master
上附加特殊名称HEAD
到知道master
应该从这里开始,并且git log
是您现在要进行的工作。
当您运行C
时(选择提交git rebase -i 4a5244b
作为新基础),Git指出这意味着副本提交A
和B
,因此会将其哈希ID放入C
命令列表中。然后,它将在命令表上打开您的编辑器。您将pick
更改为pick
,这会告诉Git:在执行操作的中间,执行“摘樱桃”操作,然后退出rebase。
您没有强制重新建立真实副本。 (为此,使用edit
或-f
或--no-ff
都是同一意思。这在这里也没有关系,在大多数情况下也是如此。)因此,Git看到有一条指令复制--force-rebase
,使它出现在B
之后,并意识到:嘿,等等,A
已经在B
之后。我会把它留在那里。 Git做完了然后停下来,让你保持这种状态:
A
请注意,A--B <-- HEAD
\
C <-- master
不再附加到HEAD
:现在它直接指向提交master
。提交B
仍然存在,并且C
仍然指向它,但是Git已停止在“分离的HEAD”模式下,允许您进行编辑。
您对文件master
和git add
进行了更改。这会产生一个 new 提交-我们可以将其称为git commit --amend
或B'
,通常我使用D
,因为通常它与B'
很像,但是这次已经足够不同了,所以让我们使用B
。新的提交将D
作为其父项,A
就是这样做的。 Git更新--amend
以指向新提交。现有的提交HEAD
保持不变。所以现在您有了:
B
D <-- HEAD
/
A--B
\
C <-- master
中的文件names.txt
的新行读为D
。
您现在运行Mary Shelly
,因此Git继续使用说明表中的内容。这由git rebase --continue
组成,这使Git运行pick <hash-of-C>
来复制git cherry-pick
。该副本需要在当前提交C
之后进行。现有的提交D
不需要,因此Git这次必须真正完成这项工作。
要执行合并操作(要合并),操作-Git需要三个输入。这三个输入是 merge base 提交,当前或C
提交(有时也称为 local ,尤其是--ours
),以及其他或git mergetool
提交(有时称为远程)。对于常规合并,基址通常有点远:这是两行提交分开的地方。对于“挑剔”和“还原”而言,该基准就在提交旁边。此操作的合并基础是--theirs
的父提交C
!
合并的实际操作包括在整个提交中运行两个B
命令:
git diff
:我们做了哪些更改?git diff --find-renames hash-of-base hash-of-ours
:它们发生了什么变化?因此Git现在将基础提交git diff --find-renames hash-of-base hash-of-theirs
与您当前的提交B
进行对比。该差异影响文件D
并说:将说玛丽的一行更改为两行:一读玛丽·雪莱,另一读约翰。然后,Git对比names.txt
与{ {1}},看看“他们”(您,之前)做了什么。差异影响文件B
并说:在读取文件的末尾,在读取Mary的行之后,添加读取John的行。
这就是Git在合并冲突部分中向您显示的内容:一个文件说用Mary Shelly代替Mary ,另一个文件说 keep Mary并添加John 。如果愿意,您可以告诉Git在合并冲突部分保留更多信息。为此,请将C
设置为names.txt
。 (如果未设置,则默认值为diff.conflictStyle
。)
使用diff3
设置,您会看到 base 内容(由merge
标记)是一行diff3
,而这两行来自冲突提交的文件已分别用|||||||
或Mary
+新行Mary Shelly
替换了该基础。我发现这种合并冲突更清晰,更易于手动合并。
无论如何,此时您的工作是想出正确的结果(无论是什么结果)并将其写出并将其复制到索引插槽零中。通常,您只需要编辑Git在工作树中留下的混乱Mary
,将正确的内容放入其中,然后运行John
。
已解决冲突,请运行names.txt
以恢复停止的任何操作-在这种情况下,请重新设置基准,但这也发生在Cherry-pick和merge中。 Git将使用您用git add names.txt
更新的索引内容进行新的提交,它是git whatever --continue
的副本:
git add
到达命令表的末尾,C
现在通过将名称 D--C' <-- HEAD
/
A--B
\
C <-- master
从提交git rebase
中移出并将其粘贴到master
上来完成,这是最后一个复制它,然后重新附加C
:
C'
答案 1 :(得分:0)
文件级合并操作(即,Git需要协调对文件的两组更改的操作)试图允许您在不引起太多冲突的情况下四处移动代码,因此为了尝试找到正确的位置,进行更改后,也会考虑 context -周围的线条集。
在这里,重新应用提交John
会造成麻烦:原始提交John
添加到行Mary
的旁边。现在,Git试图重新应用提交,但是引用行Mary
不再存在-仅有一行引用Mary Shelly
...请记住,Git无法理解目的和/或文件的含义,因此在这种情况下,不会有任何机会将其作为冲突呈现给您,以便您可以进行检查。
使用John
和Mary
之间的许多其他行再次尝试相同的操作,这些行将保持不变-您会发现不会发生冲突。
答案 2 :(得分:0)
问题在于,实际上,在添加的相邻行上也很有可能需要对原始行进行更改,以使合并成功而不会引起人为判断。我使用的示例是
<<<<<<<<<<<<<
if ( g->tag == mark
|| g->tag == error ) {
||||||||||||||
if ( tag == mark
|| tag == error ) {
==============
if ( tag == mark
|| tag == release
|| tag == error ) {
>>>>>>>>>>>>>>
其中一个更改将g->
添加到了两行,而另一个更改在中间添加了release
。