首页 文章

如何樱桃挑选一系列提交并合并到另一个分支?

提问于
浏览
525

我有以下存储库布局:

  • 主分公司( 生产环境 )

  • 整合

  • 工作

我想要实现的是从工作分支中挑选一系列提交并将其合并到集成分支中 . 我对git很陌生,我无法弄清楚如何正确地做到这一点(在一次操作中没有合并的樱桃选择提交范围)而不会弄乱存储库 . 关于这个的任何指针或想法?谢谢!

7 回答

  • 7

    当涉及到一系列提交时,采摘樱桃是不切实际的 .

    作为mentioned below Keith Kim,Git 1.7.2引入了挑选一系列提交的能力(但你还需要知道consequence of cherry-picking for future merge

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

    damian comments并警告我们:

    在“cherry-pick A..B”形式中,A应该比B更旧 . 如果它们的顺序错误,命令将无声地失败 .

    如果你想选择 range B through D (inclusive) ,那将是 B^..D .
    请参阅“Git create branch from range of previous commits?”作为说明 .

    Jubobs提到in the comments

    这假设B不是根提交;否则你会得到一个“未知版本”错误 .

    注意:从Git 2.9.x / 2.10(2016年第3季度)开始,您可以直接在孤立分支(空头)上挑选一系列提交:请参阅“How to make existing branch an orphan in git” .


    原始答案(2010年1月)

    rebase --onto 会更好,您可以在集成分支上重放给定的提交范围,如Charles Bailey described here .
    (另外,在git rebase man page中查找"Here is how you would transplant a topic branch based on one branch to another",查看 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 ”取消所有内容(并在 tmp 分支上放回 integration 分支)

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

    通过采摘樱桃或 rebase --onto ,不要忘记它会对后续合并产生影响,如described here .


    一个纯粹的“ cherry-pick ”解决方案是discussed here,并且会涉及到以下内容:

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

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

    但无论如何,当你需要"replay"一系列提交时,单词"replay"会推动你使用Git的“ rebase ”功能 .

  • 12

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

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

  • 25

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

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

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

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

  • 122

    假设你有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
    

    继续樱桃采摘过程 .

  • 0

    我将VonC's code包装成一个简短的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
    

    我现在将核心第三方代码,第三方模块和自定义分离到他们自己的git分支上,以便更好地理解未来的自定义 . git-cherry-pick 在这种情况下很有用,因为我在同一个存储库中有两棵树,但没有共享的祖先 .

  • 679

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

    下面是脚本 .

    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"
    
  • 2

    另一种选择可能是将我们的策略与范围之前的提交合并,然后与该范围的最后一次提交(或最后一次提交时的分支)进行“正常”合并 . 因此假设只有2345和3456提交的master被合并到feature分支中:

    master:
    1234
    2345
    3456
    4567
    

    在功能分支中:

    git merge -s ours 4567
    git merge 2345
    

相关问题