我有以下存储库布局:

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

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

有帮助吗?

解决方案

当涉及一系列提交时,精挑细选 曾是 不实用。

作为 如以下所说的 经过 基思·金, ,Git 1.7.2+ 引入了挑选一系列提交的能力(但您仍然需要注意 为未来合并挑选的结果)

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

达米安 评论 并警告我们:

在里面 ”cherry-pick A..B“ 形式, A 应该比 B.
如果顺序错误,命令将默默失败.

如果您想选择 范围 B 通过 D (包括的) 那将是 B^..D.
看 ”Git 从以前的提交范围创建分支?”作为例证。

作为 朱博布斯 提及 在评论中:

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

笔记:从 Git 2.9.x/2.10(2016 年第 3 季度)开始,您可以直接在孤立分支(空头)上挑选一系列提交:看 ”如何使现有分支成为 git 中的孤儿分支".


原始答案(2010 年 1 月)

A rebase --onto 会更好,您可以在集成分支之上重播给定范围的提交,如下所示 查尔斯贝利在这里描述.
(另外,请在 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 --onto, integration 将返回到集成分支的最后一次提交(即“tmp“分支+所有重放的提交)

与樱桃采摘或 rebase --onto, ,不要忘记它会对后续合并产生影响,因为 此处描述.


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

如果你想使用补丁方法,那么“git format-patch|git am”和“gitcherry”是你的选择。
现在, git cherry-pick 仅接受单个提交,但如果您想选择范围 B 通过 D 那将是 B^..D 用 git 术语来说,所以

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

但无论如何,当您需要“重播”一系列提交时,“重播”一词应该促使您使用“rebase” Git 的功能。

其他提示

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

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

假设你有2个分支,

“分支A”:包括您想要复制的提交(从“commitA”到“commitB”

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

1)

 git checkout <branchA>

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

3)

git checkout <branchB>

4)

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

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

git cherry-pick --continue

继续挑选过程。

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

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

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

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

我包裹了 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 

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

上述所有选项都会提示您解决合并冲突。如果您正在合并为团队提交的更改,则很难解决开发人员的合并冲突并继续进行。但是,“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"

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

master:
1234
2345
3456
4567

在功能分支中:

git merge -s ours 4567
git merge 2345
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top