重新合并以更改合并提交的父级 [英] Rebasing to change parent of a merge commit
问题描述
D
只是恢复 C
(这意味着工作目录在 B
和 D中相同
)。 A --- B --- C --- D主人
\\
E --- F --- G功能
我想添加 C 及其在
F
中的合并之前的历史 D
到历史记录中,如下所示:
A --- B --- C --- D主人
\\
E ----------- F' - G'功能
I don'我想要改变 E
(这实际上是一系列长时间的提交)。
git rebase --onto DB
(如建议此处)导致合并冲突(有或没有 - preserve-merges
)。
有没有办法实现我想要的?
大多数方法都会有点痛苦。使用 git filter-branch
有一个中等无痛苦的版本,除了filter-branch本身很痛苦。 :-)(你可以过滤提交 F
和 G
并编写一个提交过滤器,用于替换新父项你需要 F'
,并让filter-branch操作替换 G
。)
我认为不使用低级命令的最简单的方法就是进行新的合并,然后将 G
转换为新合并。新合并可能会有冲突,但我们并不在意,我们只想采用旧合并的树,我们可以这样做:
$ git checkout< sha1> #在这里使用D或E的sha-1。
#请注意:无论您使用何种方式,
#都是我们新的
#merge的第一个父级;基于此选择。
$ git merge --no-commit< sha1> #在这里使用剩余的sha-1
[忽略结果内容]
$ git rm -rf。 #注意:假设你在工作树顶部目录
$ git checkout< sha1-of-F> - 。
$ git commit#创建合并提交F'
第一个 checkout
让你在一个SHA-1的分离HEAD上, merge --no-commit
启动与另一个SHA-1的合并过程, git rm -rf。
会抛出合并树和任何冲突,并且 git checkout< id> - 。
填充上一次合并的索引和工作树。最后一个 git commit
创建合并 F'
与merge F
,但与父母不同。
此时(仍然使用分离的HEAD),您可以重新绑定(或选择)提交 G
(或许多提交 G
),然后强制您的分支指向新图形的顶端。我建议使用 git rebase ... --onto HEAD
但我没有用分离的HEAD测试过,至少有一种方法可能会出错(解决 HEAD
给一个ID太晚了)。
低级 git commit-tree
命令实际上可能更简单。 Andrew C在评论中写了正确的命令,尽管你必须用
git update-ref
来说明分支名称。
使用更熟悉的命令的好处在于它们更熟悉。
Suppose I've got the following history, where the top line is the master branch, the lower one is a feature branch that's merged with master at one point, and D
just reverts C
(which means that the working directory is the same in B
and D
).
A---B---C---D master
\ \
E---F---G feature
I want to add C
and its reversion D
to the history before the merge in F
, like this:
A---B---C---D master
\ \
E-----------F'--G' feature
I don't want to change E
(which is actually a long series of commits).
git rebase --onto D B
(as suggested here) results in merge conflicts (with or without --preserve-merges
).
Is there a way to accomplish what I want?
Most methods will be somewhat painful. There's a moderately painless version using git filter-branch
, except that filter-branch itself is painful. :-) (You'd filter commits F
and G
and write a commit-filter that substitutes in the new parents you want for F'
, and let the filter-branch operation replace the parentage for G
.)
I think the simplest method that does not resort to low level commands is just to make a new merge, then rebase G
onto the new merge. The new merge may have conflicts but we don't care, we just want to take the old merge's tree, which we can do like this:
$ 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'
The first checkout
gets you on a detached HEAD with one SHA-1, the merge --no-commit
starts the merge process with the other SHA-1, the git rm -rf .
throws away the merged tree and any conflicts, and the git checkout <id> -- .
fills in the index and work-tree from the previous merge. The final git commit
creates merge F'
with the same tree as merge F
, but with different parents.
At this point (still with a detached HEAD) you can rebase (or cherry-pick) commit G
(or many commits G
), then force your branch to point to the tip of the new graph. I'd suggest using git rebase ... --onto HEAD
but I have not tested this with a detached HEAD and there's at least one way it might go wrong (resolving HEAD
to an ID too late).
The low level git commit-tree
command may actually be even simpler. Andrew C wrote the correct command in a comment, although you have to spell out the branch name with git update-ref
. [Edit: maybe not quite correct, the two parents you want are D
and E
, not D
and B
. Again, put the one you want as first-parent first.]
The advantage (?) of using the more familiar commands is that they're, well, more familiar.
这篇关于重新合并以更改合并提交的父级的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!