Is there a way to cleanly and easily undo a git merge?

Viewed 1313

I've read that doing git reset commitBeforeDoingMerge --hard would no undo the merge:

1pm-3pm This is my branch and I need to merge master into it
1pm-2pm This was master
1pm-2pm-3pm-4pm Merging ended up doing this, 4pm being the merge commit
1pm-2pm-3pm if This is the result if you do git reset 3pm --hard, because it does not undo the merge, it just goes back to that commit in the branch that was merged.

Now, another proposal is to do git revert 4pm -m 1, but that has a problem according to the manual:

Usually you cannot revert a merge because you do not know which side of the merge should be considered the mainline. This option specifies the parent number (starting from 1) of the mainline and allows revert to reverse the change relative to the specified parent.

Reverting a merge commit declares that you will never want the tree changes brought in by the merge. As a result, later merges will only bring in tree changes introduced by commits that are not ancestors of the previously reverted merge. This may or may not be what you want.

So, is there a way to cleanly and easily undo a git merge?

3 Answers

I've read that doing git reset commitBeforeDoingMerge --hard would no undo the merge

Wherever you read that, it is wrong. That is exactly how to undo a merge.

Let's say you are merging branch into master. So we have this before the merge:

A -- B -- C (master)
 \
  X -- Y (branch)

Okay, so we git checkout master and git merge branch. The result is:

A -- B -- C -- D (master)
 \             |
  X -- Y (branch)

...where D is the merge commit, with C and Y as its parents.

If you now say git reset --hard <SHA-of-C>, you are back to this:

A -- B -- C (master)
 \
  X -- Y (branch)

It is a perfect Undo.

I'm still not sure what you are doing, but this is not how git merge works. I will try to recreate what you did and maybe you can spot the difference.

First i created your two starting branches:

git log --oneline --graph --all
* 0593ba1 (HEAD -> test) 3pm
| * eba415c (master) 2pm
|/
* 7f9a804 1pm

Merging master into test. git merge master -m "4pm"

git log --oneline --graph --all
*   6be609f (HEAD -> test) 4pm
|\
| * eba415c (master) 2pm
* | 0593ba1 3pm
|/
* 7f9a804 1pm

now we reset test back to 3pm

git reset --hard 0593ba1
HEAD is now at 0593ba1 3pm
git log --oneline --graph --all
* 0593ba1 (HEAD -> test) 3pm
| * eba415c (master) 2pm
|/
* 7f9a804 1pm

now we see the 2pm commit is only in master, to confirm the log of only test

git log --oneline
0593ba1 (HEAD -> test) 3pm
7f9a804 1pm

that is how I merge and undo the merge, please feel free to let me know what you are doing differently or where I have not recreated you example how you need it to be.

The reset should have worked, but on master, resetting to e:

After the merge:

a-b-d  (mybranch)
  \
   c-e (master)

A merge from myBranch to master should give:

a-b---d  (mybranch)
  \    \
   c-e-f (master)

There is no branch which would list abcdef, ony abcef (master)

If you reset --hard e while on master you would get back the original history (first one above)

If you reset --hard d while on master, you have move master HEAD but the merge still exists:

a-b---d  (mybranch, master)
  \    \
   c-e-f
Related