如何解决 "added by us" git cherry-pick 冲突?

How to resolve "added by us" git cherry-pick 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"

现在让我们对 b运行ch-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 的提交。

如何正确解决 cherry-pick 冲突由我们添加并最终导致我得到这 3 个提交在我的主人 b运行ch ?

myBarFile 包含 bar ?

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

未成年人:不要这样做:

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 没有找到实际的合并基础,只是使用提交的父级您正在挑选合并基础(当然,最终提交是常规的非合并提交)。

  • 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 会在索引中保留所有三个 "interesting" 文件——但可能会丢失一两个这样的文件(就像这里的情况一样)。 git status 命令将这些显示为 "unmerged",但您可以使用 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

这是"what we changed": 重命名操作。

$ 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

这是"what they changed":删除一个文件,添加另一个。

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

不可能两者都做,所以我们发生了冲突。同时它 可以 进行我们所做的重命名,所以它确实在工作树中(仅)进行了重命名,留下 myBarFile 包含 foo.

你想知道:

How to properly fix the cherry-pick conflict added by us and end-up ... With myBarFile containing bar ?

所有 Git 需要的是您向工作树写入包含您想要的内容的 myBarFile 版本,然后调整索引以便条目它处于零阶段。目前,索引中的内容是:

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

myBarFile有您想要的内容。

Note: I known that I could use git checkout branch-a -- myBarFile during git conflict resolution ...

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

[but] I don't think it's the git way of doing it.

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)。