git 如何挑选一系列提交并合并到另一个分支?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1994463/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to cherry pick a range of commits and merge into another branch?
提问by crazybyte
I have the following repository layout:
我有以下存储库布局:
- master branch (production)
- integration
- working
- 主分支(生产)
- 一体化
- 在职的
What I want to achieve is to cherry pick a range of commits from the working branch and merge it into the integration branch. I pretty new to git and I can't figure out how to exactly do this (the cherry picking of commit ranges in one operation not the merging) without messing the repository up. Any pointers or thoughts on this? Thanks!
我想要实现的是从工作分支中挑选一系列提交并将其合并到集成分支中。我对 git 很陌生,我无法弄清楚如何在不弄乱存储库的情况下准确地做到这一点(在一个操作中挑选提交范围而不是合并)。对此有任何指示或想法吗?谢谢!
回答by VonC
When it comes to a range of commits, cherry-picking iswasnot practical.
当涉及到一系列提交时,挑选樱桃 是是不实际的。
As mentioned belowby Keith Kim, Git 1.7.2+ introduced the ability to cherry-pick a range of commits (but you still need to be aware of the consequence of cherry-picking for future merge)
正如下面提到的基思·金,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.
git cherry-pick" 学会了选择一系列提交
(例如 "cherry-pick A..B
" 和 "cherry-pick --stdin
")," " 也是如此git revert
;不过,这些不支持更好的排序控制 "rebase [-i]
"。
In the "
cherry-pick A..B
" form,A
should be older thanB
.
If they're the wrong order the command will silently fail.
在 "
cherry-pick A..B
" 形式中,A
应该比B
.
如果它们的顺序错误,该命令将无声无息地失败。
If you want to pick the range B
through D
(inclusive)that would be B^..D
.
See "Git create branch from range of previous commits?" as an illustration.
如果您想通过(包括)选择范围B
D
,那将是B^..D
.
请参阅“ Git 从先前提交的范围创建分支?”作为说明。
As Jubobsmentions in the comments:
This assumes that
B
is not a root commit; you'll get an "unknown revision
" error otherwise.
这假设
B
不是根提交;unknown revision
否则你会得到一个 " " 错误。
Note: as of Git 2.9.x/2.10 (Q3 2016), you can cherry-pick a range of commit directly on an orphan branch (empty head): see "How to make existing branch an orphan in git".
注意:从 Git 2.9.x/2.10(2016 年第三季度)开始,您可以直接在孤儿分支(空头)上挑选一系列提交:请参阅“如何在 git 中使现有分支成为孤儿”。
Original answer (January 2010)
原始答案(2010 年 1 月)
A rebase --onto
would be better, where you replay the given range of commit on top of your integration branch, as Charles Bailey described here.
(also, look for "Here is how you would transplant a topic branch based on one branch to another" in the git rebase man page, to see a practical example of git rebase --onto
)
Arebase --onto
会更好,您可以在集成分支之上重放给定的提交范围,如Charles Bailey 所述。
(另外,在git rebase 手册页中查找“这是您如何将基于一个分支的主题分支移植到另一个分支” ,以查看 的实际示例git rebase --onto
)
If your current branch is integration:
如果您当前的分支是集成:
# 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
That will replay everything between:
这将重播之间的所有内容:
- after the parent of
first_SHA-1_of_working_branch_range
(hence the~1
): the first commit you want to replay - up to "
integration
" (which points to the last commit you want to replay, from theworking
branch)
- 在
first_SHA-1_of_working_branch_range
(因此是~1
)的父级之后:您要重播的第一个提交 - 最多“
integration
”(指向您要重播的最后一次提交,来自working
分支)
to "tmp
" (which points to where integration
was pointing before)
到“ tmp
”(integration
指向之前指向的位置)
If there is any conflict when one of those commits is replayed:
如果在重播其中一个提交时发生任何冲突:
- either solve it and run "
git rebase --continue
". - or skip this patch, and instead run "
git rebase --skip
" - or cancel the all thing with a "
git rebase --abort
" (and put back theintegration
branch on thetmp
branch)
- 要么解决它并运行“
git rebase --continue
”。 - 或者跳过这个补丁,而是运行“
git rebase --skip
” - 或用“
git rebase --abort
”取消所有事情(并将integration
分支放回分支上tmp
)
After that rebase --onto
, integration
will be back at the last commit of the integration branch (that is "tmp
" branch + all the replayed commits)
之后rebase --onto
,integration
将返回到集成分支的最后一次提交(即“ tmp
”分支+所有重放的提交)
With cherry-picking or rebase --onto
, do not forget it has consequences on subsequent merges, as described here.
使用cherry-picking or rebase --onto
,不要忘记它会对后续合并产生影响,如here所述。
A pure "cherry-pick
" solution is discussed here, and would involve something like:
这里讨论了一个纯粹的“ cherry-pick
”解决方案,将涉及如下内容:
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
如果你想使用补丁方法,那么“git format-patch|git am”和“git cherry”是你的选择。
目前,git cherry-pick
只接受一次提交,但如果你想选择范围B
,D
那就是B^..D
git lingo,所以
git rev-list --reverse --topo-order B^..D | while read rev
do
git cherry-pick $rev || break
done
But anyway, when you need to "replay" a range of commits, the word "replay" should push you to use the "rebase
" feature of Git.
但是无论如何,当您需要“重放”一系列提交时,“重放”一词应该促使您使用rebase
Git的“ ”功能。
回答by Keith Kim
As of git v1.7.2 cherry pick can accept a range of commits:
从 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.
git cherry-pick
学会了选择一系列提交(例如cherry-pick A..B
和cherry-pick --stdin
),也是如此git revert
;但是,这些不支持更好的排序控制rebase [-i]
。
回答by KostasA
Assume that you have 2 branches,
假设你有 2 个分支,
"branchA" : includes commits you want to copy (from "commitA" to "commitB"
"branchA" : 包括你想要复制的提交(从 "commitA" 到 "commitB"
"branchB" : the branch you want the commits to be transferred from "branchA"
“branchB”:您希望提交从“branchA”转移的分支
1)
1)
git checkout <branchA>
2) get the IDs of "commitA" and "commitB"
2) 获取“commitA”和“commitB”的ID
3)
3)
git checkout <branchB>
4)
4)
git cherry-pick <commitA>^..<commitB>
5) In case you have a conflict, solve it and type
5)如果你有冲突,解决它并输入
git cherry-pick --continue
to continue the cherry-pick process.
继续挑选樱桃的过程。
回答by djs
Are you sure you don't want to actually merge the branches? If the working branch has some recent commits you don't want, you can just create a new branch with a HEAD at the point you want.
您确定不想实际合并分支吗?如果工作分支最近有一些你不想要的提交,你可以在你想要的点创建一个带有 HEAD 的新分支。
Now, if you really do want to cherry-pick a range of commits, for whatever reason, an elegant way to do this is to just pull of a patchset and apply it to your new integration branch:
现在,如果你真的想挑选一系列提交,无论出于何种原因,一个优雅的方法是只拉一个补丁集并将其应用到你的新集成分支:
git format-patch A..B
git checkout integration
git am *.patch
This is essentially what git-rebase is doing anyway, but without the need to play games. You can add --3way
to git-am
if you need to merge. Make sure there are no other *.patch files already in the directory where you do this, if you follow the instructions verbatim...
这基本上就是 git-rebase 正在做的事情,但不需要玩游戏。如果需要合并,可以添加--3way
到git-am
。如果您逐字按照说明进行操作,请确保您执行此操作的目录中没有其他 *.patch 文件...
回答by Adam Franco
I wrapped VonC's codeinto a short bash script, git-multi-cherry-pick
, for easy running:
我将VonC 的代码封装到一个简短的 bash 脚本中git-multi-cherry-pick
,以便于运行:
#!/bin/bash
if [ -z ]; then
echo "Equivalent to running git-cherry-pick on each of the commits in the range specified.";
echo "";
echo "Usage: 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=
END=
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"
start^..end";
echo "";
exit 1;
fi
git rev-list --reverse --topo-order | while read rev
do
git cherry-pick $rev || break
done
I'm currently using this as I rebuild the history of a project that had both 3rd-party code and customizations mixed together in the same svn trunk. I'm now splitting apart core 3rd party code, 3rd party modules, and customizations onto their own git branches for better understanding of customizations going forward. git-cherry-pick
is helpful in this situation since I have two trees in the same repository, but without a shared ancestor.
我目前正在使用它,因为我重建了一个项目的历史,该项目在同一个 svn 主干中混合了 3rd 方代码和自定义。我现在将核心 3rd 方代码、3rd 方模块和自定义拆分到他们自己的 git 分支上,以便更好地理解未来的自定义。git-cherry-pick
在这种情况下很有帮助,因为我在同一个存储库中有两棵树,但没有共享祖先。
回答by Yoganand Bijapur
All the above options will prompt you to resolve merge conflicts. If you are merging changes committed for a team, it is difficult to get resolved the merge conflicts from developers and proceed. However, "git merge" will do the merge in one shot but you can not pass a range of revisions as argument. we have to use "git diff" and "git apply" commands to do the merge range of revs. I have observed that "git apply" will fail if the patch file has diff for too many file, so we have to create a patch per file and then apply. Note that the script will not be able to delete the files that are deleted in source branch. This is a rare case, you can manually delete such files from target branch. The exit status of "git apply" is not zero if it is not able to apply the patch, however if you use -3way option it will fall back to 3 way merge and you don't have to worry about this failure.
以上所有选项都会提示您解决合并冲突。如果您正在合并为团队提交的更改,则很难从开发人员那里解决合并冲突并继续进行。但是,“git merge”将一次性完成合并,但您不能将一系列修订作为参数传递。我们必须使用“git diff”和“git apply”命令来完成转速的合并范围。我观察到如果补丁文件对太多文件有差异,“git apply”将失败,所以我们必须为每个文件创建一个补丁然后应用。请注意,脚本将无法删除在源分支中删除的文件。这是一种罕见的情况,您可以从目标分支手动删除此类文件。如果无法应用补丁,“git apply”的退出状态不为零,
Below is the script.
下面是脚本。
master: 1234 2345 3456 4567
回答by phili_b
I have tested that some days ago, after reading the very clear explanation of Vonc.
几天前,在阅读了 Vonc 的非常清晰的解释后,我已经测试过了。
My steps
我的脚步
Start
开始
- Branch
dev
: A B C D E F G H I J - Branch
target
: A B C D - I don't want
E
norH
- 分支机构
dev
: ABCDEFGHIJ - 分公司
target
:ABCD - 我不想要
E
也不H
Steps to copy features without the step E and H in the branch dev_feature_wo_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
where I putdrop
front ofE
andH
in the rebase editor- resolve conflicts, continue and
commit
git checkout dev
git checkout -b dev_feature_wo_E_H
git rebase --interactive --rebase-merges --no-ff D
我把rebase 编辑器放在drop
前面E
和里面H
的地方- 解决冲突,继续并
commit
Steps to copy the the branch dev_feature_wo_E_H
on target.
dev_feature_wo_E_H
在目标上复制分支的步骤。
git checkout target
git merge --no-ff --no-commit dev_feature_wo_E_H
- resolve conflicts, continue and
commit
git checkout target
git merge --no-ff --no-commit dev_feature_wo_E_H
- 解决冲突,继续并
commit
Some remarks
一些备注
- I've done that because of too much
cherry-pick
in the days before git cherry-pick
is powerful and simple but- it creates duplicates commits
- and when I want to
merge
I have to resolve conflicts of the initial commits and duplicates commits, so for one or twocherry-pick
, it's OK to "cherry-picking" but for more it's too verbose and the branch will become too complex
- In my mind the steps I've done are more clear than
git rebase --onto
- 我这样做是因为
cherry-pick
前几天太多了 git cherry-pick
强大而简单,但是- 它创建重复提交
- 当我想
merge
我必须解决初始提交和重复提交的冲突时,所以对于一两个cherry-pick
,“挑选樱桃”是可以的,但更多的是它太冗长,分支会变得太复杂
- 在我看来,我所做的步骤比
git rebase --onto
回答by Koos Vriezen
Another option might be to merge with strategy ours to the commit before the range and then a 'normal' merge with the last commit of that range (or branch when it is the last one). So suppose only 2345 and 3456 commits of master to be merged into feature branch:
另一种选择可能是将我们的策略合并到范围之前的提交,然后与该范围的最后一次提交(或最后一次提交时的分支)进行“正常”合并。所以假设只有 2345 和 3456 个 master 提交被合并到特性分支中:
git merge -s ours 4567 git merge 2345
in feature branch:
在功能分支中:
##代码##