I have a topic branch that looks like this. (the branch topic currently points at 'a0a0')
- a0a0 Merge branch 'Master' into topic
- b1b1 Merge Branch 'Master' into topic
- c2c2 Merge commit 'something from master' into topic
- d3d3 Merge Branch 'Master' into topic
- e4e4 Merge Branch 'Master' into topic
- f5f5 Merge Branch 'Master' into topic
- 6666 an actual commit
- [lots of history on the topic branch]
- 9999 original divergence point
How do I turn a0-f5 into a single merge commit?
I've tried: git rebase -i f5f5
with the intention of telling it to squash
those 5 commits, but instead it just gave every commit on the master branch between f5f5 and a0a0. So I turned all but the first and last from pick
into squash
, but that went crazy and rebased every single commit from master into a giant commit in place of all the merges, losing the fact that it was a 'merge'. That isn't what I expected!
I can do:
$ git reset 6666
$ git merge Master
but that requires me to re-do any merge commits.
So how do I compress those different merges into a single merge commit, that knows it's a merge, and also retains all the merge-conflict stuff?
This will do it:
git reset --hard $(git commit-tree -p f5f5 -p Master -m "Merge hurricane" a0a0:)
To understand how it works, first remember that there is absolutely nothing magical about a merge commit. It can be hard to create, but once created, it's embarrassingly easy to recreate exactly. This is because git doesn't record changesets, it records entire trees. Tools like git show
display commits as diffs, and merge commits as very fancy diffs, but in reality a commit is the pointer to a full-fledged tree. Squashing some commits is a simple matter of recreating a new commit that contains the same tree, giving it a different parent and commit message.
The plumbing command git commit-tree
does this last part. Given a parent commit (f5f5
), a commit message, and a tree ID (easily referenced with COMMIT:
), it creates a new commit that contains the specified tree. If the commit has a single parent, it will be a regular commit. If it has multiple parents, it will be a merge commit. Once the squashed merge commit is created, what remains is to point the current branch to the new commit with git reset --hard
.
Try this:
git checkout 6666 -b master_squashed
git merge --squash master
git commit -m 'My new feature'
git checkout master
git merge master_squashed
A late answer:
git diff 6666 f5f5 > patch-f5f5.patch
git checkout 6666
git apply patch-f5f5.patch
As you prefer, you may also use git apply --cached
or git apply --index
which allow you to make further modification before commit.
来源:https://stackoverflow.com/questions/13029516/how-do-i-compress-multiple-merges-into-a-single-merge