我有两个分支。提交a是一个的头,而另一个有b, c, d, e和f在a的顶部。我想移动c, d, e和f到第一个分支,而不提交b。使用樱桃采摘很容易:签出第一个分支樱桃采摘一个接一个c到f,并重新建立第二个分支到第一个。但是有没有办法在一个命令中选择所有的c-f ?

下面是场景的可视化描述(感谢JJD):


git format-patch --full-index --binary --stdout range... | git am -3

git rev-list --reverse b..f | xargs -n 1 git cherry-pick

git rev-list打印从分支b到分支f(反向)的所有修订,这样当每一行(提交哈希)按顺序传递时,它会将每一行都樱桃式地选择到当前git HEAD中。例如,git选择{哈希的c};Git选择{哈希的d};... ——@coderatchet评论


最简单的方法是使用on选项来调整基数。假设电流在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

Git 1.7.2引入了樱桃选择一系列提交的能力。发布说明如下:

Git学会了选择一个提交范围 (例如cherry-pick A..B和cherry-pick——stdin),所以git恢复;但是,这些不支持rebase [-i]拥有的更好的排序控件。

要从commit A挑选所有提交到commit B(其中A比B老),运行:

git cherry-pick A^..B

如果你想忽略A本身,运行:

git cherry-pick A..B

评论注释:

A应该比B老,或者A应该来自另一个分支。 在Windows上,它应该是A^^..B作为插入符需要转义,或者它应该是“A^..”B”(双引号)。 在zsh shell中,它应该是'A^..B'(单引号)作为插入符号是一个特殊字符。 有关说明,请参阅Gabriel Staples的回答。

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


或者请求的一行代码:

git rebase --onto a b f

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

请在新标签页打开图片…

以文本形式总结命令:

使用命令:gitk——all &将gitk作为一个独立的进程打开。 运行git rebase——to a bf。 在gitk中按F5。没有什么变化。但是没有HEAD标记。 运行git分支选择 在gitk中按F5。新的分支及其提交将出现。

这应该澄清一些事情:

Commit a是组的新根目的地。 提交b是组的第一个提交之前的提交(独占)。 提交f是组(包括)的最后一次提交。

然后,你可以使用git checkout功能&& git reset—hard b从功能分支中删除c到f的提交。

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


运用J. B. Rainsberger和sschaef的评论来具体回答这个问题…在这个例子中使用樱桃选择范围:

git checkout a
git cherry-pick b..f

or

git checkout a
git cherry-pick c^..f

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

记录两个分支之间的merge-base: MERGE_BASE=$(git merge-base branch-a branch-b) 快进或将旧的分支重置到新的分支上 从步骤1的merge base开始,将生成的分支重新基于自身,并手动删除不需要的提交: ${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(和任何其他你想要的)


如果你有选择性的修订要合并,比如从A,B,C,D,E,F,G,H,I,J提交A, C, F, J,简单地使用下面的命令:

git cherry-pick A C F J

这里有一个脚本,允许你在一行中选择多个提交,只需告诉脚本哪个源和目标分支的樱桃选择和提交的数量:

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

从你的分支中选择master(使用当前分支作为源):

./gcpl.sh -m

从6.19中挑选最新的5个提交。X科要掌握:

./gcpl.sh -c 5 -s 6.19.x -t master

要从一个提交id中选取到分支的顶端,你可以使用:

git cherry-pick commit_id^..branch_name

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

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

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


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

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

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_file,然后git cherry-pick——continue来继续选择过程。

git cherry-pick commit1 commit2 commit3 commit4 commit5

3.选择一系列提交

我最初是从@Eric Darchis得到最多赞的回答中学到这种风格的基础知识。

注意,要选择一个提交范围,必须指定一个开始和结束提交散列,使用..他们之间。但是,在一系列提交中,不包括开始提交。因此,要包含它,必须在开始提交之前指定提交。指定之前提交的语法是将~、~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~2  # my preferred syntax
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_branch的分支,然后推送到名为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.

现在他们已经把所有的更改都推到了远程的原点,作为他们自己名为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

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

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

git merge-base my_branch origin/peer_branch部分查找分支my_branch和分支origin/peer_branch之间的公共父提交哈希。这是他们将peer_branch从my_branch中分叉的提交。然后,当然,您可以选择从该点到(..)它们在origin/peer_branch上的最终提交的提交范围。

要阅读更多关于3点语法的内容,请参见这里:Git diff提交范围中双点“..”和三点“…”之间有什么区别?(重复)。关于git checkout -b new_branch from_branch的帮助,请参阅我的回答:从另一个分支创建git分支的各种方法

要进一步

还有一点需要知道:git rebase只是一堆连续的git精选。在这里可以看到我的另一个答案(根据Git,谁是“我们”,谁是“他们”?),在这里我展示了我所做的一个ASCII图,其中包括Git是如何变基的以及它正在做什么。 在Git的diff提交范围中,双点“..”和三点“…”有什么区别?(复制) 在git中从另一个分支创建一个分支的各种方法



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


步骤如下:

从“dev”分支获取1个带名字的提交(“移除姓氏字段”) 在"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

如果要一次做多个,只需要在上面修改一个,按顺序给出所有的提交id:

git cherry-pick 1e2e3e4e1 1e2e3e4e2 1e2e3e4e3


或者使用GitHub桌面应用程序,

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


除了提交之外,还可以通过管道从stdin输入sha列表。

git rev-list --reverse ..main -- path/ | git cherry-pick --stdin 

Rev-list基本上是git-log的管道命令(“丑陋”但快速的表亲) 注意——反向是需要的。

您可以通过这种方式执行更高级的操作,而不仅仅是提交范围。


选择多次提交:

签出到您想要选择提交的分支

使用以下命令:(通过部分哈希)

git cherry-pick 1e038f10 1e038f11 1e038f12 ...