如何选择多个提交

我有两个分支。提交a是一个分支的头部,而另一个分支在a的顶部有bcdef。我想在没有提交b的情况下将cdef移动到第一个分支。使用樱桃选择很容易:逐个检查第一个分支的樱桃选择cf,并将第二个分支重新定位到第一个分支。但是有没有办法在一个命令中选择所有cf

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

输入图片描述

849495 次浏览
git format-patch --full-index --binary --stdout range... | git am -3
git rev-list --reverse b..f | xargs -n 1 git cherry-pick

最简单的方法是将onto选项添加到rebase。假设当前在a处完成的分支称为我的分支,这是您要移动c-f的分支。

# checkout mybranchgit checkout mybranch
# reset it to f (currently includes a)git reset --hard f
# rebase every commit after b and transplant it onto agit rebase --onto a b

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

git cherry-pick学会了选择一系列提交(例如cherry-pick A..Bcherry-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"(双引号)。
  • zsh shell中,它应该是'A^..B'(单引号),因为插入符号是一个特殊字符。
  • 有关说明,请参阅加布里埃尔·斯台普斯的回答

(归功于Damian,J. B. Rainsberger,Sschaef,尼泊尔,Pete和TMin在评论中。

或请求的单行代码:

git rebase --onto a b f

您可以使用git rebasegit branch的串行组合将一组提交应用到另一个分支上。就像已经由Wolfc发布一样,第一个命令实际上复制了提交。然而,在您将分支名称添加到组的最顶部提交之前,更改是不可见的。

请在新标签中打开图片…

Workflow

以文本形式总结命令:

  1. 使用命令gitk --all &gitk作为独立进程打开。
  2. 运行git rebase --onto a b f
  3. gitk中的F5。没有任何变化。但没有标记HEAD
  4. 运行git branch selection
  5. gitk中按F5。出现带有提交的新分支。

这应该澄清一些事情:

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

之后,您可以使用git checkout feature && git reset --hard bfeature分支中删除提交cf

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

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

git checkout agit cherry-pick b..f

git checkout agit cherry-pick c^..f

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

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

    git rebase ${SAVED_MERGE_BASE} -i

    或者,如果只有几个新的提交,请跳过步骤1,只需使用

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

    在第一步中,使用足够的^来移动过去的merge-base。

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

pick 3139276 commit apick c1b421d commit bpick 7204ee5 commit cpick 6ae9419 commit dpick 0152077 commit epick 2656623 commit f

然后删除行b(和任何其他你想要的)

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

git cherry-pick A C F J

这是一个脚本,它将允许您在一行中选择多个提交,只需告诉脚本樱桃选择的源和目标分支以及提交的数量:

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

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

./gcpl.sh -m

要从您的6.19. x分支中挑选最新的5个提交到master:

./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 namegit cherry-pick 1e038f108a130831f108329b1083a8139813fabc  # by full hashgit cherry-pick 1e038f10                                  # by partial hash

2.到Cherry-选择多个提交

请注意,您可以一次选择任意数量的提交哈希按照你想要的任何顺序。它们将按您指定的顺序一次应用一个。如果出现任何冲突,您必须一次解决一个,然后使用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_commitgit 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_commitgit cherry-pick beginning_commit..ending_commit

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

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

commit~~commit~2  # my preferred syntaxcommit^^

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

commit~~~commit~3   # my preferred syntaxcommit^^^

这不工作:

commit^3   # INVALID syntax

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

git log commitgit log commit~git log commit~1git 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 remotegit checkout my_branch        # ensure you're on your branch# cherry-pick their range of commitsgit cherry-pick my_branch...origin/peer_branchgit log                       # review the commits you just chery-pickedgit 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 outgit 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-pickedgit push                      # push your changes to the remote

为了您的理解,上面的cherry-pick命令,其中有3个点,是这个较长命令的完全等同

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之间找到共同的父提交哈希。这是他们从你的my_branch分叉他们的peer_branch的提交。然后,当然,你是从那一点挑选提交范围到(..)他们在origin/peer_branch的最终提交。

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

走得更远

  1. 还有一点要知道:git rebase只是一堆连续的git cherry-pick。请参阅我的另一个答案(根据Git,谁是“我们”,谁是“他们”?),其中我展示了我制作的ASCII绘图,其中包括git rebase的工作原理及其作用。
  2. 在Git diff提交范围中,双点"…"和三点"…"有什么区别?
  3. 我的答案从另一个分支在git中创建分支的各种方法

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


要做的程序如下:

  1. 从“dev”分支获取1个带有名称的提交(“删除姓氏字段”)
  2. 将其提交到“hotFix1”分支

1.从“dev”分支获取提交详细信息

// Go to "dev" branchgit checkout dev
// Get the commit id (1e2e3e4e1 here)git log --oneline
> ...> ...> 1e2e3e4e1     Remove Last Name field> ...> ...

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

// Go to "hotfix1" branchgit checkout hotfix1
// Get the commit (1e2e3e4e1) from "dev" branch to "hotfix1" branchgit cherry-pick 1e2e3e4e1
// verify changes are correctgitk
// push to "hotfix1" branchgit push

要一次做多个,上面只有1个更改,按顺序给出所有提交ID:

git cherry-pick 1e2e3e4e1 1e2e3e4e2 1e2e3e4e3

或者使用github桌面应用程序

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

除此之外,您可以通过管道从标准输入SHA列表

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

rev-list基本上是git-log管道命令(“丑陋”但快速的表亲)注意:需要#0

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

Cherry选择多个提交:

结帐到您想要选择提交的分支

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

git chury-pice1e038f10 1e038f11 1e038f12…