如何压缩实际上在合并提交之前的两个提交?

How to squash two commits which are actually before a merge commit?

假设以下历史记录:

   X - Y - M - Z   <- feature
 /        /
A - B - C - D      <- master

我想重写历史以将 X 和 Y 修复为单个提交。所以我希望历史看起来像这样:

    X' -   M'- Z'  <- feature
 /        /
A - B - C - D      <- master

到目前为止我所有的尝试都失败了。大多数情况下,在 rebase 期间会发生冲突。也许变基不是实现这一目标的正确方法?

我知道(不知道合并之前的有效情况并没有真正改变)重新应用从主到功能(M)的合并会导致我在第一个地方解决的相同冲突。命令 'rerere' 可能是解决这个问题的一个选项,但据我们所知,这只有在 'rerere' 也首先被激活的情况下才有可能。

但在这种情况下,X - Y 确实与 X' 具有相同的变更集。为什么 git 不够聪明,只能重新应用 M?。如果我只是在单个提交 X' 中压缩 X 和 Y,则原始已解决的更改(存储在 M 中)应该再次成为 M' 的正确内容。我如何告诉 git 只使用 M 的旧内容来构建 M'?

git checkout feature
git checkout HEAD~2
git rebase -i HEAD~2

压制最后两次提交。然后使用 git cherry-pick 重写历史 MZ.

希望对您有所帮助。

回答我的问题,因为我发现了一个黑客。希望git可以做到better/easier,所以还在求更好的解决方案...

技巧是在不提交的情况下再次开始合并。然后将原始合并(M)中的所有更改读入索引并完成合并。重置对于清除工作目录中的冲突文件是必要的。之后继续采摘Z ...

git checkout -b featureRewritten A
git cherry-pick X
git cherry-pick -n Y
git commit --amend           #this will actually fixup Y into X
git merge -n C               #start the merge without commit
git read-tree M              #throw away the staged merge results by
                             #replacing them with the original solution
git commit                   #finish commit. need to change the automsg?
git reset --hard HEAD        #throw away any left overs
git cherry-pick Z            #continue

为悠久的历史而努力工作......更好的解决方案?

echo `git rev-parse Y A` >.git/info/grafts
git filter-branch -- --all
rm .git/info/grafts

filter-branch docs

grafts 是 repo-local 祖先覆盖。 git 中任何看到祖先的东西都会看到嫁接的祖先,特别是重写提交的东西会看到它,因此将其烘焙到重写的提交中。

过滤器是 shell 脚本片段,如果您想提供新的提交消息,您可以例如

git filter-branch --msg-filter="
        [ $commit = `git rev-parse Y` ] && cat <&3 || cat" -- --all 3<<EOD
your new commit message subject

your new commit message body here
...
EOD

另一个解决方案(基于 jthill 的想法):

git checkout Y
git rebase -i A
#i-rebase: reword X
#i-rebase: fixup Y
git replace Y HEAD
git filter-branch -- --all --ancestry-path Y^!
git replace -d Y

首先结帐到分离的 Y(一个以 Y 结尾的未命名分支)。交互式 rebase 将 Y 合并到 X 中并重新表述 X 的消息。结果是一个新的提交 N。HEAD 当前指向它。现在 Y(仍然是分支功能的一部分)被 N(HEAD)取代。使用过滤器分支永久化。删除替换 Y 以摆脱 N.