我有以下存储库布局:
主分支(生产)集成工作
我想实现的是从工作分支中挑选一系列提交,并将其合并到集成分支中。我对git很陌生,我无法弄清楚如何在不破坏存储库的情况下准确地做到这一点(在一次操作中挑选提交范围,而不是合并)。对此有什么建议或想法吗?谢谢
我有以下存储库布局:
主分支(生产)集成工作
我想实现的是从工作分支中挑选一系列提交,并将其合并到集成分支中。我对git很陌生,我无法弄清楚如何在不破坏存储库的情况下准确地做到这一点(在一次操作中挑选提交范围,而不是合并)。对此有什么建议或想法吗?谢谢
当涉及到一系列的提交时,挑选樱桃是不实际的。
正如Keith Kim在下面提到的,Git 1.7.2+引入了樱桃选择一系列提交的能力(但您仍需要意识到樱桃选择对未来合并的影响)
gitcherry-pick“学会了选择一系列提交(例如“cherry-pick A..B”和“cherry pick-stdin”),“git-restore”也是如此;然而,这些并不支持“rebase[-i]”所具有的更好的测序控制。
达米安评论并警告我们:
在“cherry-pick A..B”形式中,A应该早于B。如果顺序错误,命令将自动失败。
如果要选择范围B到D(包括B),则该范围为B^。。D(而不是B..D)。请参阅“Git从以前的提交范围创建分支?”作为示例。
正如Jubobs在评论中提到的:
这假设B不是根提交;否则会出现“未知修订”错误。
注意:从Git 2.9.x/2.10(2016年第3季度)开始,您可以直接在孤立分支(空头)上选择一系列提交:请参阅“如何在Git中使现有分支成为孤立分支”。
原答覆(2010年1月)
如Charles Bailey在这里所描述的那样,在集成分支的顶部回放给定范围的提交会更好。(此外,请在gitrebase手册页中查找“这是如何将基于一个分支的主题分支移植到另一个分支”,以查看gitrebase的一个实际示例——到)
如果您当前的分支机构是集成的:
# 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_SHAR_of_working_branch_range的父级之后(因此为~1):要重播的第一次提交直到“集成”(它指向您要从工作分支重播的最后一次提交)
到“tmp”(指向集成之前指向的位置)
如果在重播其中一个提交时发生冲突:
要么解决它,然后运行“git-rebase--continue”。或跳过此修补程序,改为运行“git-rebase--skip”或使用“gitrebase--abort”取消所有操作(并将集成分支放回tmp分支)
在重新基址之后,集成将返回到集成分支的最后一次提交(即“tmp”分支+所有重放的提交)
对于cherry-pick或rebase-ton,不要忘记它会对后续合并产生影响,如本文所述。
这里讨论的是一个纯粹的“樱桃采摘”解决方案,它将涉及以下内容:
如果您想使用补丁方法,那么可以选择“git format patch | git am”和“git cherry”。目前,gitcherry-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
但无论如何,当您需要“重放”一系列提交时,“重放”这个词应该会促使您使用Git的“rebase”特性。
是否确实不想合并分支?如果工作分支有一些您不需要的最近提交,您可以在您需要的位置创建一个带有HEAD的新分支。
现在,如果出于任何原因,你真的想要挑选一系列提交,那么一个很好的方法就是只需提取一个补丁集并将其应用于新的集成分支:
git format-patch A..B
git checkout integration
git am *.patch
这基本上就是git rebase所做的,但不需要玩游戏。如果需要合并,可以向git-am添加--3way。如果您逐字遵循说明,请确保执行此操作的目录中没有其他*.patch文件。。。
我将VonC的代码打包成一个简短的bash脚本gitmulticherry-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主干中混合了第三方代码和定制。我现在正在将核心第三方代码、第三方模块和定制拆分到各自的git分支上,以便更好地理解未来的定制。gitcherry-pick在这种情况下很有用,因为我在同一个存储库中有两棵树,但没有共享的祖先。
从git v1.7.2起,cherry-pick可以接受一系列提交:
git cherry-pick学会了选择一系列提交(例如cherry-pick a..B和cherry--pick-stdin),git revert也学会了选择;然而,这些并不支持rebase[-i]所具有的更好的测序控制。
正如Gabe Moothart所指出的,cherry pick A..B不会得到commit A(你需要A~1..B),如果有任何冲突,git不会像rebase那样自动继续(至少在1.7.3.1)。
另一种选择可能是将我们的策略合并到范围之前的提交,然后与该范围的最后一次提交(或当它是最后一次时的分支)进行“正常”合并。因此,假设只有2345和3456个主提交要合并到功能分支中:
master: 1234 2345 3456 4567
在功能分支中:
git merge -s ours 4567 git merge 2345
以上所有选项都将提示您解决合并冲突。如果您正在合并为团队提交的更改,那么很难从开发人员那里解决合并冲突并继续。然而,“gitmerge”将一次性完成合并,但不能将一系列修改作为参数传递。我们必须使用“gitdiff”和“gitapply”命令来执行revs的合并范围。我观察到,如果补丁文件对太多文件有差异,“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个分支,
“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
继续樱桃采摘过程。
几天前,在阅读了Vonc的非常清晰的解释后,我已经测试了这一点。
我的脚步
开始
分支机构名称:A B C D E F G H I J分支目标:A B C D我不想要E和H
复制分支dev_feature_wo_E_H中没有步骤E和H的要素的步骤
git结帐设备git checkout-b dev_feature_wo_E_Hgit rebase--交互式--rebase合并--没有ff D,我在rebase编辑器中将E和H放在前面解决冲突,继续并提交
在目标上复制分支dev_feature_wo_E_H的步骤。
git结帐目标git merge--无ff--无提交dev_feature_wo_E_H解决冲突,继续并提交
一些备注
我这么做是因为前几天摘了太多樱桃git cherry pick功能强大且简单,但它会创建重复的提交当我想合并时,我必须解决初始提交和重复提交之间的冲突,所以对于一个或两个樱桃选择,“樱桃选择”是可以的,但对于更多的,它太冗长,分支将变得太复杂
git cherry pick start_commit_sha_id ^。。结束委托sha_id
例如git cherry pick 3a7322ac。。7d7c123c型
假设您在branchA上,希望从branchB选择提交(给定范围的开始和结束提交SHA,左侧提交SHA较旧)。整个提交范围(包括两个)将在branchA中进行精心挑选。
官方文件中给出的示例非常有用。
先摘樱桃。。LAST仅适用于简单场景。
为了实现一个体面的“将其合并到集成分支中”,同时对自动跳过已集成的拾取、移植钻石合并、交互控制等事情感到舒适……)最好使用重基。这里的一个答案指出了这一点,然而协议包括一个危险的git分支-f和一个临时分支。这里有一个简单的稳健方法:
git rebase -i FIRST LAST~0 --onto integration
git rebase @ integration
-i允许交互控制。如果LAST是分支名称,~0可确保分离的重基(不移动/另一个分支)。否则可以省略。第二个rebase命令只是以安全的方式将集成分支ref向前移动到中间分离的头部-它不会引入新的提交。要使用合并菱形等对复杂结构进行重基化,请考虑第一个重基中的--rebase merges或--rebase convers=rebase cousins。
如果你只有两次提交,并且想挑选,你可以
git cherry-pick <commit> -n
然后将它们转换为新的提交。
-n不会自动创建提交,而只是阶段性地进行更改,因此您可以继续对提交中的文件进行cherry pick或更改。
合并有时会很困难,只需创建一个补丁并手动应用更改即可。
创建从start_commit到end_commit的修补程序文件。git diff<start_commit><end_commit>>patch.diff签出分支并手动应用修补程序中的更改。