为什么git cherry-pick冲突的额外变化?

0xF*_*0xF 6 git git-cherry-pick

为了git cherry-pick导致冲突,为什么Git建议的更改不仅仅来自给定的提交?

例:

-bash-4.2$ git init
Initialized empty Git repository in /home/pfusik/cp-so/.git/
-bash-4.2$ echo one >f
-bash-4.2$ git add f
-bash-4.2$ git commit -m "one"
[master (root-commit) d65bcac] one
 1 file changed, 1 insertion(+)
 create mode 100644 f
-bash-4.2$ git checkout -b foo
Switched to a new branch 'foo'
-bash-4.2$ echo two >>f
-bash-4.2$ git commit -a -m "two"
[foo 346ce5e] two
 1 file changed, 1 insertion(+)
-bash-4.2$ echo three >>f
-bash-4.2$ git commit -a -m "three"
[foo 4d4f9b0] three
 1 file changed, 1 insertion(+)
-bash-4.2$ echo four >>f
-bash-4.2$ git commit -a -m "four"
[foo ba0da6f] four
 1 file changed, 1 insertion(+)
-bash-4.2$ echo five >>f
-bash-4.2$ git commit -a -m "five"
[foo 0326e2e] five
 1 file changed, 1 insertion(+)
-bash-4.2$ git checkout master
Switched to branch 'master'
-bash-4.2$ git cherry-pick 0326e2e
error: could not apply 0326e2e... five
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'
-bash-4.2$ cat f
one
<<<<<<< HEAD
=======
two
three
four
five
>>>>>>> 0326e2e... five
Run Code Online (Sandbox Code Playgroud)

我期待冲突标记之间只有"五"线.我可以将Git切换到我预期的行为吗?

tor*_*rek 6

在进一步介绍之前,让我们绘制提交图:

A   <-- master (HEAD)
 \
  B--C--D--E   <-- foo
Run Code Online (Sandbox Code Playgroud)

或者,以便您可以比较,这是Git绘制的方式:

$ git log --all --decorate --oneline --graph
* 7c29363 (foo) five
* a35e919 four
* ee70402 three
* 9a179e6 two
* d443a2a (HEAD -> master) one
Run Code Online (Sandbox Code Playgroud)

(请注意,我已将您的问题变成一个附加的命令序列;我的提交哈希当然与您的不同。)

Cherry-pick是合并的一种特殊形式

您在此处看到某种病理行为的原因git cherry-pick是实际上正在执行合并操作。最奇怪的部分是所选的合并基础。

正常合并

对于正常的合并,您需要签出一些提交(通过签出一个分支来签出该分支的尖端提交)并运行。Git定位由所指定的提交,然后使用提交图来定位合并基础,这在图中通常很明显。例如,当图形如下所示:git merge otherother

          o--o--L   <-- ours (HEAD)
         /
...--o--B
         \
          o--o--R   <-- theirs
Run Code Online (Sandbox Code Playgroud)

合并基础只是提交B(对于基础)。

为了进行合并,Git然后进行两个操作git diff,一个从合并库到我们的本地提交L在左边,而他们的提交R在右边(有时称为远程提交)。那是:

git diff --find-renames B L   # find what we did on our branch
git diff --find-renames B R   # find what they did on theirs
Run Code Online (Sandbox Code Playgroud)

然后,Git可以组合这些更改,并将组合的更改应用于B,以进行新的合并提交,其第一父级为L,第二父级为R。最后的合并提交是合并提交,它使用单词“ merge”作为形容词。我们通常只称其为merge,它使用“ merge”一词作为名词。

但是,要获得这种“作为一个名词的合并”,Git必须运行合并机制,以合并两组差异。这是合并过程,使用单词“ merge”作为动词。

樱桃选择合并

为了进行挑选,Git运行了合并机制(如我喜欢说的那样,将合并作为动词进行合并),但选择了一个特殊的合并基础。樱桃选择的合并基础只是被樱桃选择的提交的父对象

在您的情况下,您正在选择commit E。因此,Git正在以提交D作为合并基础进行合并(动词),将提交A作为左侧/本地L提交,并将提交E作为右侧R提交。Git生成两个差异列表的内部等效项:

git diff --find-renames D A   # what we did
git diff --find-renames D E   # what they did
Run Code Online (Sandbox Code Playgroud)

什么,我们所做的就是删除四条线:的那些读书twothreefour。他们所做的就是增加一行:一行five

使用 merge.conflictStyle

如果设置为,这一切将变得更加清晰-好吧,也许会更加清晰。现在,Git 不仅向我们展示了我们的以及由等号包围的部分,还添加了合并基本版本,并标有:merge.conflictStylediff3<<<<<<<|||||||

one
<<<<<<< HEAD
||||||| parent of 7c29363... five
two
three
four
=======
two
three
four
five
>>>>>>> 7c29363... five
Run Code Online (Sandbox Code Playgroud)

现在,我们看到Git声称我们从基础上删除了三行,而他们保留了这三行并添加了第四行。

当然,我们需要了解,这里的合并基础是commit的父级E,也就是我们当前commit之前的任何东西A。我们删除了三行并不是真的。实际上,我们从来没有过三行。 我们只需要处理Git显示的内容,就好像我们删除了一些行一样。

附录:生成冲突的脚本

#! /bin/sh
set -e
mkdir t
cd t
git init
echo one >f
git add f
git commit -m "one"
git checkout -b foo
echo two >>f
git commit -a -m "two"
echo three >>f
git commit -a -m "three"
echo four >>f
git commit -a -m "four"
echo five >>f
git commit -a -m "five"
git checkout master
git cherry-pick foo
Run Code Online (Sandbox Code Playgroud)