Let’s say we have this situation:
--o---o---o---M---x---x master / --A---B my-dev-branch
A and B are on development branch “my-dev-branch,” which actually had some bugs in it. M is the merge that brings these premature changes into master, x are changes unrelated to what the side branch did (so maybe some bug fixes, other merges, or whatever).
So we need to get rid of M. On your local development environment, you need to first pull in all changes from origin (Bitbucket, or wherever your remote repo is), make sure you are on master, then execute the following:
$ git revert -m 1 <M>
Where <M> is the SHA hash of merge M. Then commit and push that. At this point, we have:
--o---o---o---M---x---x---M^ master / --A---B my-dev-branch
We’re all good now — disaster averted!
However, when we make fixes to my-dev-branch and want to merge back in, we can’t just re-merge the branch. This is because M^ undoes the code changes from the other branch, but the history remains intact — git still knows that my-dev-branch was merged into master at some point. So if you make changes C and D like so:
--o---o---o---M---x---x---M^---x---x master / --A---B---------------C---D my-dev-branch
And then try to merge back into master, only C and D will come over.
So how do we merge the branch back in? We have to first undo M^ (basically, revert the revert), and then merge our branch in, like so:
$ git revert <M^>
$ git merge my-dev-branch
This post was a distilled version of this post by Linus Torvalds on how to revert a faulty merge.