git - 如何同时重写多个分支的历史记录?
问题描述
使用交互式 rebase ( git -i rebase ...
),可以在当前分支的沿袭中的任何位置编辑提交,从而“重写历史”。
但是,给定的提交可以属于多个分支的沿袭。
例如,假设我有一个具有此分支结构的仓库:
A --- B --- C --- D --- F --- G branch_1*
\
`-- H --- I --- J branch_2
\
`-- K branch_3
如果活动分支是branch_1
,并且我rebase -i
用来编辑 commit B
,则生成的 repo 将如下所示(至少在 GC 发生之前):
,-- b --- c --- d --- f --- g branch_1*
/
A --- B --- C --- D --- F --- G
\
`-- H --- I --- J branch_2
\
`-- K branch_3
请注意,原件B
继续在 和 的血统branch_2
中branch_1
。为这些分支中的每一个重复该过程,尽管这会很乏味,但会导致多次冗余提交,并且原始分支结构将丢失:
,-- b --- c --- d --- f --- g branch_1
/
A --- B --- C --- D --- F --- G
| \
| `-- H --- I --- J
| \
| `-- K
|\
| `-- b' -- c' -- h --- i --- j branch_2*
\
`-- b'' - c'' - h' -- i' -- k branch_3*
请注意b
,b'
, 和b''
本质上是等效的提交。c
, c'
, and c''
, h
and h'
, and i
and 也是一样的i'
。
有没有一种方便的方法来实现这样的事情:
A --- b --- c --- d --- f --- g branch_1*
\
`-- h --- i --- j branch_2
\
`-- k branch_3
...对提交的修改在哪里通过它所属的所有B
血统传播?
(我更喜欢将更改传播到所有后代存储的解决方案。)
解决方案
...有没有中途方便的方法来实现[一个明智的结果]
不。
有一个命令 ,git filter-branch
可以做到这一点,但它不是一半方便,甚至不是 1/4 方便。方便度大约是1000% 。:-)
新git rebase --rebase-merges
机器显然很适合以更方便的方式做这类事情,1但它目前并未设计为重新设置多个分支名称。
新的实验git filter-repo
有足够的能力做你想做的事,而且可能没有那么不方便git filter-branch
,但它仍然用核武器打击虫子——在这种情况下,可能是一个中等大小的虫子,不仅仅是一只苍蝇,但仍然是严重的过度杀伤。
(我曾经写过自己的实验性东西来做这种变基,但我从未完成它。它在我需要的时候做了我需要的事情。它使用相当于重复git rebase --onto
操作的方式工作,并且有很多极端情况。 )
1关键是能够标记特定的提交,以便在 rebase 复制它们之后,您可以将 <old, new> 哈希 ID 对配对,否则随着 rebase 的进行,图结构从一条链跳到另一条链. 旧--preserve-merges
代码无法做到这一点;新--rebase-merges
代码可以。一旦你有了这些部分,“同时”重新设置多个分支只是保存多个分支名称以在重新设置完成后强制调整的问题。然后你有 rebase 列表正确的提交和跳转点。rebase 操作的主要部分包括复制这些提交。最后,使用 old-to-new 映射,rebase 可以调整每个分支名称,然后重新连接HEAD
到您想要的。
剩下的用户界面级别问题在于选择正确的分支名称集以进行多重变基。