对于导致冲突的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
我只期待&#34;五&#34;冲突标记之间的界限。 我可以将Git切换到我预期的行为吗?
答案 0 :(得分:3)
在我们进一步讨论之前,让我们绘制提交图:
A <-- master (HEAD)
\
B--C--D--E <-- foo
或者,你可以比较,这是Git绘制它的方式:
$ git log --all --decorate --oneline --graph
* 7c29363 (foo) five
* a35e919 four
* ee70402 three
* 9a179e6 two
* d443a2a (HEAD -> master) one
(请注意,我将您的问题转换为我附加的命令序列;我的提交哈希值当然与您的不同。)
您在此处看到一些病态行为的原因是git cherry-pick
实际上正在执行合并操作。关于这个最奇怪的部分是选择的合并基础。
对于正常的合并,你检查一些提交(通过检查一些检查该分支的提示提交的分支)并运行git merge other
。 Git找到 other
指定的提交,然后使用提交图来定位合并库,这在图中通常很明显。例如,当图表看起来像这样:
o--o--L <-- ours (HEAD)
/
...--o--B
\
o--o--R <-- theirs
合并基础只是提交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
然后,Git可以将这些更改组合在一起,将合并后的更改应用于B
,以进行新的合并提交,其第一个父级为L
,第二个父级为R
。最后的合并提交是合并提交,它使用单词“merge”作为形容词。我们经常称它为一个合并,它使用“merge”这个词作为名词。
要获得这个合并为名词,Git必须运行合并机制,以组合两组差异。这是合并的过程,使用“merge”作为动词。
要做一个樱桃选择,Git运行合并机制 - 合并为动词,因为我喜欢把它 - 但是挑出一个特殊的合并基础。樱桃选择的合并基础只是提取的提交的父。
在您的情况下,您正在挑选提交E
。所以Git将(动词)与commit D
合并为合并库,将A
提交为左/本地 L 提交,并将E
提交为右-side R 提交。 Git生成两个差异列表的内部等价物:
git diff --find-renames D A # what we did
git diff --find-renames D E # what they did
我们所做的是删除四行:阅读two
,three
和four
的行。他们所做的是添加一行:读一行five
。
merge.conflictStyle
这一切都变得更加清晰 - 好吧,可能有点清楚 - 如果我们将merge.conflictStyle
设置为diff3
。现在,Git不再向我们展示我们和他们的<<<<<<<
等所包围的部分,而是添加了合并基础版本,标有|||||||
:
one
<<<<<<< HEAD
||||||| parent of 7c29363... five
two
three
four
=======
two
three
four
five
>>>>>>> 7c29363... five
我们现在看到Git声称我们从基地删除了三行,而他们保留了这三行并添加了第四行。
当然,我们需要了解这里的合并基础是提交E
的父级,如果有任何“提前”我们当前的提交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
答案 1 :(得分:1)
嗯......在我看来,这是一个边缘案例,生活在&#34;樱桃采摘插入源于源分支中的一条线并不存在于目标分支&#34;,以及&#34;变化的进展,所有这些变化都被视为一个&#39; hunk&#39;因为每个都与#34;之前的那个相邻。
这些问题使得git不确定你的意图,所以它要求&#34;我应该做什么&#34;以最广泛的方式,为您提供做出正确决定所需的所有代码。
如果您将文件初始化为
a
b
c
d
e
f
g
h
i
然后创建像
这样的更改x -- A <--(master)
\
C -- E -- G <--(branch)
(其中标记A
的提交大写了文件中相应的字母等,这是一个表现得更像你期望的情况 - 因为git的所有内容看起来都非常清楚,它只是显而易见的当你说樱桃挑选E
到master
时。 (它不仅不会将无关的变化粘附到冲突标记上,而且没有冲突标记;它只是起作用。)
现在要明确 - 我并不是说樱桃选择只会在我这样明确的案例中做正确的事情。但正如你的测试用例是最糟糕的情况&#34;对于樱桃采摘的场景,我的理想情况。介于两者之间的情况很多,在实践中,人们通常似乎从樱桃挑选中得到他们想要的结果。 (如果这听起来像是对cherry-pick命令的认可,请让我澄清一下:我认为它是套件中最过度推荐的命令,我从来没有真正使用它。但它确实有效,在它的内部定义边界。)
基本记住两件事:
1)如果在源分支上插入新行,并且无法在目标分支中明确确定相应的插入点,则会发生冲突,并且可能包含& #34;上下文&#34;来自源分支
2)如果两个变化相邻 - 即使它们不重叠 - 它们仍然可以被认为是冲突的。