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

自闭症网瘾萝莉.ら 提交于 2019-12-11 12:33:50

问题


assume the following history:

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

I want to rewrite history to fixup X and Y into a single commit. So i want the history look like this:

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

All of my attempts so far failed. Most of the time there are conflicts during the rebase. Maybe rebase is not the right way to achieve this?

I understand that (without the knowledge that the effective situation just before the merge hasn't really changed) reapplying the merge from master to feature (M) lead to the same conflicts that i solved in the frist place. The command 'rerere' could be an option to solve this, but as far is a know this is only possible if 'rerere' had been activated also in the first place.

But in this case X - Y do have the same changeset as X'. Why is git not clever enough to just reapply M?. If i just squash X and Y in a single commit X', the original resolved changes (which are stored in M) should again be the correct content for M'. How can i tell git to just take the old content of M to build M' ?


回答1:


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

filter-branch docs

grafts are repo-local ancestry overrides. Anything in git that sees ancestry sees the grafted ancestry, in particular things that rewrite commits see it and so bake it into the rewritten commits.

The filters are shell script fragments, if you want to supply a new commit message you can e.g.

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



回答2:


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

Squash last two commits. And then use git cherry-pick to rewrite history with M and Z.

Hope that helps.




回答3:


Answering my question as i found a hack. I hope git can do it better/easier, so i am still asking for better solutions...

The hack is to start the merge again without commit. Then read all the changes from the original merge (M) into the index and finish the merge. The reset is necessary to clean conflicting files from the working dir. After that continue cherry-picking 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

Quite a hard work for a long history... Better solutions?




回答4:


Yet another solution (based on the ideas from 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

First checkout to detached Y (a unnamed branch ending at Y). The interactive rebase incorporates Y into X and rephrases the message of X. The result is a new commit N. HEAD currently points to it. Now Y (which is still part of branch feature) is replace by N (HEAD). Make permanent with filter-branch. Remove the replacement of Y to get ridd of N.



来源:https://stackoverflow.com/questions/31202672/how-to-squash-two-commits-which-are-actually-before-a-merge-commit

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!