761

我有以下存储库布局:

  • 主分支(生产)
  • 一体化
  • 在职的

我想要实现的是从工作分支中挑选一系列提交并将其合并到集成分支中。我对 git 很陌生,我无法弄清楚如何在不弄乱存储库的情况下准确地做到这一点(在一个操作中挑选提交范围,而不是合并)。对此有任何指示或想法吗?谢谢!

4

10 回答 10

935

当涉及到一系列提交时,挑选樱桃 实用。

正如下面Keith Kim所提到的,Git 1.7.2+ 引入了挑选一系列提交的能力(但您仍然需要注意挑选樱桃以备将来合并的后果

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

damian 评论并警告我们:

在 " cherry-pick A..B" 形式中,A应该比B.
如果它们的顺序错误,命令将静默失败

如果您想通过(包括选择范围BDBB^..D(而不是B..D)。
请参阅“ Git create branch from range of previous commits? ”作为说明。

正如Jubobs在评论中提到的:

这假设这B不是根提交;否则你会得到一个“ unknown revision”错误。

注意:从 Git 2.9.x/2.10(2016 年第三季度)开始,您可以直接在孤立分支(空头)上挑选一系列提交:请参阅“如何使现有分支成为 git 中的孤立分支”。


原始答案(2010 年 1 月)

Arebase --onto会更好,您可以在集成分支上重放给定的提交范围,正如Charles Bailey 所描述的那样。(另外,在git rebase 手册页
中查找“这是如何将基于一个分支的主题分支移植到另一个分支的方法” ,以查看一个实际示例)git rebase --onto

如果您当前的分支是集成:

# Checkout a new temporary branch at the current location
git checkout -b tmp

# Move the integration branch to the head of the new patchset
git branch -f integration last_SHA-1_of_working_branch_range

# Rebase the patchset onto tmp, the old location of integration
git rebase --onto tmp first_SHA-1_of_working_branch_range~1 integration

这将重播以下之间的所有内容:

  • first_SHA-1_of_working_branch_range在(因此是)的父级之后~1:您要重播的第一个提交
  • 直到“ integration”(它指向你想要重放的最后一个提交,从working分支)

到“ tmp”(integration指向之前指向的位置)

如果在重放其中一个提交时存在任何冲突:

  • 要么解决它并运行“ git rebase --continue”。
  • 或者跳过这个补丁,而是运行“ git rebase --skip
  • 或用“ git rebase --abort”取消所有东西(并将分支放回integration分支上tmp

之后rebase --ontointegration将回到集成分支的最后一次提交(即“ tmp”分支+所有重放的提交)

使用cherry-picking or rebase --onto,不要忘记它会对后续合并产生影响,如此处所述


这里讨论了一个纯粹的“ cherry-pick”解决方案,并且将涉及以下内容:

如果你想使用补丁方法,那么“git format-patch|git am”和“git cherry”是你的选择。
目前,git cherry-pick只接受一个提交,但如果你想选择 git lingo 中的范围,B所以DB^..D

git rev-list --reverse --topo-order B^..D | while read rev 
do 
  git cherry-pick $rev || break 
done 

但无论如何,当你需要“重播”一系列提交时,“重播”这个词应该会促使你使用rebaseGit 的“”特性。

于 2010-01-03T10:08:04.123 回答
150

从 git v1.7.2 开始,cherry pick 可以接受一系列提交:

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

于 2010-09-08T03:57:31.417 回答
86

假设你有 2 个分支,

"branchA" : 包括你想要复制的提交(从 "commitA" 到 "commitB"

“branchB”:您希望从“branchA”转移提交的分支

1)

 git checkout <branchA>

2)获取“commitA”和“commitB”的ID

3)

git checkout <branchB>

4)

git cherry-pick <commitA>^..<commitB>

5)如果你有冲突,解决它并输入

git cherry-pick --continue

继续挑选樱桃的过程。

于 2017-08-01T14:29:48.193 回答
33

你确定你不想真正合并分支吗?如果工作分支有一些你不想要的最近提交,你可以在你想要的位置创建一个带有 HEAD 的新分支。

现在,如果您真的想挑选一系列提交,无论出于何种原因,一个优雅的方法就是拉出一个补丁集并将其应用于您的新集成分支:

git format-patch A..B
git checkout integration
git am *.patch

这基本上就是 git-rebase 正在做的事情,但不需要玩游戏。如果需要合并,--3way可以添加。git-am如果您逐字按照说明进行操作,请确保您执行此操作的目录中没有其他 *.patch 文件...

于 2010-01-04T09:32:52.277 回答
9

我将VonC 的代码包装成一个简短的 bash 脚本git-multi-cherry-pick,以便于运行:

#!/bin/bash

if [ -z $1 ]; then
    echo "Equivalent to running git-cherry-pick on each of the commits in the range specified.";
    echo "";
    echo "Usage:  $0 start^..end";
    echo "";
    exit 1;
fi

git rev-list --reverse --topo-order $1 | while read rev 
do 
  git cherry-pick $rev || break 
done 

我目前正在使用它,因为我重建了一个项目的历史,该项目在同一个 svn 主干中混合了第 3 方代码和自定义项。我现在将核心第 3 方代码、第 3 方模块和自定义拆分到他们自己的 git 分支上,以便更好地理解未来的自定义。git-cherry-pick在这种情况下很有帮助,因为我在同一个存储库中有两棵树,但没有共享祖先。

于 2010-03-31T14:20:56.950 回答
7

git cherry-pick start_commit_sha_id ^.. end_commit_sha_id

例如git cherry-pick 3a7322ac^..7d7c123c

假设您在要从branchA中选择提交的位置(已给出范围的开始和结束提交 SHA,而左提交 SHA 较旧branchB。整个提交范围(包括在内)将在branchA.

官方文档中给出的示例非常有用。

于 2020-07-17T12:47:46.387 回答
4

几天前,在阅读了 Vonc 的非常清晰的解释后,我已经对此进行了测试。

我的脚步

开始

  • 分公司dev:ABCDEFGHIJ
  • 分公司target:ABCD
  • 我不想E也不H

在分支中没有步骤 E 和 H 的复制特征的步骤dev_feature_wo_E_H

  • git checkout dev
  • git checkout -b dev_feature_wo_E_H
  • git rebase --interactive --rebase-merges --no-ff D我放在rebase编辑器drop前面E和里面的地方H
  • 解决冲突,继续并commit

复制dev_feature_wo_E_H目标上的分支的步骤。

  • git checkout target
  • git merge --no-ff --no-commit dev_feature_wo_E_H
  • 解决冲突,继续并commit

一些备注

  • cherry-pick我这样做是因为前几天太多了
  • git cherry-pick功能强大且简单但

    • 它创建重复提交
    • 当我想要merge解决初始提交和重复提交的冲突时,因此对于一两个cherry-pick,“采摘”是可以的,但对于更多,它太冗长并且分支会变得太复杂
  • 在我看来,我所做的步骤比git rebase --onto
于 2020-02-15T09:36:15.167 回答
3

以上所有选项都会提示您解决合并冲突。如果您正在合并为团队提交的更改,则很难从开发人员那里解决合并冲突并继续。但是,“git merge”将一次性完成合并,但您不能将一系列修订作为参数传递。我们必须使用“git diff”和“git apply”命令来合并转速范围。我观察到如果补丁文件有太多文件的差异,“git apply”将失败,因此我们必须为每个文件创建一个补丁然后应用。请注意,该脚本将无法删除源分支中已删除的文件。这是一种罕见的情况,您可以从目标分支手动删除此类文件。如果“git apply”无法应用补丁,则退出状态不为零,

下面是脚本。

enter code here



  #!/bin/bash

    # This script will merge the diff between two git revisions to checked out branch
    # Make sure to cd to git source area and checkout the target branch
    # Make sure that checked out branch is clean run "git reset --hard HEAD"


    START=$1
    END=$2

    echo Start version: $START
    echo End version: $END

    mkdir -p ~/temp
    echo > /tmp/status
    #get files
    git --no-pager  diff  --name-only ${START}..${END} > ~/temp/files
    echo > ~/temp/error.log
    # merge every file
    for file in `cat  ~/temp/files`
    do
      git --no-pager diff --binary ${START}..${END} $file > ~/temp/git-diff
      if [ $? -ne 0 ]
      then
#      Diff usually fail if the file got deleted 
        echo Skipping the merge: git diff command failed for $file >> ~/temp/error.log
        echo Skipping the merge: git diff command failed for $file
        echo "STATUS: FAILED $file" >>  /tmp/status
        echo "STATUS: FAILED $file"
    # skip the merge for this file and continue the merge for others
        rm -f ~/temp/git-diff
        continue
      fi

      git apply  --ignore-space-change --ignore-whitespace  --3way --allow-binary-replacement ~/temp/git-diff

      if [ $? -ne 0 ]
       then
#  apply failed, but it will fall back to 3-way merge, you can ignore this failure
         echo "git apply command filed for $file"
       fi
       echo
       STATUS=`git status -s $file`


       if [ ! "$STATUS" ]
       then
#   status is null if the merged diffs are already present in the target file
         echo "STATUS:NOT_MERGED $file"
         echo "STATUS: NOT_MERGED $file$"  >>  /tmp/status
       else
#     3 way merge is successful
         echo STATUS: $STATUS
         echo "STATUS: $STATUS"  >>  /tmp/status
       fi
    done

    echo GIT merge failed for below listed files

    cat ~/temp/error.log

    echo "Git merge status per file is available in /tmp/status"
于 2016-09-07T06:48:01.473 回答
3

git cherry-pick FIRST^..LAST仅适用于简单场景。

为了实现一个体面的“将其合并到集成分支中”,同时让用户感到舒适,例如自动跳过已经集成的选择、移植菱形合并、交互式控制......)更好地使用变基。这里的一个答案指出了这一点,但是该协议包括一个冒险git branch -f和一个临时分支的杂耍。这里有一个直接稳健的方法:

git rebase -i FIRST LAST~0 --onto integration
git rebase @ integration

The -i allows for interactive control. The ~0 ensures a detached rebase (not moving the / another branch) in case LAST is a branch name. It can be omitted otherwise. The second rebase command just moves the integration branch ref in safe manner forward to the intermediate detached head - it doesn't introduce new commits. To rebase a complex structure with merge diamonds etc. consider --rebase-merges or --rebase-merges=rebase-cousins in the first rebase.

于 2020-11-08T18:34:35.383 回答
1

另一种选择可能是与我们的策略合并到范围之前的提交,然后与该范围的最后一个提交(或最后一个提交时的分支)进行“正常”合并。所以假设只有 2345 和 3456 个 master 提交合并到特性分支中:

掌握:
1234
2345
3456
4567

在功能分支中:

git 合并 -s 我们的 4567
git 合并 2345
于 2015-08-16T12:47:03.700 回答