【问题标题】:How to cherry-pick multiple commits如何挑选多个提交
【发布时间】:2010-12-12 20:39:15
【问题描述】:

我有两个分支。提交a 是一个的头部,而另一个在a 之上有bcdef。我想在不提交b 的情况下将cdef 移动到第一个分支。使用cherry pick 很容易:逐个签出第一个分支cherry-pick cf,然后将第二个分支重新定位到第一个分支。但是有没有办法在一个命令中挑选所有c-f

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

【问题讨论】:

  • 您提到的变基与问题无关,是吗? (我知道您以后可能希望 bf 为基础,但这与采摘樱桃无关。)

标签: git git-rebase cherry-pick


【解决方案1】:

Git 1.7.2 引入了挑选一系列提交的能力。来自release notes

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

(感谢 cmets 中的 damian、J. B. Rainsberger 和 sschaef)

【讨论】:

  • 在“cherry-pick A..B”形式中,A 应该比 B 更老。如果顺序错误,命令将静默失败。
  • 如果你有 git 1.7.1 或更早版本并且无法更新,你可以通过运行git cherry-pick f~3 然后git cherry-pick f~2 等来快速挑选它们直到git cherry-pick f (按向上箭头获取上一个命令,以便我可以快速更改数字并运行它,在大多数控制台中应该是相似的)。
  • 很高兴知道这种语法也适用于分支名称。 git cherry-pick master..somebranch 将选择自 master 以来某个分支上的所有提交(假设已经重新基于 master),并将它们应用于您当前的分支。
  • 在 Windows 的 cmd.exe 中,^ 是一个特殊字符,在 A^..B 中会被忽略。您必须将其加倍 (^^) 或将提交引用放在引号中。
  • 请注意,A^ 正在选择 A 的父提交之一。这意味着如果它有多个父提交(例如,它是一个合并提交),您必须小心。
【解决方案2】:

最简单的方法是使用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

【讨论】:

  • 谢谢!您能否也添加git checkout secondbranch && git rebase mybranch 以获得完整答案
  • 这个答案帮助我了解了在这种情况下哪个提交是哪个提交。并且:您也可以使用rebase 的交互模式。谢谢,@Charles!
  • 这种方法的美妙之处在于,您可以使用--interactive 从序列中删除一些提交或在“樱桃挑选”之前重新排序它们。 +1
  • 这是一个巧妙的命令,有点难以绕开你的脑袋,但它会产生奇迹。
  • 您必须提供您 不想 想要变基的提交作为参数之一(在此示例中为b),但是是的,这适用于我。
【解决方案3】:

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

git cherry-pick A C F J

【讨论】:

    【解决方案4】:

    或要求的单线:

    git rebase --onto a b f
    

    【讨论】:

    • 如果只是为了简洁,这是最好的答案。
    • 已投票,但如果 f 是提交(而不是分支),您将处于分离的 HEAD 状态 - 您应该编辑以添加应该像 answer below 中那样签出分支
    • 为什么要打扰其他的?
    • 应该注意,挑选一堆提交并不等同于重新定位它们。如果您查看图表,原始发布者似乎想要变基的效果,但如果您已经将提交发布到团队或公共存储库,变基是不安全的。
    【解决方案5】:

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

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

    以文本形式总结命令:

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

    这应该澄清一些事情:

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

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

    除了这个答案,我写了一个blog post,描述了另一个场景中的命令,应该有助于一般使用它。

    【讨论】:

    • 如果不再需要 mybranch(a..f 提交),这可以简化为:git rebase --onto a b mybranch 和顺便说一句 - 那些漂亮的 git 图片是哪个程序?
    • @Mr_and_Mrs_D 感谢您的评论。我想我是用cacoo.com 来画的。
    【解决方案6】:

    应用 J. B. Rainsberger 和 sschaef 的 cmets 来专门回答这个问题...在这个例子中使用一个精选范围:

    git checkout a
    git cherry-pick b..f
    

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

    【讨论】:

    • 我使用 git 2.7.0.windows.1 并注意到当我尝试挑选提交范围时一切正常,但 git 并没有告诉你在尝试提交之前你必须做的任何地方 git cherry-pick --continue | --abort | --quit/再次摘樱桃。因此,如果您选择提交范围,则每次准备好(解决冲突等)时都需要运行git cherry-pick --continue,并使用给定范围内的提交。
    • 我做的完全一样,但致命:找不到'a..b'
    • 我不知道我错在哪里,但是当我在我这边执行 'git cherry-pick c^..f' 时,这包括提交 f 但不包括提交 c。但正如我在各处阅读的那样,它应该将 c 和 f 定义为包容性。还是我错了?
    • @Samuel 是的,没错。 c 之后的 ^ 实际上表示“c 之前的提交”,在这种情况下是 b。这就是为什么c^..fb..f 的同义词。尝试执行git log c^..f,您应该会看到提交 c 到 f,与执行 git log b..f 时完全相同
    【解决方案7】:
    git rev-list --reverse b..f | xargs -n 1 git cherry-pick
    

    【讨论】:

    • 在没有冲突的情况下完美运行,否则“重新定位”可能会更容易,因为您不必弄清楚它在哪里停止并重新应用其余的补丁。
    • 请添加 cmets 解释这是做什么的
    • 因为没有人解释... git rev-list 打印从分支 b 到 f 的所有修订(反转),以便当每一行(提交哈希)按顺序传递时,它会挑选每个一个到当前的 git HEAD。即git cherry-pick {hash of c}; git cherry-pick {hash of d}; ...
    【解决方案8】:

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

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

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

    【讨论】:

      【解决方案9】:

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

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

      1。挑选一个 single 分支或提交名为 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。挑选一个范围的提交

      我最初是从most-upvoted answer by @Eric Darchis here 那里学到了这种风格的基础知识。

      请注意,要挑选一个范围的提交,您必须指定一个开始和结束提交哈希,在它们之间加上..。但是,在一系列提交中,不包括开始提交。因此,要包含它,您必须在开始提交之前指定提交。指定 preceding 提交的语法是将 ~~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~1commit^ 都表示“一个提交 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.
      

      走得更远

      还有一点需要了解:git rebase 只是一堆连续的git cherry-picks。请在此处查看我的其他答案 (Who is "us" and who is "them" according to Git?),其中我展示了我制作的关于 git rebase 的工作原理及其作用的 ASCII 绘图。

      【讨论】:

        【解决方案10】:

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

        git cherry-pick commit_id^..branch_name

        【讨论】:

        • 这已经是答案stackoverflow.com/a/31640427/96823的一部分
        • 这个答案实际上是不同的,对我很有帮助。它指定分支名称,而不是最终提交的 SHA。
        【解决方案11】:

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

        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(以及您想要的任何其他行)

        【讨论】:

          【解决方案12】:
          git format-patch --full-index --binary --stdout range... | git am -3
          

          【讨论】:

          • 请添加 cmets 解释这是做什么的
          【解决方案13】:

          这是一个脚本,您只需告诉脚本樱桃选择的源分支和目标分支以及提交的数量,即可连续选择多个提交:

          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
          

          【讨论】:

          • 当您可以直接使用 Git 执行此操作时,为什么需要此脚本?...
          • 输入更少,我的脚本会自动为您挑选提交,因此您不必通过其 SHA 挑选每个提交。无论如何,YMMV。
          【解决方案14】:

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


          如下操作:

          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
          

          【讨论】:

            猜你喜欢
            • 2022-06-30
            • 1970-01-01
            • 2014-07-04
            • 2012-04-08
            • 1970-01-01
            • 2014-10-04
            • 1970-01-01
            • 2015-03-01
            • 1970-01-01
            相关资源
            最近更新 更多