Option 1: Hard reset and force push
If it's possible to do a non-fast-forward forced-update to your master
branch
in your upstream repository, then instead of reverting the merge of develop
into master
, you could simply do a hard reset of master
:
# On master branch, do a hard reset back to the commit before the merge
git reset --hard <commit of master before the merge>
# Force push to upstream ONLY IF IT'S OK WITH OTHER DEVELOPERS
git push <remote> master --force
A possible disadvantage to doing a hard-reset and force-push is that if other
developers have already based work off of the merge commit (i.e. have made
commits on top of it), then they'll need to redo that same work on top of the
reset head of master
. This may or may not be a difficult/costly task for
them.
Option 2: Revert the revert
I tested this out with a quick test repo. I have to stress that it might
work, I'm not 100% confident that there aren't any cases that I didn't
consider. So be sure to test it out locally with a backup clone of your repo
first. If you choose to use this in your actual repo, please do so at your own
risk.
Also, this may not be the easiest/simplest solution. Its advantage over the
hard-reset option, however, is that it doesn't force developers to have to redo
work on top of a reset master
branch.
Ok, with all of that out of the way, one thing you could try doing is merging
master
into develop
, then revert the revert of the merge from develop
into
master
, then merge develop
into master
when you're ready. In commands:
# Coworker accidentally merges develop into master before it's ready
git merge --no-ff develop
# You revert the merge in the master branch (this creates commit "ABCDEFG"
git revert -m 1 <sha of merge commit>
# You want to merge fixes from master into develop
git checkout develop
git merge --no-ff master
# But now all that work in develop is reverted, so revert the revert "ABCDEFG"
git revert ABCDEFG
# When you're ready to merge develop into master...
git checkout master
git merge --no-ff develop
Here's a sequence of commands I used to test this out in a test repo:
mkdir practice
cd practice/
git init
touch readme.txt
git add practice.txt
git commit -m "Add practice.txt"
git checkout -b develop
touch feature1.txt
git add feature1.txt
git commit -m "Add feature 1"
touch feature2.txt
git add feature2.txt
git commit -m "Add feature 2"
git checkout master
touch hotfix1.txt
git add hotfix1.txt
git commit -m "Fix issue 1"
git merge --no-ff develop
# Creates commit "ABCDEFG" that reverts the merge
git revert -m 1 head
git checkout develop
git merge --no-ff master
git revert ABCDEFG
git checkout master
git merge --no-ff develop
You can read more about the "Reverting Revert" technique at the official Linux
Kernel Git documentation for git revert
:
-m parent-number
--mainline parent-number
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.
See the revert-a-faulty-merge How-To for more details.
The link to How to revert a faulty merge is highly recommended if you
fully want to understand how this technique works, it's not difficult to
understand and it actually kind of interesting and fascinating.