1217

我有两个分支。Commita是一个的头部,而另一个有b, c, d,efa. 我想在没有提交的情况下移动c,def第一个分支b。使用cherry pick 很容易:逐个签出第一个分支cherry-pickc并将f第二个分支重新设置到第一个分支上。但是有没有办法在一个命令中挑选所有cf

这是场景的视觉描述(感谢JJD):

在此处输入图像描述

4

15 回答 15

1867

Git 1.7.2 引入了挑选一系列提交的能力。从发行说明

git cherry-pick学会了选择一系列提交(例如cherry-pick A..Band cherry-pick --stdin),所以也学会了git revert;但是,这些不支持更好的排序控制rebase [-i]

要从提交中挑选所有A提交B(其中A早于B),请运行:

git cherry-pick A^..B

如果要忽略A 本身,请运行:

git cherry-pick A..B

评论中的注释:

  • A应该早于B,或者A应该来自另一个分支。
  • 在 Windows 上,应该是A^^..B插入符号需要转义,或者应该是"A^..B"(双引号)。
  • zshshell 中,它应该是'A^..B'(单引号),因为插入符号是一个特殊字符。
  • 有关说明,请参阅Gabriel Staples 的答案

(在评论中感谢 damian、JB Rainsberger、sschaef、Neptilo、Pete 和 TMin。)

于 2010-10-14T13:08:39.700 回答
129

最简单的方法是onto使用rebase. 假设当前完成的分支a称为 mybranch ,这是您要移动的分支c-f进入。

# checkout mybranch
git checkout mybranch

# reset it to f (currently includes a)
git reset --hard f

# rebase every commit after b and transplant it onto a
git rebase --onto a b
于 2009-11-04T08:13:13.607 回答
121

如果您有选择性的修改要合并,比如 A、B、C、D、E、F、G、H、I、J 提交中的 A、C、F、J,只需使用以下命令:

git 樱桃挑选 ACFJ

于 2018-02-20T10:00:36.563 回答
94

或要求的单线:

git rebase --onto a b f
于 2011-05-25T15:12:16.107 回答
77

您可以使用git rebase和的串行组合git branch将一组提交应用到另一个分支。正如wolfc 已经发布的那样,第一个命令实际上复制了提交。但是,在您将分支名称添加到组的最顶部提交之前,更改是不可见的。

请在新标签页中打开图片...

工作流程

以文本形式总结命令:

  1. 使用以下命令将gitk作为独立进程打开: gitk --all &.
  2. 运行git rebase --onto a b f
  3. gitkF5_ 没有什么变化。但是没有标记。HEAD
  4. git branch selection
  5. gitkF5_ 出现带有提交的新分支。

这应该澄清一些事情:

  • 提交a是组的新根目的地。
  • Commitb是组的第一次提交之前的提交(独占)。
  • Commitf是组的最后一次提交(包括)。

之后,您可以使用从分支git checkout feature && git reset --hard b中删除提交。cffeature

除了这个答案,我还写了一篇博客文章,描述了另一个场景中的命令,应该有助于一般使用它。

于 2012-09-28T20:30:24.353 回答
55

应用 JB Rainsberger 和 sschaef 的评论来专门回答这个问题......在这个例子中使用一个樱桃挑选范围:

git checkout a
git cherry-pick b..f

或者

git checkout a
git cherry-pick c^..f
于 2015-07-26T18:25:40.197 回答
32

如何挑选单个提交、多个提交或一系列提交

...到您当前签出的分支:

1. 挑选一个分支或提交命名commit

git cherry-pick commit

例子:

git cherry-pick my_branch                                 # by branch name
git cherry-pick 1e038f108a130831f108329b1083a8139813fabc  # by full hash
git cherry-pick 1e038f10                                  # by partial hash

2. 挑选多个提交

请注意,您可以一次挑选任意数量的提交哈希,并以您想要的任何顺序。它们将按您指定的顺序一次一个地应用。如果出现任何冲突,您必须一次解决一个,然后在完成后使用git add my_filethengit cherry-pick --continue来继续挑选过程。

git cherry-pick commit1 commit2 commit3 commit4 commit5

3. 挑选一系列提交

我最初是从@Eric Darchis here 的最受好评的回答中学到了这种风格的基础知识。

请注意,要挑选一系列提交,您必须..在它们之间指定一个开始和结束提交哈希。但是,在一系列提交中,不包括开始提交。因此,要包含它,您必须在开始提交之前指定提交。指定前面提交的语法是在提交之后放置~~1或,如:,这意味着:“之前的提交”。^beginning_commit~beginning_commit

# A. INCLUDING the beginning_commit
git cherry-pick beginning_commit~..ending_commit
# OR (same as above)
git cherry-pick beginning_commit~1..ending_commit
# OR (same as above)
git cherry-pick beginning_commit^..ending_commit 

# B. NOT including the beginning_commit
git cherry-pick beginning_commit..ending_commit

注意: commit~ , commit~1, 和commit^都表示“一个之前的 commit提交”,或者说:“之前的提交commit”。

要在 之前指定两次提交commit,可以使用如下语法:

commit~~
commit~2  # my preferred syntax
commit^^

要在 之前指定三个提交commit,您可以这样做:

commit~~~  
commit~3   # my preferred syntax
commit^^^

这不起作用:

commit^3   # INVALID syntax

要自己测试上述“以前的提交语法”概念,最简单的方法是使用git log命令。前任:

git log commit
git log commit~
git log commit~1
git log commit^
git log commit~~
git log commit~5
# etc.

4. 挑选你的同行提交到你的分支的范围

...当他们的分支peer_branch从您的分支的早期版本中分出时my_branch

快速总结

# you cherry-pick all of their extra commits from their `peer_branch` onto 
# your `my_branch` (note: the 3 dots below are very important!)

git fetch origin peer_branch  # get their latest changes from the remote
git checkout my_branch        # ensure you're on your branch
# cherry-pick their range of commits
git cherry-pick my_branch...origin/peer_branch  
git log                       # review the commits you just chery-picked
git push                      # push your changes to the remote

完整的细节和工作流程演练

假设您正在开发您的功能分支my_branch,并且您的同行想要帮助您进行一些更改以帮助您完成您的功能。您已经推my_branch送到名为origin. 因此,他们将把你的远程分支获取my_branch到他们的本地计算机,peer_brach从它分叉出他们自己的分支,然后推送到他们自己的名为peer_branch. 一旦他们这样做,您将立即挑选所有添加的内容。这是这个过程的第一部分的样子:

# **your peer** does this

# peer fetches your branch named `my_branch` and forks their `peer_branch`
# off of it

# they fetch your latest work from remote `my_branch` into their locally-stored
# remote-tracking "hidden" branch named `origin/my_branch`
# (note: you can see all locally-stored remote-tracking "hidden" branches
# with `git branch -r`)
git fetch origin my_branch
# create `peer_branch` as a fork off of `origin/my_branch`, and check it out
git checkout -b peer_branch origin/my_branch

# Now they can add their changes and commits and `git push` to remote `origin`
# as their own `peer_branch` when done.

现在他们已将所有更改origin作为他们自己的分支推送到远程peer_branch,您可以像这样挑选他们在您的工作之上添加的所有提交:

# **you** do this to cherry-pick your peer's helpful changes they added to 
# your work

# you fetch their latest work from their branch named `peer_branch` on remote
# `origin` into your locally-stored remote-tracking "hidden" branch named 
# `origin/peer_branch` 
# (note: you can see all locally-stored remote-tracking "hidden" branches
# with `git branch -r`)
git fetch origin peer_branch
# ensure you are on `my_branch` (if you aren't already)
git checkout my_branch
# you cherry-pick all of their extra commits from their `peer_branch` onto 
# your `my_branch` (note: the 3 dots here are very important!)
git cherry-pick my_branch...origin/peer_branch

git log                       # review the commits you just chery-picked
git push                      # push your changes to the remote

为了您的理解,cherry-pick上面那个带有 3 个点的命令完全等同于这个更长的命令:

git cherry-pick $(git merge-base my_branch origin/peer_branch)..origin/peer_branch

该部分在 branch和 branchgit merge-base my_branch origin/peer_branch之间找到共同的父提交哈希。这是他们从你的. 然后,当然,您正在挑选从该点到 ( ) 的最终提交的提交范围my_branchorigin/peer_branchpeer_branchmy_branch..origin/peer_branch

要了解有关 3 点语法的更多信息,请参阅此处:Git diff 提交范围中双点“..”和三点“...”之间的区别是什么?[重复]。如需帮助git checkout -b new_branch from_branch,请在此处查看我的答案:从另一个分支在 git 中创建分支的各种方法

走得更远

  1. 其他要知道的事情:agit rebase只是一堆连续git cherry-pick的 s。请在此处查看我的另一个答案(根据 Git,谁是“我们”,谁是“他们”?),其中我展示了我制作的 ASCII 绘图,其中包含 a 的git rebase工作原理和正在做什么。
  2. Git diff 提交范围中的双点“..”和三点“...”有什么区别?[复制]
  3. 我对从另一个分支在 git 中创建分支的各种方法的回答
于 2021-10-06T20:14:17.490 回答
25
git rev-list --reverse b..f | xargs -n 1 git cherry-pick
于 2009-11-04T03:59:07.277 回答
19

另一个值得一提的变体是,如果您想要n分支的最后一次提交,则~语法可能很有用:

git cherry-pick some-branch~4..some-branch

在这种情况下,上述命令将从名为的分支中选择最后 4 次提交some-branch(尽管您也可以使用提交哈希代替分支名称)

于 2019-11-13T10:27:33.187 回答
16

要从提交 id 到分支的顶端进行挑选,您可以使用:

git cherry-pick commit_id^..branch_name

于 2019-06-13T08:48:10.363 回答
3

实际上,最简单的方法可能是:

  1. 记录两个分支之间的合并基础:MERGE_BASE=$(git merge-base branch-a branch-b)
  2. 快进或将旧分支重新定位到新分支
  3. 从步骤 1 的合并基础开始,将生成的分支重新定位到自身,并手动删除不需要的提交:

    git rebase ${SAVED_MERGE_BASE} -i
    

    或者,如果只有几个新提交,跳过第 1 步,直接使用

    git rebase HEAD^^^^^^^ -i
    

    在第一步中,使用足够^的移动通过合并基础。

您将在交互式 rebase 中看到类似的内容:

pick 3139276 commit a
pick c1b421d commit b
pick 7204ee5 commit c
pick 6ae9419 commit d
pick 0152077 commit e
pick 2656623 commit f

然后删除行 b (以及您想要的任何其他行)

于 2017-12-12T07:13:25.913 回答
2

我需要优先选择从一个分支到另一个分支的提交,但是这里的提交很难理解,希望下面对一个简单的提交有所帮助:


流程如下:

  1. 从“dev”分支获取 1 个带有名称的提交(“删除姓氏字段”)
  2. 在“hotfix1”分支中提交

1. 从“dev”分支获取提交细节

// Go to "dev" branch
git checkout dev

// Get the commit id (1e2e3e4e1 here)
git log --oneline

    > ...
    > ...
    > 1e2e3e4e1     Remove Last Name field
    > ...
    > ...

2. 将提交推送到“hotfix1”分支

// Go to "hotfix1" branch
git checkout hotfix1

// Get the commit (1e2e3e4e1) from "dev" branch to "hotfix1" branch
git cherry-pick 1e2e3e4e1

// verify changes are correct
gitk

// push to "hotfix1" branch
git push

一次做多个,上面只做1个改动,依次给出所有的commit id:

git cherry-pick 1e2e3e4e1 1e2e3e4e2 1e2e3e4e3

于 2021-12-01T05:37:34.853 回答
0

这是一个脚本,它允许您通过简单地告诉脚本樱桃选择的源分支和目标分支以及提交的数量来连续选择多个提交:

https://gist.github.com/nickboldt/99ac1dc4eb4c9ff003a1effef2eb2d81

要从您的分支中挑选到 master(使用当前分支作为源):

./gcpl.sh -m

要从 6.19.x 分支中挑选最新的 5 个提交来掌握:

./gcpl.sh -c 5 -s 6.19.x -t master
于 2019-03-19T17:22:49.690 回答
0

或者使用GitHub 桌面应用程序

您可以在源分支的历史选项卡中多选提交,然后右键单击以获取选项“Cherry-Pick Selected Commits”。

于 2022-01-31T07:29:31.680 回答
-1
git format-patch --full-index --binary --stdout range... | git am -3
于 2009-11-04T01:08:16.123 回答