14

Working with git flow. We have a co-worker who is not familiar with Git that accidentally merged develop into master yesterday.

Develop has quite a few features that are launching with our next release, requiring a revert on the merge. This has created a commit which undoes all of the changes. When we merge master back into develop, the revert commit is removing code generated by our features.

What is the best way of going about being able to synchronize develop with master's hotfixes, while preserving the new features?

-- Edit -- Just to clarify, the revert was a revert. I.E. git revert -m 1 <sha>, as the commit had already been pushed to the remote repository.

Since posting this, I've come up with a possible fix, by branching master and reverting the revert, however I'm curious if there are other possibilities that may minimize collision.

7
  • Please add details on how the revert was done (especially the specific commands used). Was force resetting master to its previous state (instead of reverting) not an option? Commented Jul 24, 2013 at 22:19
  • Also, "accidentally merging branch X into branch Y" should not have been a problem if your coworker hadn't been able to push the merged results up to your canonical repo. Please explain if your developers are all pushing to the same repo, or if you're all using your own private forks. Are you using pull requests via GitHub? Commented Jul 24, 2013 at 22:25
  • It's not Github, it's Gitlab utilizing the Git Flow framework, with standard branch recommendations. Commented Jul 24, 2013 at 23:00
  • The exact command was git revert -m 1 <sha> to force a rollback, as the commit had been pushed to the global repository. Commented Jul 24, 2013 at 23:01
  • 2
    What if you reset master to the state before merge and force push? It requires everyone to sync again but it preserves your history Commented Jul 24, 2013 at 23:08

2 Answers 2

14

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Yep, this is correct, reverting the revert is the way to go. The original revert removes the content but keeps the history; before re-merging from the same branch, you re-introduce the history's content by reverting the revert.
Thanks! It saved me a lot of time
6

Something similar was happening with my team. I actually already have a relatively simple solution, I only found this thread because I was researching ways to prevent this from happening in the first place (still no solution for that).

Here's how I fix it, assuming child branch ("develop") was updated (commit M1) prior to "bad" merge (commit M2) with master:

Problem state

 ... <-- Work after revert that needs merged to develop | R <-- Revert Bad Merge | A <-- Commits after merge, | / but before revert ... </ and needs merged to develop | M2 <-"bad" merge ... ____/ | | / | M1 | | \____ | ... \... develop master 

Step 1

# Get latest from both parent and child branches locally git checkout master git pull git checkout develop git pull # Merge all code from before revert in master branch to develop # (not necessary if "bad" merge into master was immediately reverted) git merge A 

State after Step 1:

 ... <-- Work after revert that needs merged to develop M3 | | \____ R <-- Revert Bad Merge | \ | | A <-- Commits after merge, | | / but before revert | ... </ and needs merged to develop | | | M2 <-"bad" merge ... ____/ | | / | M1 | | \____ | ... \... develop master 

Step 2 - IMPORTANT PART!

# Use "ours" strategy to merge revert commit to develop. # This doesn't change any files in develop. # It simplly tells git that we've already accounted for that change. git merge R -s ours 

State after Step 2

 M4 | \____ ... <-- Work after revert that needs merged to develop M3 \ | | \____ R <-- Revert Bad Merge | \ | | A <-- Commits after merge, | | / but before revert | ... </ and needs merged to develop | | | M2 <-"bad" merge ... ____/ | | / | M1 | | \____ | ... \... develop master 

Step 3

# Merge as normal, from the tip of master to develop. # This should now be an "easy" merge, with only "real" conflicts. # (Those that have changed in both branches) # # Note: I've had issues using origin master to merge from latest on remote, # so instead I just ensure I've pulled the latest from master locally and # merge from there git merge master 

State after Step 3

 M5 | \_____ M4 \ | \____ ... <-- Work after revert that needs merged to develop M3 \ | | \____ R <-- Revert Bad Merge | \ | | A <-- Commits after merge, | | / but before revert | ... </ and needs merged to develop | | | M2 <-"bad" merge ... ____/ | | / | M1 | | \____ | ... \... develop master 

Now develop is updated with the latest from master, without having had to resolve repetitive or meaningless merge conflicts. Future merges will behave as normal as well.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.