如何在Git中将非最新的指定提交迁移到新分支并生成合并提交
操作开始前请先确认本地main分支工作区无未提交改动,可通过git log --oneline查看并记录以下关键提交的哈希值:
- 提交
a的哈希:<HASH_A>,即线性历史的分叉基点 - 提交
c的哈希:<HASH_C>,即需要迁移到新分支的最后一个提交 - 原main分支顶端提交
f的哈希:<ORIGIN_F>,即迁移前main分支最末尾的提交,用于后续找回原有的d/e/f三个提交
具体操作流程
第一步:基于提交
c创建新的常驻分支,承载原本线性历史上的b、c两个提交
执行命令:git branch feature-bc <HASH_C>
此时新分支自然从提交a分叉,包含完整的原封不动的b、c提交,和目标结构图里的侧枝完全一致。第二步:将本地main分支硬重置回分叉基点提交
a
执行命令:git reset --hard <HASH_A>
此时main分支仅保留从仓库起始到a的提交历史,原本在a之后的b/c/d/e/f暂时脱离main主线。第三步:在main分支当前位置(即提交
a),强制非快进合并刚才创建的feature-bc分支,生成要求的合并提交x
执行命令:git merge --no-ff feature-bc -m "合并b、c提交至主分支基线"
此处必须添加--no-ff参数:如果不加该参数,Git会因两个分支的提交存在线性包含关系,直接执行快进合并把main移到c的位置,不会生成独立的合并提交x,无法达到目标的非线性结构要求。
执行完成后生成的x提交会同时拥有两个父提交:a和c,完全匹配目标结构的定义。第四步:将原本属于main主线的
d/e/f三个提交,依次拣选到合并提交x的后面
执行命令:git cherry-pick <HASH_C>..<ORIGIN_F>
该Git范围语法会自动选中从提交c的下一个提交(也就是d)开始,到原main顶端f为止的所有提交,不需要额外记录单个提交的哈希。因为应用这几个提交时,基线已经从原来的c变成了新的合并提交x,所以生成的新提交哈希会和原来的d/e/f不同,也就是目标图里标注的d'/e'/f'。
正常情况下这里不会出现合并冲突,因为合并提交x的代码内容和原来的c完全一致,和拣选提交的基线代码完全匹配。如果出现意外冲突,解决冲突文件后执行git cherry-pick --continue即可完成流程。
结果校验
执行完所有步骤后,可通过git log --graph --oneline main feature-bc查看提交图,最终结构会和目标完全匹配:
* (main) f' * e' * d' * x |\ | * (feature-bc) c | * b |/ * a
操作提示:因为整个流程改写了main分支的提交历史,如果该分支已经推送到远端仓库,后续同步时需要使用
git push --force-with-lease origin main完成推送,避免远端历史校验冲突。
内容的提问来源于stack exchange,提问作者mdcq

