在这些情况下,一个不错的技巧是将所有要移动的分支合并(连接)到最终提交节点中。之后,使用 rebase 和
--preserve-merges
移动生成的封闭子树(分支集)的选项。
创建一个包含所有分支的封闭子树,公开 2 个节点(开始和结束),用作 rebase 命令的输入参数。
封闭子树的末端是一个人工节点,在移动树后可能会被删除,以及可能为合并其他分支而创建的其他节点。
让我们看看下面的案例。
开发人员想要将一个新的提交(master)插入其他 3 个开发分支(b11、b2、b3)。其中之一 ( b11 ) 是特征分支b12的合并,两者均基于b1。其他 2 个分支(b2,b3)发散。
当然,开发人员可以在每个分支中挑选新的提交,但开发人员可能不希望在 3 个不同的分支中拥有相同的提交,而是在分支分歧之前只有 1 次提交。
* 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的临时分支
。
合并到包中可能会产生冲突,但这并不重要,因为这些合并稍后将被丢弃。只需指示 git 自动解决它们,添加 options -s recursive -Xours
。
$ git checkout -b pack b11 # create new branch at 'b11' to avoid losing original refs
$ git merge -s recursive -Xours b2 # merges b2 into pack
$ git merge -s recursive -Xours b3 # merges b3 into pack
这是将所有内容合并到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 。这个提交不是 rebase,它是 3 个 rebase 分支的起源。当然,pack 是整个子树的最终引用。
在 rebase 期间可能会有一些合并冲突。如果在进行合并之前已经存在冲突,这些将再次出现。一定要以同样的方式解决它们。对于为合并到临时节点包而创建的人工提交,请不要打扰并自动解决它们。
在 rebase 之后,这将是生成的树:
* 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 个分支共享。