How do you easily unmerge a topic branch in Subversion/git?
So I have
/trunk, to which all things golden are eventually committed. And I have a topic branch,
Normally, you would merge
/topicA into trunk and it would be one commit. Hooray! To undo this merge, you just have to reverse merge the commit caused by this merge. Easy peasy.
Now, let’s say I have
/topicA and I merge it into
/trunk… But then I have a bugfix that relates to
/topicA. So, should I be committing to
/topicA again? I can even remerge
/topicA so that
/topicA. But how to I rollback all of
/topicA, original work AND bugfixes, easily?
- Commit to
- Commit to
- Commit to
- Bugfix1 to
- Bugfix2 to
- Bugfix3 to
- ??? (unmerge
/topicA, bugfixes included)
I know you can just unmerge each bugfix and then the initial merge, but that doesn’t seem sexy at all.
Does git have an answer to this as well?
6 Solutions collect form web for “How do you easily unmerge a topic branch in Subversion/git?”
You can rewrite history using
git rebase --interactive --preserve-merges <hash before first merge>
This will bring up your default text editor with a list of commits including your merges. Remove all merges from the list that comes up in the editor and save, then exit. git will replay all the commits excluding the ones that you removed, thus rewriting history. Your /topicA branch will still exist, so you can simply merge it into truck.
It helps that all the merges have the words “Merge branches” in the interactive editor, so it’s easy to identify them.
Here’s a example of the text in the editor that is bought up after the command is issued on a fictional repository
pick 07c2942 Added "Hello" to README pick e98e38b Added "There" to README pick e3d66ad Added "Here" to README pick 2105946 Merge branches 'master' and 'tmp'
You don’t want the merge to happen so you simple remove that line and the line just before it since it is part of the merge in this case.
pick 07c2942 Added "Hello" to README pick e98e38b Added "There" to README
Save and exit and git will now replay these commits, minus the merge.
Note you can remove any line, and that commit will be removed from the repository. Also you can rearrange the commits to whatever order you like. If you have merges in the middle, you can cut and paste them to the end.
In our project, we keep one more branch until integration is done (trunk_plus_topicA in your case). It’s created from the trunk. We merge to it commits from the trunk as well as from topicA.
Then, after integration tests are done, and we are sure we want topicA, the whole topicA_plus_trunk gets merged into the trunk (or you can replace trunk with the trunk_plus_topicA).
In canse when topicA is going to be merged only to the trunk (or dropped at all), you don’t need extra branch. You can simply merge commits from the trunk to the topicA. Do bugfixing, integration and testing in topicA, and at the end merge it all at once to the trunk.
As my friend says – “the easiest way to fix a broken plate is to not brake it in the first place.”
This advice doesn’t work well in a real life, but works quite often in software engineering.
If you haven’t merged in any other work in the mean time, you could use
git reset --hard <sha-1> to reset your working tree back to an older time, and then remerge
topicA on top of
Unfortunately it’s likely there is no easy answer for this. It does depend on whether the commits have been pushed and are therefore “public” to other developers (who may have then based work on top of them).
If the commits have been pushed your only real solution is to do a bunch of reverts and push those.
Otherwise you have options to reset before the first merge, finish the bugfixes on topicA and then merge after they’re all done (as X-Istence suggested).
As a rule our team generally kills off the topic branch once it’s been merged to our equivalent of “trunk” and pushed (we also preach using squash merges so it all comes in one single commit). Then any bugfixes are just applied to “trunk” itself. If the topic branch shouldn’t have landed, we can always re-generate the topic branch from the commit and then revert the merge. The developer then skulks away, does more work on the topic to get it more stable, and tries to merge it in later.
You could revert each of the merges with:
git revert -m 1 <id of second remerge> git revert -m 1 <id of first remerge> git revert -m 1 <id of first merge>
But if you are going to want to merge topicA at some point after this, be sure to read http://www.kernel.org/pub/software/scm/git/docs/howto/revert-a-faulty-merge.txt before you do this.
Revert the trunk to before the first time you merged the branch into it. Then re-merge each change unrelated to the branch which you had done to the trunk.
This is far from elegant or ideal or ‘sexy’, but can be much easier than unmerging branch changes one by one, depending on how much work unrelated to the branch was done on the trunk.