git rebase 交互式:将提交合并到一起

Posted

技术标签:

【中文标题】git rebase 交互式:将提交合并到一起【英文标题】:git rebase interactive: squash merge commits together 【发布时间】:2010-12-16 02:11:59 【问题描述】:

我想要一个简单的解决方案,在交互式 rebase 期间将两个合并提交压缩在一起。

我的存储库如下所示:

   X --- Y --------- M1 -------- M2 (my-feature)
  /                 /           /
 /                 /           /
a --- b --- c --- d --- e --- f (stable)

也就是说,我有一个 my-feature 分支,最近合并了两次,中间没有真正的提交。我不只是想重新设置 my-feature 分支,因为它是它自己的已发布分支,我只想将最后两个合并提交合并为一个(尚未发布这些提交)

   X --- Y ---- M (my-feature)
  /            /
 /            /
a --- ... -- f (stable)

我试过了:

git rebase -p -i M1^

但我得到了:

Refusing to squash a merge: M2

我最后做的是:

git checkout my-feature
git reset --soft HEAD^  # remove the last commit (M2) but keep the changes in the index
git commit -m toto      # redo the commit M2, this time it is not a merge commit
git rebase -p -i M1^    # do the rebase and squash the last commit
git diff M2 HEAD        # test the commits are the same

现在,新的合并提交不再被视为合并提交(它只保留了第一个父级)。所以:

git reset --soft HEAD^               # get ready to modify the commit
git stash                            # put away the index
git merge -s ours --no-commit stable # regenerate merge information (the second parent)
git stash apply                      # get the index back with the real merge in it
git commit -a                        # commit your merge
git diff M2 HEAD                     # test that you have the same commit again

但是如果我有很多提交,这可能会变得复杂,你有更好的解决方案吗? 谢谢。

米尔德里德

【问题讨论】:

好吧,当你进行第二次合并时,你总是可以使用--squash 来避免创建提交,然后使用git commit --amend 修改之前的合并。 这不起作用,它不会保存您在提交中合并的分支的新版本 【参考方案1】:

这是一个老话题,但我只是在寻找类似信息时遇到了它。

类似于Subtree octopus merge 中描述的技巧是解决此类问题的一个非常好的方法:

git checkout my-feature
git reset --soft Y
git rev-parse f > .git/MERGE_HEAD
git commit

这将采用存在于 my-feature 顶端的索引,并使用它从 Y 创建一个新的提交,并将“f”作为第二个父级。结果与您从未执行过 M1 而是直接执行 M2 一样。

【讨论】:

请注意,这种合并与子树或章鱼合并无关。您链接的博客只是使用该技术将子树合并和章鱼合并到一个合并提交中组合(因为 git 不能直接一次性完成这两个合并)。 这样做的缺点是 git 将无法生成正确的提交消息。我从旧的合并提交中复制了消息。否则是一个不错的简单解决方案。 这么多赞!我们只是做了一个非常困难的合并,我的同事的方法是将他的树中的每个提交合并到我的,一次一个。然后是挤压,我们找到了这个解决方案。很遗憾你需要编写自己的提交信息。 如果您启动失败的合并,您可以获得合并消息的内容,然后使用它 我更喜欢使用git update-ref MERGE_HEAD f 而不是git rev-parse f > .git/MERGE_HEAD,因为它不会直接修改内部 git 文件。我可以编辑那部分吗?【参考方案2】:

如果您尚未发布最后两个合并提交,您可以进行重置和简单合并。

git reset --hard Y
git merge stable

【讨论】:

是的,但是合并很困难,我宁愿合并尽可能少的更改。我不想解决我已经解决的冲突。 如果你不想重新解决冲突,你需要“使用” git-rerere(并且“使用”我的意思是“打开它”,因为 git 处理重新启用后自动修复相同的冲突)。【参考方案3】:

我来到这个主题是想压缩一个合并提交;所以我的回答对原来的问题没有那么有用。

               X
                \   
                 \  
a --- b --- c --- M1 (subtree merge)

我想要的是重新设置 M1 合并并将所有内容压缩为 b 之上的单个提交。

a --- b --- S (include the changes from c, X and M1)

我尝试了各种不同的组合,但这是有效的:

git checkout -b rebase b (checkout a working branch at point b)
git merge --squash M1

这会将更改应用到可以提交的索引中 git提交

【讨论】:

在这种情况下,您可以使用git diff b > diff.patch,然后是git checkout bcat diff.patch | patch -p1,然后是git commit。如果 merge 包含分辨率,则此方法有效。原来的问题不同;但我想你来这里是为了找和我一样的东西。您可以提前通过git log 获取入住信息。 如果你想在你的主人身上遇到这种情况,还需要额外的步骤:git checkout master && git reset --hard b && git rebase rebase。这只是为了自我安慰。您可以为分支选择另一个名称,而不是“rebase”:)【参考方案4】:

使用原始合并提交的树对象将确保内容保持不变。 commit-tree 可用于使用所需的父级和相同的内容进行新的提交。但是,要使 fmt-merge-msg 生成正常的合并消息,您需要先软重置回 Y。以下是使用通用配方打包的所有内容:

parent2=$(git rev-parse f)
parent1=Y
merge_branch=stable
tree=$(git rev-parse HEAD^tree)
git reset --soft $parent1
commit=$(echo $parent2$'\t\t'"branch $merge_branch" | git fmt-merge-msg | git commit-tree -p $parent1 -p $parent2 -F - $tree)
git reset --hard $commit

这是一个别名,可以放在你的 ~/.gitconfig 中:

[alias]
    remerge = "!f()  p1=$1; p2=`git rev-parse $2`; t=`git rev-parse HEAD^tree`; git reset --soft $p1; git reset --hard `echo $p2$'\t\t'"branch $3:-$2" | git fmt-merge-msg | git commit-tree -p $p1 -p $p2 -F - $t`; ; f"

启用:

git remerge <parent1-rev> <parent2-rev> [<parent2-branch>]

【讨论】:

不错,不知道git fmt-merge-msg,TIL。但是,为什么不复制现有的提交消息,或者让 replace 和 filter-branch 为你做呢? git replace --graft my-feature,~2,^2; git filter-branch -- myfeature@^!? 我假设一开始就使用了未修改的合并提交消息,并且标准的合并提交消息可以用于此替换合并提交 - 就好像新的简单合并已按照 @bobDevil 的建议完成。 git filter-branch 对我来说是新的,看起来是解决这个问题的另一个很好的解决方案。【参考方案5】:

在最近的 git 版本中,上述方法均不适用于我。在我的情况下,以下方法成功了:

git reset --soft Y
git reset --hard $(git commit-tree $(git write-tree) -p HEAD -p stable < commit_msg)

不过,您必须先将提交消息写入文件 commit_msg。

【讨论】:

你可以用 $($EDITOR commit_msg) 代替 commit_msg,或者写成 【参考方案6】:

在我看来,最好的方法是将自己置于合并提交之上并撤消顶部提交并修改对先前合并提交的更改。

您将使用以下命令撤消 git-commit:

    git reset HEAD~1

    git reset HEAD^

然后使用:

    git add . && git commit --amend

然后检查结果:

    git log

您应该看到第一个合并提交现在包含了两个更改。

此方法可用于所有类型提交的任何此类需求,或何时

    git rebase -i HEAD~10

...无法使用。

【讨论】:

以上是关于git rebase 交互式:将提交合并到一起的主要内容,如果未能解决你的问题,请参考以下文章

git rebase简介(高级篇)

Git rebase变基如何使用

【Git】rebase 用法小结

Git 交互式 rebase 没有提交选择

自定义 Git *Rebase* 提交消息模板

git rebase -i 合并多次提交