The short answer is that you can't (revert and at the same time "undo" the history).
What you can do, though, is repeat the merge on a new, different, temporary branch (such as an anonymous branch) on which the faulty merge has never occurred. See this answer for methods. You can then use the resulting tree to make a "corrected merge" commit on the target branch (master
, in this case). This "corrected merge" commit will not, itself, be a merge commit, just an ordinary commit, but it will have the contents that you want.
If you want to retain the extra "re-merge history" you can make this a real merge of or on the temporary anonymous branch. That's just a matter of supplying the desired parent-commit IDs, whether by doing a real merge into master
, faking up a MERGE_HEAD
file, doing an --ours
merge into the anonymous branch and then swapping the labels around, or supplying the right parents to git commit-tree
. (The exact details depend on how you want any --first-parent
revision tracking to go.)
Note that if needed, you can cherry-pick additional commits into the anonymous branch before using it for the tree. Or, if you elect the "real merge" method, you can just merge the temporary branch result into master
:
... - o - m - w - A - B - * <-- master
\/ /
/\_____ M ______/ <-- temp-branch
/ /
... - o - o - o <-- develop
Here all the o
s are boring ordinary commits, m
is the bad merge, w
is its reversion, and A
and B
are commits on master that you wish to preserve (they're shown as non-merge commits but they can be merges: the important part here is their attached trees). Then, M
is the re-done, correct merge on the temporary branch, and *
is the final merge back into master
, after which you can delete the temp-branch
label while retaining the underlying branch in the commit graph. In this particular scenario you'd use these commands (though I omit any merge conflict resolution required):
$ git checkout -b temp-branch <master-commit-before-m>
$ git merge develop
$ git checkout master
$ git merge temp-branch # be sure to fix up the merge message
$ git branch -d temp-branch
and the first parent of commit *
is then B
, with its second parent being commit M
.
The trick here is remembering that you need to do this, and finding the correct commit on master
(the one that was the tip of branch master
just before the faulty merge; it's the first parent of faulty merge m
, and you can give it a tag now, or even give it a branch name now, while you know where it is, rather than using git checkout -b temp-branch <sha1>
later after having to re-locate the SHA-1—but if you do this, pick a name that's more meaningful than temp-branch
!).
The "remembering that you need to do this" is really the hardest part. All the rest is mere fussing-about with git.