假设我有以下历史记录,其中第一行是主分支,较低的一个是在一个点与主服务器合并的功能分支,而D
只是恢复C
(这意味着B
和D
中的工作目录是相同的。
A---B---C---D master
\ \
E---F---G feature
我想在C
合并之前将D
及其版本F
添加到历史记录中,如下所示:
A---B---C---D master
\ \
E-----------F'--G' feature
我不想更改E
(这实际上是一系列提交)。
git rebase --onto D B
(按照建议here)会导致合并冲突(有或没有--preserve-merges
)。
有没有办法实现我想要的目标?
答案 0 :(得分:10)
大多数方法都会有些痛苦。使用git filter-branch
有一个适度无痛的版本,除了过滤器分支本身很痛苦。 :-)(你过滤提交F
和G
并编写一个提交过滤器,替换你想要F'
的新父项,并让filter-branch操作替换G
的父母身份。)
我认为不采用低级命令的最简单方法只是进行新的合并,然后将G
重新绑定到新的合并上。新的合并可能有冲突,但我们不在乎,我们只想采用旧的合并树,我们可以这样做:
$ git checkout <sha1> # Use D or E's sha-1 here.
# Note: whichever you use will
# be the first parent of our new
# merge; choose based on that.
$ git merge --no-commit <sha1> # use the remaining sha-1 here
[ignore resulting stuff]
$ git rm -rf . # Note: assumes you're in top dir of work tree
$ git checkout <sha1-of-F> -- .
$ git commit # Create merge commit F'
第一个checkout
使用一个SHA-1获取分离的HEAD,merge --no-commit
启动与其他SHA-1的合并过程,git rm -rf .
抛弃合并的树和任何冲突,git checkout <id> -- .
填写上一次合并的索引和工作树。最终git commit
使用与合并F'
相同的树创建合并F
,但使用不同的父级。
此时(仍然使用分离的HEAD)您可以重新设置(或挑选)提交G
(或许多提交G
),然后强制您的分支指向新图。我建议使用git rebase ... --onto HEAD
,但我没有使用分离的HEAD测试它,并且至少有一种方法可能出错(将HEAD
解析为ID太晚了。)
低级git commit-tree
命令实际上可能更简单。 Andrew C wrote the correct command in a comment,但您必须使用git update-ref
拼出分支名称。 [编辑:可能不太正确,您想要的两位家长是D
和E
,而不是D
和B
。再次,把你想要的那个作为第一父母的第一个。]
使用更熟悉的命令的优点(?)是,它们更熟悉。