使Cherry Pick不会带来历史

时间:2013-12-02 20:28:05

标签: git git-merge git-rebase

所以我遇到了一个git的问题,当我重新定义或者选择一个特定的提交时,我将获得比该特定提交更多的信息。这个问题在这个问题中得到了很好的概述:

Am I misunderstanding git cherry-pick?

这一切都很好,我明白它正在汲取历史,因为我想要的提交取决于那里的历史。我没有得到的是如何解决它。我真的不希望樱桃选择应用所有这些历史,我更愿意使用该历史来确定线应该去哪里然后引发冲突说它必须做一些有点腥使它适合。

我有什么方法可以做到这一点吗?我找到的唯一方法是我可以通过该提交制作补丁并应用补丁,但我真的希望能够为我的树使用cherry-pick和rebase。

为了说明我的示例,您可以在空目录中保存git存储库,然后将1行文本文件添加到存储库。然后在3个单独的提交中向文件中再添加3行,这样您的文件中将有4个提交和4行。我的文件看起来像:

line 1
line 2
line 3
line 4

现在执行git checkout HEAD~3,这样你只有

line 1

然后调用git cherry-pick HEAD,你就会发生冲突。为什么会发生这种情况,请参阅我上面发布的链接。您的文件将如下所示:

line 1
<<<<<<< HEAD
=======
line 2
line 3
line 4

>>>>>>> 6fe905b... Commit 4

但这很糟糕,因为如果这出现在一个真实的例子中,你的合并冲突就是说Commit 4有比实际在Commit 4中更多的东西。所以有什么方法让我修复它以便樱桃 - 挑选会给我一个更合乎逻辑的合并冲突?在我的现实世界问题即将到来的时候,当我正在做一次改变时,它就出现了。如果我可以解决这个问题,那么我的逻辑是,这个问题的根本原因也应该适用于rebase。

2 个答案:

答案 0 :(得分:2)

[注意:编辑后添加diff3,重新编辑问题;见最底部]

cherry-pick命令将提交ID的列表作为其参数:

  

git cherry-pick [--edit] [-n] [-m parent-number] [-s] [-x] [--ff]&lt; commit&gt; ...

但你说你正在使用:

git cherry-pick branch-name commit-hash

这意味着你要求git选择两个 cherries:与命名分支的提示相关联的一个(branch-name解析的SHA-1 ID),以及与给定commit-hash相关联的一个。

换句话说,首先运行:

git rev-list --no-walk branch-name commit-hash

rev-list的输出是cherry-pick“看到”的效果。


git rebase做的事情比git cherry-pick更广泛。通常情况下,人们通过此操作将“分支机构”(这是我刚刚编写的术语)从一个点移动到另一个点:

A - B - C - D - E   <-- origin/master
          \
            K - L   <-- local

当你让一个本地分支做一些工作(在K上提交Llocal),同时其他人做自己的工作时,会出现这种情况(在主人身上提交DE)并且您决定要在他们的工作之上“重新建立local分支”,所以您可以执行以下操作:

git fetch

(然后你环顾四周看看提交DE,所以你想让你的本地master更新:

git checkout master
git merge --ff-only origin/master

(本地主人现在是最新的,现在你决定改变分支local):

git checkout local
git rebase master

Rebase基本上是一个自动化的“反复樱桃挑选,直到完成,然后更改分支标签”操作。鉴于我在上面提到的提交图,它首先执行一个分离的 - HEAD git checkout来提交E,然后它执行git cherry-pick提交K重复提交L,并且(如果一切顺利)最后调整本地分支标签local以指向最后一个新挑选的提交:

[开始]

A - B - C - D - E       <-- master, origin/master
          \
            K - L       <-- HEAD=local

[第1步,将HEAD直接设置为E,然后挑选K的副本,给予:]

A - B - C - D - E       <-- master, origin/master
         \       \
          \       K'    <-- HEAD [detached]
           \
            K - L       <-- local

[第2步,樱桃挑选L的副本,给予:]

A - B - C - D - E       <-- master, origin/master
         \       \
          \       K'-L' <-- HEAD [detached]
           \
            K - L       <-- local

[完成樱桃采摘序列,移动标签,给予:]

A - B - C - D - E       <-- master, origin/master
         \       \
          \       K'-L' <-- HEAD=local
           \
            K - L       <-- [no label, abandoned]

如果需要,重复的Cherry-pick序列将停止(合并冲突),但如果需要,它会将文件留在.git目录中以记住它正在做什么以及它已经走了多远,所以修复问题后,您可以运行git rebase --continue

基本上,cherry-pickrebase之间的两个巨大差异是:

    在向cherry-pick提交提交内容时,
  • --no-walk使用git rev-list; rebase遍历提交(虽然rebase随机播放参数,但您不能只运行git rev-list以提前查看它们。)
  • cherry-pick永远不会移动分支标签; rebase完成后即可完成。

问题中用于说明差异问题的新示例略有不同:当您git checkout HEAD~3然后尝试git cherry-pick HEAD时,HEAD将引用ID为{{的提交1}}那时候。但是master~3会做到这一点。以下是我将cherry-pick master设置为merge.conflictstyle的示例,以便您可以查看diff3正在应用的具体内容:

cherry-pick

有些像$ echo 'line 1' > file && git add file && git commit -m 'add file with 1 line' [master 72f46c5] add file with 1 line 1 file changed, 1 insertion(+) create mode 100644 file $ echo 'line 2' >> file && git add file && git commit -m 'add line 2' [master 134cdd1] add line 2 1 file changed, 1 insertion(+) $ echo 'line 3' >> file && git add file && git commit -m 'add line 3' [master e8634fc] add line 3 1 file changed, 1 insertion(+) $ echo 'line 4' >> file && git add file && git commit -m 'add line 4' [master 50810b6] add line 4 1 file changed, 1 insertion(+) $ git checkout master~3 ... HEAD is now at 72f46c5... add file with 1 line $ git config merge.conflictstyle diff3 $ git cherry-pick master error: could not apply 50810b6... add line 4 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' $ cat file line 1 <<<<<<< HEAD ||||||| parent of 50810b6... add line 4 line 2 line 3 ======= line 2 line 3 line 4 >>>>>>> 50810b6... add line 4 样式足以diff3(我现在还在考虑这个:-))。

答案 1 :(得分:0)

运行git rebasegit cherry-pick的相应结果通常不相同。

运行git rebase通常会重写历史记录,并且可以显示移动整个分支。这可能是您所看到的不受欢迎的历史的来源。

相反,git cherry-pick会重置存储库历史记录中其他位置的一个或多个提交的增量。典型的用例是挑选一个提交来创建另一个新提交。我需要了解有关您正在运行的命令的更多具体信息,以了解您可能会对结果感到沮丧。

将多提交主题分支压缩为单个提交的有用命令是git merge --squash。另一种选择是反转,然后是另一种互动反转。