git rebase命令的"--preserve-merges --onto"选项无法保留合并。

8
使用git v1.7.1,我正在尝试同时使用--preserve-merges--onto功能进行rebase。最终结果似乎没有合并提交,因此呈线性状态。我更愿意保留合并提交,出于与人们通常使用--preserve-merges相同的原因(更容易看到在其自己的分支中逻辑上分离的一组提交)。
我的主分支(rebase的目标)很无聊: A-B-C 我想要从中获取的特性分支有一个已合并到其中的子特性分支。像这样:
     X - Y
   /      \
V-W ------ Z

在这里,Z是要获取的特性分支的合并提交的主分支,而X和Y位于子功能分支上。

我使用命令: git rebase --preserve-merges --onto C V Z

最终我想得到的结果是:

         X - Y
        /     \
A-B-C-W ------ Z

但是实际上我得到的是:
A-B-C-W-X-Y

由于Z是一个无冲突的合并,代码的最终状态是正确的,但历史记录不如我所希望的那么表达。

有没有办法得到我想要的?

编辑以回答@Bombe:: 我编写了一个bash脚本来构建我的示例。在我的系统上(RHEL 6.2和git 1.7.1),这展示了我的问题。

#! /bin/bash
# start a new empty repo
git init
# make some commits on the master branch
git checkout master
touch A.txt; git add A.txt; git commit -m "add A.txt"; git tag Atag
touch B.txt; git add B.txt; git commit -m "add B.txt"; git tag Btag
touch C.txt; git add C.txt; git commit -m "add C.txt"; git tag Ctag
# now build the feature branch
# start at Btag (more or less arbitrary; point is it's before C)
git checkout Btag
git checkout -b feature
touch V.txt; git add V.txt; git commit -m "add V.txt"; git tag Vtag
touch W.txt; git add W.txt; git commit -m "add W.txt"; git tag Wtag
# now a subfeature
git checkout -b subfeature
touch X.txt; git add X.txt; git commit -m "add X.txt"; git tag Xtag
touch Y.txt; git add Y.txt; git commit -m "add Y.txt"; git tag Ytag
# merge the subfeature into the feature
# preserves branch history with --no-ff
git checkout feature
git merge --no-ff subfeature
# the merge commit is our Z
git tag Ztag
# one more commit so that merge isn't the tip (for better illustration of Z missing later)
touch postZ.txt; git add postZ.txt; git commit -m "add postZ.txt"; git tag postZtag
# now do the rebase
git rebase --preserve-merges --onto Ctag Vtag
# optionally move the master branch forward to the top of feature branch
git checkout master
git merge feature

在变基之前,我会得到以下内容:
        X-Y
       /   \
    V-W-----Z-postZ
   / 
A-B-C

在合并之后,我得到了:


        X-Y
       /   \
    V-W-----Z-postZ
   / 
A-B-C-W'-X'-Y'-postZ'

请注意,在Y'和postZ'之间缺少Z'。
3个回答

12
感谢Bombe和一位离线朋友指出它确实对某些人有效。受此启发,我现在能够回答自己的问题了。
简短回答:git版本1.7.5.2之前会表现出这种不良行为。
长回答:在git的源代码库中,2011年4月28日提交c192f9c865dbdae48c0400d717581d34cd315fb8明确修复了此问题。
引用提交消息(作者Andrew Wong):
git-rebase--interactive.sh: preserve-merges fails on merges created with no-ff

'git rebase' uses 'git merge' to preserve merges (-p).  This preserves
the original merge commit correctly, except when the original merge
commit was created by 'git merge --no-ff'.  In this case, 'git rebase'
will fail to preserve the merge, because during 'git rebase', 'git
merge' will simply fast-forward and skip the commit.  For example:

               B
              / \
             A---M
            /
    ---o---O---P---Q

If we try to rebase M onto P, we lose the merge commit and this happens:

                 A---B
                /
    ---o---O---P---Q

To correct this, we simply do a "no fast-forward" on all merge commits
when rebasing.  Since by the time we decided to do a 'git merge' inside
'git rebase', it means there was a merge originally, so 'git merge'
should always create a merge commit regardless of what the merge
branches look like. This way, when rebase M onto P from the above
example, we get:

                   B
                  / \
                 A---M
                /
    ---o---O---P---Q
解决方案:获取一个新版本的git,如果需要可以从源代码构建。
顺便说一句,我使用了git bisect来找出问题。很棒的工具。

1

我遇到了这个问题。请注意我的git版本,它是1.7.10.2。

我正在对一个提交范围(由其SHA1哈希标识)进行rebase,并且缺少最后的合并提交。

我的解决方案是将W到X重新基于C(不使用--preserve-merges),然后将Y、Z和postZ重新基于X'(使用--preserve-merges)。

希望这可以帮助你。


0

我刚刚尝试了重现你的情况,我可以报告--preserve-merges似乎按照广告宣传的那样工作。当你在提交Z时,只需输入:

git rebase --preserve-merges --onto C V

这就是我所做的,它保留了合并提交。


我编辑了原始帖子以提供重现我的问题的步骤。我相信你是正确的,但我不明白我做错了什么。 - RaveTheTadpole
2
我确实有一个大于1.7.5.2的版本。 :) - Bombe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接