Git中的cherry-picking是什么意思?

3277

26
与其使用合并(merge),从一个分支中挑选所需的提交(cherry-pick)再重新提交到目标分支(例如:主分支master)更为简单。 - Levent Divilioglu
2
可以这样说吗?:“挑选一个提交意味着在HEAD上创建一个临时分支,将该提交的差异合并到其中,然后快进HEAD。”或者简单地说:“合并单个提交”。 - U. Windl
14个回答

3966

在Git中,Cherry-picking指的是从一个分支中选择一个提交并将其应用到另一个分支。

这与其他方式(如mergerebase)形成对比,后者通常将许多提交应用到另一个分支。

也可以挑选多个提交,但merge是优先于Cherry-picking的方法。

请确保您在要应用提交的分支上。
执行以下操作:
git switch master 执行以下操作:
git cherry-pick

N.B.:

  1. 如果你从公共分支中挑选某些提交,你应该考虑使用

    git cherry-pick -x <commit-hash>
    

    这将生成一个标准化的提交信息。这样,你(和你的同事)仍然可以跟踪提交的来源,并可能避免未来的合并冲突。

  2. 如果你在提交中附有注释,它们不会随着 cherry-pick 被复制。要同时复制它们,你必须使用:

    git notes copy <from> <to>
    

其他链接:


7
摘樱桃是否真的必要?混合重置或软重置不会完成类似的工作吗? - Nav
11
git push是将更改推送到主分支的最后一步。 - Nagappa L M
142
FYI:一个提交(commit)在语义上包含了那个时刻工作树中的所有文件(和前一个提交的提交哈希),因此你不是将整个提交应用于另一个提交,而是将一个提交所做的更改应用于前一个提交。 "cherry-pick commit将指定提交引入到当前分支所做的更改"。大多数人倾向于将提交视为更改(例如svn),但实际上不是这样,每个提交都涉及完整的工作树。虽然这在这种情况下没有影响,但它可以帮助理解git为什么会像现在这样工作。 - Emile Vrijdags
3
@Zitrax 笔记和提交信息不同吗?我的单个 git cherry-pick 命令能够带来我的提交信息。你说的是别的事情吗?我根本不需要运行 git notes 命令就可以完成它。 - RBT
3
-x 选项的目的: 在记录提交时,在原始提交消息中附加一行,以指示此更改是从哪个提交中挑选出来的,该行将显示为“(cherry picked from commit …​)”。 https://git-scm.com/docs/git-cherry-pick - Loner
显示剩余6条评论

461

这段引用来自:《使用 Git 进行版本控制》

git cherry-pick 命令会将指定的提交所带来的更改应用到当前分支上,并生成一个新的、独立的提交。 严格来说,使用 git cherry-pick 不会改变存储库中现有的历史记录,而是会向其中添加新的内容。和其他通过应用 diff 来引入更改的 Git 操作一样,你可能需要解决冲突才能完全应用给定提交中的更改。通常情况下,git cherry-pick 命令被用来将某个分支中的特定提交合并到另一个分支中。一个常见的用法就是将维护分支(maintenance branch)中的提交前移或后移到开发分支(development branch)。

$ git checkout rel_2.3
$ git cherry-pick dev~2 # commit F, below

之前: before

之后: after

此外,这里有一个非常好的实际操作视频教程: Youtube: Git cherry-pick介绍


22
当从某个分支(b1)选择一些精选提交并稍后交付给主分支时,如果尝试将原始选择提交的分支b1也交付到主分支,则会出现冲突。这些冲突将如何处理?是否已经处理好或者它是如何工作的? - parasrish
7
是的,你之前的合并已经处理了它们。所以你从(b1)分支修改了a、b、c、d。然后你只挑选了“c”。未来一旦你从(b1)合并到主分支,由于“c”变动相同,只会合并a、b、d,并保留“c”的变动。但是,如果你回滚了你的合并,那么你将回滚带有“c”的变动。你需要单独回滚它们。 - Teoman shipahi
36
需要强调的是:在给定的例子中,仅仅将(F - E)的差异应用于Z。这是一个特殊情况。可以使用"挑选最好的"来应用多个提交的差异,比如说,所有不相邻提交之间的差异。例如,在上面的例子中,(F - E)、(E - D)、(D - C) 和 (C - B)。这相当于应用差异(F - B)。 - Thomas Bitonti
2
@ThomasBitonti,我认为只有(F-E)的差异应用于 Z 是相当清楚的。字母表示提交,而单个提交应用于 Z。您还可以清楚地在命令中看到它。此外,OP询问的是“什么是cherry picking”,而不是如何应用“多个Cherry Picks”。此外,从单个提交应用cherry pick并不是一个狭窄的情况,这基本上是一个广泛的情况。 - Teoman shipahi
4
换句话说, cherry-pick 只会取出最新的提交所做的更改。如果你有三次不同的提交,并且你只 cherry-pick 最后一次,它将不会包含第一次和第二次提交所做的更改。而合并命令会获取所有更改并应用到目标分支(主分支)上。 - Teoman shipahi
显示剩余7条评论

206

Git中的摘樱桃(cherry picking)旨在将一个分支中的某些提交应用到另一个分支中。如果您例如犯了一个错误并将更改提交到了错误的分支,但不想合并整个分支,您可以撤销提交并将其摘选到另一个分支。

使用该功能,您只需要输入git cherry-pick hash,其中hash是其他分支中的提交哈希。

完整的操作步骤请参见:http://technosophos.com/2009/12/04/git-cherry-picking-move-small-code-patches-across-branches.html


如果底层分支发生了变化,导致挑选的樱桃补丁不再合适,会发生什么? - information_interchange

146
我准备了逐步说明“挑选”操作的插图,并附有这些插图的动画(接近结尾处)。
1. 在挑选之前(我们将从分支“feature”中挑选提交“L”): 显示Git存储库在挑选之前的状态的图像。主分支上有五个提交,从A到E。一个标记为'feature'的第二个分支包含四个提交,并在提交B处分叉。'feature'提交被标记为K到N,其中L位于中间。
开始执行命令git cherry-pick feature~2feature~2是在feature之前的第二个提交,即提交L): 此图显示了将L提交从feature分支中取出,并将其放置在主分支上的E提交之后的cherry-pick操作结果。
执行命令(git cherry-pick feature~2)后: 主分支现在包含一个名为L'或L-prime的提交。功能分支保持不变。
同样的动画: 上述步骤的动画。
注意:
提交“L'”从用户的角度来看(提交=快照)是提交“L”的完全副本。
从技术上讲(内部而言),它是一个新的、不同的提交(因为例如,提交“L”包含指向“K”的指针(作为其父提交),而提交“L'”包含指向“E”的指针)。

这是指,L将会在主分支上成为N->M->L的一部分吗?还是它只会在主分支上带来提交L? - Priyank Thakkar
4
@PriyankThakkar,是的,完全是L,除此之外没有其他的(正如你从图片/动画中所看到的)。 - MarianD
2
“提交 L' 从用户的角度来看(提交 = 快照),是提交 L 的精确副本。” - 不,它不是相同的快照(除非快照 K 和 E 已经相同),只是相同的差异(即 E→L' = K→L)。 - Paŭlo Ebermann
@Paŭlo,你把这句话从上下文中割裂出来了,没有读到接下来的一句话...:-) 此外,E→L'和K→L之间的差异是不同的。 - MarianD
1
是的,但K中的更改也被应用了,对吧? - Jovylle
1
@Jovylle,将提交视为快照,而不是它们之间的差异。Git 不保存差异,只保存文件的当前完整内容。因此,从 KL 的更改并不重要,只有 L 的当前状态才是关键。 - MarianD

121

需要使用 cherry pick 的场景示例

考虑以下情况。您有两个分支:

a) release1 - 此分支正在向客户交付,但仍需要修复一些错误。

b) master - 经典的主分支,在此分支上可以添加例如用于 release2 的功能。

NOW 现在: 您修复了 release1 中的某个问题。 当然您还需要将此修复应用到 master 分支中。这是 cherry picking 的一个典型用例。因此,在这种情况下,cherry pick 意味着您从 release1 分支中获取一个提交,并将其包含到 master 分支中。


22
也许你只需要另一种方式。你在主代码库中修复了一个 bug,应该将其精选到 release1 分支中。此外,它们可能是仓库而不是分支。 - canbax
7
为什么不使用合并(merge)呢? - FreeLightman
4
我会创建一个从发布版本分离出来的分支,将修复内容应用到该分支中,将分支合并回发布版本,最后将发布版本合并回主分支。 - Jasper-M
我认为这个答案需要解释分支之间的关系:如果release1预计稍后会合并到master中,那么挑选可能就没有意义(依我之见)。而在挑选后,您也会想要对master1进行变基,我想是这样。 - U. Windl

78

cherry-pick是Git的一个功能。如果有人想将一个分支中特定的提交合并到目标分支中,则使用cherry-pick。
git cherry-pick 的步骤如下:

  1. 切换到目标分支。
  2. 使用命令 git cherry-pick 提交的哈希值来挑选需要合并的提交。
git cherry-pick <commit id>

这里的提交 ID 是另一个分支的活动 ID。例如:

git cherry-pick 9772dd546a3609b06f84b680340fb84c5463264f
  • 推送到目标分支
  • 访问https://git-scm.com/docs/git-cherry-pick


    28

    您可以把 cherry-pick 想象成类似于 rebase,或者更确切地说是像 rebase 一样进行管理。这意味着它会拿取现有的提交,并以您当前所在分支的头部为起点重新生成该提交。

    rebase 获取一个具有父级 X 的提交,并将该提交重新生成为实际上具有父级 Y,而这正是 cherry-pick 所做的。

    Cherry pick 更多关注的是如何选择提交。使用 pull(rebase)时,Git 会自动将您本地的提交重放到所拉取的分支之上,但是使用 cherry-pick,您需要显式地选择某个提交,并隐式地将其重新生成到您当前的分支之上。

    因此,操作方法存在差异,但在底层,它们非常相似——都是提交的重新生成。


    1
    我认为这是一种非常有帮助的事物观点。它解释了为什么在将目标分支合并回源分支时,cherry-pick表现出它所做的方式。谢谢你,先生。 - Aluan Haddad
    3
    完成一个功能后,我想使用“cherry-pick”而不是“git merge”。每当完成一个功能时,每个人总是会使用“git merge feature_branch”。为什么不使用“cherry-pick”命令呢?你有什么想法吗?如果我可以使用“cherry-pick”,为什么还要麻烦地压缩提交记录呢? - j2emanue
    1
    你可以将rebase视为reset和cherry-pick的组合。 - Paŭlo Ebermann
    1
    与真正的合并相比,cherry-pick会扁平化历史记录,如果不注意可能会在中间得到无法编译的代码。与压缩合并相比,它更难使用,因为您需要注意带上所有提交。 - Paŭlo Ebermann

    26

    它将一个特定的提交应用到您当前的分支。

    这意味着:

    • 此提交添加的所有文件将被添加
    • 此提交删除的所有文件将被删除
    • 此提交修改的所有文件将被合并。这意味着从提交中合并整个文件,而不仅仅是从此提交中的更改!

    例如:考虑提交A

    added newFileA
    modified main:
    + import './newFileA'
    

    提交B

    added newFileB
    modified main:
    + import './newFileB'
    

    如果您在另一个分支上挑选提交B,您最终会得到:

    /newFileB
    /main :
       import './newFileA'
       import './newFileB'
    

    由于提交B包含newFileBmain,但没有newFileA,导致出现了一个错误,因此请谨慎使用。


    7
    最有趣的答案,因为它谈论的是重要的东西——文件,而不是整个提交。 - mins
    这需要更多的考虑,cherry pick 的结果可能会导致危险的路径。^^ - Burgito

    18

    这有点像从某处复制粘贴到另一个地方,但是针对特定的提交记录。

    比如,如果你想进行紧急修复,你可以使用 cherry-pick 功能。

    在开发分支中进行 cherry-pick,然后将该提交合并到发布分支。同样,在发布分支中进行 cherry-pick,并将其合并到主分支中。完成!


    13

    如果您想进行无需提交ID的合并,可以使用此命令

    git cherry-pick master~2 master~0
    

    上面的命令将合并主分支从1到3的最后三个提交记录

    如果您只想对单个提交进行此操作,请删除最后一个选项

    git cherry-pick master~2
    
    这样,您将合并来自主分支末尾的第三个提交。

    这有点令人困惑。我认为你现在所在的分支不是主分支,对吗?当你提到两个提交时,是指使用<from>和<to>提交来定义你想要挑选的范围,对吗?如果能描述一下具体情况会非常有帮助。不过这是一个很好的补充。谢谢。 - Saurabh Patil

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接