如何解决“由我们添加”git cherry-pick冲突?

时间:2018-06-15 09:11:54

标签: git git-cherry-pick git-merge-conflict

我在使用git时遇到了问题,在一次樱桃挑选后的冲突解决过程中。问题涉及对先前提交中git mv的文件进行的修改。

以下是重现完整命令的示例:

mkdir git_repository && cd git_repository
git init
echo "foo" > myFooFile
git add myFooFile 
git commit -m "First commit add myFooFile"

git checkout -b branch-a
rm myFooFile
echo "bar" > myBarFile
git add -A
git commit -m "rm myFooFile add myBarFile"

git checkout master
git mv myFooFile myBarFile
git add -u
git commit -m "git mv myFooFile myBarFile"

现在让我们在branch-a

上进行修改
git cherry-pick $(git show-ref branch-a)
>error: could not apply 70c80f3... rm myFooFile add myBarFile
>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'

以下是冲突:

git status
>On branch master
>You are currently cherry-picking commit 70c80f3.
>  (fix conflicts and run "git cherry-pick --continue")
>  (use "git cherry-pick --abort" to cancel the cherry-pick operation)
>
>Unmerged paths:
>  (use "git add <file>..." to mark resolution)
>
>   added by us:     myBarFile
>
>no changes added to commit (use "git add" and/or "git commit -a")

不幸的是,执行git add myBarFile然后git cherry-pick --continue不是解决方案,因为它会创建一个空提交。

在解析期间使用git rm myBarFile并不是更好,因为它会创建一个从myBarFile中删除foo的提交。

如何正确修复由我们添加的 的挑选冲突,并在我的主分支上获得这3次提交的情况下结束?

  • rm myFooFile add myBarFile
  • git mv myFooFile myBarFile
  • 首次提交添加myFooFile

myBarFile包含栏?

注意:我知道我可以在git冲突解决期间使用git checkout branch-a -- myBarFile,但这不是我正在寻找的解决方案,因为我不认为这是做这件事的好方法。

1 个答案:

答案 0 :(得分:1)

轻微:不要这样做:

git cherry-pick $(git show-ref branch-a)

请改为:

git cherry-pick branch-a

cherry-pick命令采用标识修订版本甚至修订版本范围的任何内容,如the gitrevisions documentation中所述。 git show-ref命令输出哈希ID 的名称,因此这会尝试两次提交提交。 (幸运的是git cherry-pick非常聪明,可以消除额外的费用。)

解决冲突的顶级视图

没有一种正确的方法可以解决冲突。你可以使用任何你喜欢的东西。这里要记住几件重要的事情:

  • Cherry-picking本质上是一个三向合并la git merge,除了不是找到实际的合并基础,Git只使用你正在挑选的提交的父级作为合并base(当然,最后的提交是一个常规的非合并提交)。

  • Git不跟踪文件名更改。相反,当进行合并操作时 - 包括一个cherry-pick-Git本质上运行两个git diff命令:

    git diff --find-renames <merge-base> HEAD     # figure out what we did
    git diff --find-renames <merge-base> <other>  # figure out what they did
    

    如果结果是合并冲突,Git会在索引中留下所有三个“有趣”的文件 - 但是可能会丢失一两个这样的文件(就像这里的情况一样)。 git status命令将这些显示为“未合并”,但您可以使用git ls-files --stage查找完整详细信息。我稍后会详细说明。

  • 此时,您的工作只是在索引中安排您在最终提交时所需的文件,这是合并的结果(或者在本例中为cherry-pick)。这些文件需要处于零阶段,这是正常的,未冲突的文件存在于索引中的位置。第1阶段的任何条目表示合并库中文件的版本,第2阶段的任何条目表示HEAD提交中的文件版本,第3阶段的任何条目表示另一个提交中的文件版本你正在和(或采摘樱桃)合并。

冲突本身

所以,让我们看看git diff --find-renames找到了什么,知道合并基础是被挑选的提交的父级。我们可以使用名称branch-a来识别cherry-pick提交。因此,它的父级是branch-a^(再次参见gitrevisions文档)。

$ git diff --find-renames branch-a^ HEAD
diff --git a/myFooFile b/myBarFile
similarity index 100%
rename from myFooFile
rename to myBarFile

这是“我们改变了什么”:重命名操作。

$ git diff --find-renames branch-a^ branch-a
diff --git a/myBarFile b/myBarFile
new file mode 100644
index 0000000..5716ca5
--- /dev/null
+++ b/myBarFile
@@ -0,0 +1 @@
+bar
diff --git a/myFooFile b/myFooFile
deleted file mode 100644
index 257cc56..0000000
--- a/myFooFile
+++ /dev/null
@@ -1 +0,0 @@
-foo

这是“他们改变了什么”:删除一个文件,添加另一个文件。

Git现在负责将myFooFile重命名为myBarFile(内容为foo),同时在创建myFooFile的同时删除myBarFile内容bar

它不可能同时做到,所以我们会发生冲突。同时它可以执行我们所做的重命名,因此它确实在工作树中执行此操作(仅限),使myBarFile包含foo

你想知道:

  

如何正确修复我们添加的挑选冲突并结束...使用myBarFile包含栏?

所有Git需求都是您在工作树中编写包含所需内容的myBarFile版本,然后调整索引以使其条目位于零阶段。目前,索引中的内容是:

$ git ls-files --stage
100644 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 2      myBarFile

myBarFile包含您想要的内容。

  

注意:我知道我可以在git冲突解决期间使用git checkout branch-a -- myBarFile ...

是:从myBarFile标识的提交中提取branch-a的版本,将其写入第0阶段的索引,在第2阶段删除条目,并使所有内容都准备好进行提交。 (作为奖励,它还将文件写入工作树,以便您可以看到它,尽管此时Git实际上并不关心工作树版本。)所以这是一个很好的快速实现方法期望的结果。不过,我们可以看一下另一种方法,它在某些情况下可能会有用:

  

[但]我不认为这是做这件事的好方法。

Git没有 a (单一)方式。 Git是一个工具。根据您的喜好使用它。如果您希望在未先触摸索引的情况下调整工作树内容,则可以使用git show提取文件的branch-a版本:

$ git show branch-a:myBarFile > tmp
$ cat tmp
bar
$ mv tmp myBarFile
$ git add myBarFile

如果我们检查实际的临时区域,我们会发现:

$ git ls-files --stage
100644 5716ca5987cbf97d6bb54920bea6adde242d87e6 0       myBarFile

这就是我们想要的,所以我们现在可以git cherry-pick --continuegit commit来完成这个挑选:

$ git cherry-pick --continue

此时,首选编辑器应在包含主题rm myFooFile add myBarFile的消息文件和其他数据上打开(因为这是您正在挑选的提交)。写出来的结果是:

[master 1837c17] rm myFooFile add myBarFile                   
 Date: Fri Jun 15 22:21:48 2018 -0700
 1 file changed, 1 insertion(+), 1 deletion(-)

git log --oneline显示您想要的内容:

1837c17 (HEAD -> master) rm myFooFile add myBarFile
a30b874 git mv myFooFile myBarFile
bde7df5 First commit add myFooFile

(注意:我的配置中有log.decorate = auto。)