如何压缩实际上在合并提交之前的两个提交?
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
重写历史 M
和 Z
.
希望对您有所帮助。
回答我的问题,因为我发现了一个黑客。希望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
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.
假设以下历史记录:
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
重写历史 M
和 Z
.
希望对您有所帮助。
回答我的问题,因为我发现了一个黑客。希望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
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.