如何樱桃挑选多个提交

tig*_*tig 779 git git-rebase cherry-pick

我有两个分支.提交a是一个头,而其他有b,c,d,ef之上a.我想移动c,d,ef第一支没有提交b.使用樱桃挑选很容易:结帐第一个分支樱桃 - 一个接一个c地挑选f第二个分支到第一个.但有没有办法挑选所有c- f一个命令?

这是场景的直观描述(感谢JJD):

在此输入图像描述

Eri*_*his 1141

Git 1.7.2引入了一系列提交的能力.从发行说明:

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


包括重要评论(各自作者的学分)

注1:在"cherry-pick A..B"形式中,A应该比B更旧.如果它们的顺序错误,命令将无声地失败. - 达米安

注2:此外,这不会选择A,而是A之后的所有内容,包括B. - JB Rainsberger

注3:要包括一个只是类型git cherry-pick A ^ .. B - sschaef

  • 包括A只是键入`git cherry-pick A ^ .. B` (419认同)
  • 此外,这不会挑选A,而是A之后的所有内容,包括B. (271认同)
  • 在"cherry-pick A..B"形式中,A应该比B更旧.如果它们的顺序错误,命令将无声地失败. (231认同)
  • 知道此语法也适用于分支名称可能会很好.`git cherry-pick master..somebranch`将在master之后选择somebranch上的所有提交(假设已经重新设置为master),并将它们应用到当前分支. (16认同)
  • 如果你有git 1.7.1或更早版本并且无法更新,你可以通过运行`git cherry-pick f~3`然后`git cherry-pick f~2`等来快速挑选它们. `git cherry-pick f`(按向上箭头获取上一个命令,这样我可以快速更改数字并运行它,在大多数控制台中应该类似). (15认同)
  • 在Windows的cmd.exe中,“ ^”是一个特殊字符,在“ A ^ .. B”中被忽略。您必须将其加倍(`^^`)或将提交引用加引号。 (3认同)
  • 请注意,`A^` 正在选择 A 的父提交之一。这意味着如果它有多个父提交(例如,它是一个合并提交),您必须小心。 (2认同)
  • cherry-pick ... -n 给你最大的控制权。它只分阶段,不提交。所以你仍然可以看看你要做什么。 (2认同)

Gab*_*les 140

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

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

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

git cherry-pick commit
Run Code Online (Sandbox Code Playgroud)

例子:

git cherry-pick my_branch                                 # by branch name
git cherry-pick 1e038f108a130831f108329b1083a8139813fabc  # by full hash
git cherry-pick 1e038f10                                  # by partial hash
Run Code Online (Sandbox Code Playgroud)

2. 挑选多个提交

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

git cherry-pick commit1 commit2 commit3 commit4 commit5
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

注意: commit~commit~1和all 表示“之前的commit^一次提交”,或者换句话说:“之前的提交”。 commitcommit

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

commit~~
commit~2  # my preferred syntax
commit^^
Run Code Online (Sandbox Code Playgroud)

要指定之前的三个commit提交,您可以执行以下操作:

commit~~~  
commit~3   # my preferred syntax
commit^^^
Run Code Online (Sandbox Code Playgroud)

这不起作用:

commit^3   # INVALID syntax
Run Code Online (Sandbox Code Playgroud)

确实有效,但这是一个非常特殊的情况。另请参阅我的问答:git merge-style 工作流程中,仅显示合并之前某人在其 PR(拉取请求)功能分支中拥有的唯一提交

# valid on **merge commits** only
commit^2   # this is the immediate **right** parent of the two parents 
           # involved in the merge (which made commit `commit`)

# valid on any commits (gets the left parent of a merge commit, or 
# the only parent of a non-merge commit)
commit~    # and this is the immediate **left** parent of the two 
           # parents involved in the merge (which made commit `commit`)
Run Code Online (Sandbox Code Playgroud)

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

git log commit
git log commit~
git log commit~1
git log commit^
git log commit~~
git log commit~5
# etc.
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

提交范围内 2 点和 3 点之间的差异非常显着。git diff branch1...branch2相当于git diff $(git merge-base branch1 branch2) branch2. branch2当您想要查看自 偏离 以来所做的更改branch1,而不是两个分支在当前状态下的差异时,这非常有用。请参阅此处和此处的评论以及此处的答案:What are the Differences between double-dot ".." and Triple-dot "..." in Git diff commit Ranges?

完整的详细信息和工作流程演练

假设您正在开发功能分支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.
Run Code Online (Sandbox Code Playgroud)

现在他们已经将所有更改推送到远程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
Run Code Online (Sandbox Code Playgroud)

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

git cherry-pick $(git merge-base my_branch origin/peer_branch)..origin/peer_branch
Run Code Online (Sandbox Code Playgroud)

该部分查找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 中创建分支的各种方法

官方 Git 文档

  1. https://git-scm.com/docs/gitrevisions - 提到 git commit 3 点 ( ...) 与 2 点范围语法、^commit("not" commit)、commit^( 的父级commit) 等。

更进一步

  1. 还有一点需要知道: agit rebase只是一堆连续的git cherry-picks。请参阅我的其他答案(根据 Git,谁是“我们”,谁是“他们”?),其中我展示了我制作的 ASCII 绘图,其中显示了 a 的git rebase工作原理和正在执行的操作。
  2. Git diff 提交范围中的双点“..”和三点“...”有什么区别?[复制]
  3. 我对从另一个分支在 git 中创建分支的各种方法的回答
  4. 我的问答:git merge风格的工作流程中,仅显示合并之前某人在其 PR(拉取请求)功能分支中的唯一提交


CB *_*ley 94

最简单的方法是使用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
Run Code Online (Sandbox Code Playgroud)

  • 这个答案对我理解在这种情况下哪个提交是哪个提交有很大帮助。并且:您也可以使用 `rebase` 的交互模式。谢谢,@查尔斯! (2认同)
  • 这种方法的美妙之处在于您可以使用`--interactive`从序列中删除一些提交或在“樱桃选择”之前对它们重新排序。+1 (2认同)
  • 狂野的是,您必须给出您不想重新设置的提交作为参数之一(本例中为“b”),但是是的,这对我有用。 (2认同)

小智 79

或者要求的单行:

git rebase --onto a b f
Run Code Online (Sandbox Code Playgroud)

  • 如果f是提交(而不是分支),Upvoted但会让你处于分离的HEAD状态 - 你应该编辑添加一个应该签出一个分支,如[下面的回答](http://stackoverflow.com/a/ 281545分之12646996) (9认同)
  • 如果只是为了简洁,这是最好的答案. (5认同)
  • 应该注意的是,挑选一堆提交并不等于重新调整它们的基础。如果你看一下图表,原始发帖者似乎想要 rebase 的效果,但是如果您已经将提交发布到团队或公共存储库,则 rebase 并不安全。 (2认同)

JJD*_*JJD 65

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

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

工作流程

以文本形式总结命令:

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

这应该澄清一些事情:

  • Commit a是组的新根目标.
  • Commit b是第一次提交组(独占)之前的提交.
  • 提交f是该组的最后一次提交(包括).

之后,你可以使用git checkout feature && git reset --hard b删除提交c,直到ffeature分支.

除了这个答案之外,我写了一篇博文,其中描述了另一种场景中的命令,这些命令应该有助于普遍使用它.

  • 如果不再需要mybranch(a..f提交),这可以简化为:`git rebase --onto ab mybranch`和btw - 哪个程序可以做那些漂亮的git图片? (2认同)
  • @Mr_and_Mrs_D感谢您的评论.我想我用http://cacoo.com来画画. (2认同)

And*_*ndy 40

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

git checkout a
git cherry-pick b..f
Run Code Online (Sandbox Code Playgroud)

要么

git checkout a
git cherry-pick c^..f
Run Code Online (Sandbox Code Playgroud)

  • 我使用`git 2.7.0.windows.1`并注意到,当我尝试樱桃选择提交范围时一切都还可以,但是git并没有告诉你任何地方你必须要做的事情`git cherry-pick --continue | --abort | 在你尝试再次提交/樱桃选择之前 - 你.因此,如果您选择提交范围,则每次准备好(解决冲突等)时都需要运行`git cherry-pick --continue`并使用给定范围的提交. (2认同)

Shr*_*are 24

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

git cherry-pick ACFJ


Dus*_*tin 19

git rev-list --reverse b..f | xargs -n 1 git cherry-pick
Run Code Online (Sandbox Code Playgroud)

  • 因为没有人解释... git rev-list打印从分支b到f(反向)的所有修订,以便当按顺序传递每一行(提交哈希)时,它会将每一个选择到当前的git HEAD.即`git cherry-pick {hash of c}; git cherry-pick {hash of d}; ...` (6认同)
  • 请添加评论,解释这是做什么的 (4认同)
  • 如果没有冲突,则工作完美,否则"rebase into"可能会更容易,因为您不必弄清楚停止的位置并重新应用其余的补丁. (2认同)

Nic*_*k F 16

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

git cherry-pick some-branch~4..some-branch
Run Code Online (Sandbox Code Playgroud)

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


Ayu*_*pta 10

择优挑选多个提交:

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

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

git cherry-pick 1e038f10 1e038f11 1e038f12 ...
Run Code Online (Sandbox Code Playgroud)


ut9*_*081 5

要从提交ID到分支的尖端进行挑选,可以使用:

git cherry-pick commit_id^..branch_name

  • 这个答案实际上是不同的,对我有帮助。它指定分支名称,而不是最终提交的 SHA。 (6认同)