在这些情况下,一个好的技巧是将所有需要移动的分支合并(join)到一个最终提交节点中。之后,使用rebase命令,并添加
--preserve-merges
选项来移动所得到的封闭子树(一组分支)。
创建一个包含所有分支的封闭子树,会暴露出两个节点(起始和结束),这些节点被用作rebase命令的输入参数。
封闭子树的末端是一个人工节点,可以在移动树之后删除,其他可能为合并其他分支而创建的节点也可以删除。
让我们看看以下情况。
开发人员希望将一个新的提交(master)插入到其他3个开发分支(b11、b2、b3)中。其中一个分支(b11)是基于b1的一个特性分支b12的合并。另外两个分支(b2、b3)则是分叉的。
当然,开发人员可以将该新提交cherry-pick到每个分支中,但开发人员可能更喜欢在分支分叉之前只有1个提交而不是在3个不同的分支中都有相同的提交。
* baa687d (HEAD -> master) new common commit
| * b507c23 (b11) b11
| * 41849d9 Merge branch 'b12' into b11
| |\
| | * 20459a3 (b12) b12
| * | 1f74dd9 b11
| * | 554afac b11
| * | 67d80ab b11
| |/
| * b1cbb4e b11
| * 18c8802 (b1) b1
|/
| * 7b4e404 (b2) b2
| | * 6ec272b (b3) b3
| | * c363c43 b2 h
| |/
| * eabe01f header
|/
* 9b4a890 (mirror/master) initial
* 887d11b init
为此,第一步是创建一个包含3个分支的公共合并提交。为此使用临时分支pack。
合并到pack可能会产生冲突,但这并不重要,因为这些合并将在以后被丢弃。只需指示git自动解决它们,添加选项-s recursive -Xours
。
$ git checkout -b pack b11
$ git merge -s recursive -Xours b2
$ git merge -s recursive -Xours b3
这是将所有内容合并到
pack分支后的完整树形结构:
* b35a4a7 (HEAD -> pack) Merge branch 'b3' into pack
|\
| * 6ec272b (b3) b3
| * c363c43 b2 h
* | 60c9b7c Merge branch 'b2' into pack
|\ \
| * | 7b4e404 (b2) b2
| |/
| * eabe01f header
* | b507c23 (b11) b11
* | 41849d9 Merge branch 'b12' into b11
|\ \
| * | 20459a3 (b12) b12
* | | 1f74dd9 b11
* | | 554afac b11
* | | 67d80ab b11
|/ /
* | b1cbb4e b11
* | 18c8802 (b1) b1
|/
| * baa687d (master) new common commit
|/
* 9b4a890 initial
* 887d11b init
现在是时候移动已创建的子树了。为此,使用以下命令:
$ git rebase --preserve-merges --onto master master^ pack
参考文献
master^表示
master之前的提交(
master的父提交),在本例中为9b4a890。该提交未被重新基于,它是3个被基于的分支的起点。当然,pack是整个子树的最终参考。
在重新基于过程中可能会出现一些合并冲突。如果在合并之前已经存在冲突,则这些冲突将再次出现。请确保以相同的方式解决它们。对于为了合并到临时节点
pack而创建的人工提交,请不要费心,自动解决它们即可。
重新基于后,将得到以下结果树:
* 95c8d3d (HEAD -> pack) Merge branch 'b3' into pack
|\
| * d304281 b3
| * ed66668 b2 h
* | b8756ee Merge branch 'b2' into pack
|\ \
| * | 8d82257 b2
| |/
| * e133de9 header
* | f2176e2 b11
* | 321356e Merge branch 'b12' into b11
|\ \
| * | c919951 b12
* | | 8b3055f b11
* | | 743fac2 b11
* | | a14be49 b11
|/ /
* | 3fad600 b11
* | c7d72d6 b1
|/
* baa687d (master) new common commit
|
* 9b4a890 initial
* 887d11b init
有时旧的分支引用可能不会被重新定位(即使树已经重新定位而没有它们)。在这种情况下,您可以手动恢复或更改某些引用。
现在是撤销先前的变基合并以便变基整个树的时候了。通过一些删除、重置/检出操作,得到了以下树形结构:
* f2176e2 (HEAD -> b11) b11
* 321356e Merge branch 'b12' into b11
|\
| * c919951 (b12) b12
* | 8b3055f b11
* | 743fac2 b11
* | a14be49 b11
|/
* 3fad600 b11
* c7d72d6 (b1) b1
| * d304281 (b3) b3
| * ed66668 b2 h
| | * 8d82257 (b2) b2
| |/
| * e133de9 header
|/
* baa687d (mirror/master, mirror/HEAD, master) new common commit
* 9b4a890 initial
* 887d11b init
这正是开发人员想要实现的目标:提交内容被3个分支共享。
H
中的更改,并且合并之前和之后的提交之间的差异将包括master
中的所有更改。对于我正在进行的开发(大多数与master
中发生的事情分开),我认为变基更有意义。 - Jellby