How to cherry pick a range of commits and merge into another branch?
我有以下存储库布局:
- 主分公司(生产)
- 积分
- 工作的
我想要实现的是从工作分支中挑选一系列提交并将其合并到集成分支中。 我是git的新手,我无法弄清楚如何正确地做到这一点(在一次操作中不提取合并的樱桃选择提交范围)而不会弄乱存储库。 关于这个的任何指针或想法? 谢谢!
当涉及到一系列提交时,采摘 del>是不切实际的。
正如Keith Kim在下面提到的,Git 1.7.2+引入了挑选一系列提交的能力(但你仍然需要知道未来合并的挑选的结果)
git cherry-pick" learned to pick a range of commits
(e.g."cherry-pick A..B " and"cherry-pick --stdin "), so did"git revert "; these do not support the nicer sequencing control"rebase [-i] " has, though.
达米安评论并警告我们:
In the"
cherry-pick A..B " form,A should be older thanB .
If they're the wrong order the command will silently fail.
如果要选择
请参阅"Git从先前提交的范围创建分支?"作为一个例子。
正如Jubobs在评论中提到的那样:
This assumes that
B is not a root commit; you'll get an"unknown revision " error otherwise.
注意:从Git 2.9.x / 2.10(2016年第3季度)开始,您可以直接在孤儿分支(空头)上挑选一系列提交:请参阅"如何在git中使现有分支成为孤儿"。
原始答案(2010年1月)
一个
(另外,在git rebase手册页中查找"以下是如何将基于一个分支的主题分支移植到另一个分支",以查看
如果您当前的分支是集成:
1 2 3 4 5 6 7 8 | # 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 分支重放的最后一次提交)
到"
如果重播其中一个提交时有任何冲突:
-
要么解决它并运行"
git rebase --continue "。 -
或者跳过此补丁,改为运行"
git rebase --skip " -
或用"
git rebase --abort "取消所有内容(并在tmp 分支上放回integration 分支)
在
使用cherry-picking或
这里讨论一个纯粹的"
If you want to use a patch approach then"git format-patch|git am" and"git cherry" are your options.
Currently,git cherry-pick accepts only a single commit, but if you want to pick the rangeB throughD that would beB^..D in git lingo, so
1 2 3 4 | git rev-list --reverse --topo-order B^..D | while read rev do git cherry-pick $rev || break done |
但无论如何,当你需要"重放"一系列提交时,"重放"一词应该会促使你使用Git的"
从git v1.7.2开始,cherry pick可以接受一系列提交:
git cherry-pick learned to pick a range of commits (e.g.cherry-pick A..B andcherry-pick --stdin ), so didgit revert ; these do not support the nicer sequencing controlrebase [-i] has, though.
你确定你不想真正合并分支吗?如果工作分支有一些你不想要的最近提交,你可以在你想要的位置创建一个带有HEAD的新分支。
现在,如果你真的想要挑选一系列提交,无论出于何种原因,一个优雅的方法是只需要一个补丁集并将其应用到你的新集成分支:
1 2 3 | git format-patch A..B git checkout integration git am *.patch |
这基本上就是git-rebase正在做的事情,但不需要玩游戏。如果需要合并,可以将
假设你有2个分支,
"branchA":包括要复制的提交(从"commitA"到"commitB")
"branchB":您希望从"branchA"传输提交的分支
1)
1 | git checkout <branchA> |
2)获取"commitA"和"commitB"的ID
3)
1 | git checkout <branchB> |
4)
1 | git cherry-pick <commitA>^..<commitB> |
5)如果您有冲突,请解决并输入
1 | git cherry-pick --continue |
继续樱桃采摘过程。
我将VonC的代码包装成一个简短的bash脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #!/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主干中混合在一起。我现在将核心第三方代码,第三方模块和自定义分离到他们自己的git分支上,以便更好地理解未来的自定义。
以上所有选项都将提示您解决合并冲突。如果要合并为团队提交的更改,则很难解决开发人员的合并冲突并继续进行。但是,"git merge"将一次性完成合并,但您无法通过一系列修订作为参数。我们必须使用"git diff"和"git apply"命令来进行转速的合并范围。我观察到如果补丁文件包含太多文件的差异,"git apply"将失败,因此我们必须为每个文件创建一个补丁然后应用。请注意,该脚本将无法删除源分支中删除的文件。这是一种罕见的情况,您可以从目标分支手动删除此类文件。如果无法应用补丁,"git apply"的退出状态不为零,但是如果使用-3way选项,它将回退到3路合并,您不必担心此失败。
下面是脚本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | 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被合并到feature分支中:
1 2 3 4 5 | master: 1234 2345 3456 4567 |
在功能分支中:
1 2 | git merge -s ours 4567 git merge 2345 |