I want to add that I personally prefer the filter-branch
approach, since this doesn't involve the loss of history. If filter-branch
seems too difficult to use, you can take a look at BFG Repo-cleaner which is simpler.
Nevertheless I will provide an alternative for completness sake.
Since you said in the comments, that you have the option to throw away the old merge and create a new one, I would like to suggest that you create a squash merge.
Let's assume your repository has a history similar to this.
* The merge commit [master]
|\
| * feature commit [feature]
| |
| * feature commit (the evil one)
. .
. . (some more commits)
. .
|/
*
First things first, we will undo the merge.
git checkout master
git reset --hard HEAD^
Now we merge feature again, but this time use the --squash
option. When doing this, git won't create a usual merge commit (speaks multiple parents), but it will apply all changes from the feature branch and leave your working tree in a state as if a "usual" merge happened (take a look at the documentation).
git merge --squash feature
Now you can resolve possible conflicts and then simply commit the merged changes (git commit
). However, note that such a merge obviously has no reference on the history of the merged branch (in this case feature); you should include that information in the commit message.
Assuming that there are no other references onto feature - except for the actual branch - you can now continue to delete the branch, followed by a garbage collection.
git branch -D feature
git gc
You have to realise that git gc
will only remove the history and blobs of the feature branch, if you have no other reference pointing to them. You have to make sure that this is the case.
Important
git gc
will not only remove the history of the feature branch but each dangling object. To better understand the implications and consequences I would suggest to read the Git Internals - Maintenance and Data Recovery chapter of the progit book.