merge revert != ctrl-z

Git revert is not simply an undo

Elliott Gorrell

4 minute read

Just do a revert he says, It will be so simple he says

Today I was asked to review and merge my co-workers pull request on a repo of VPC written in cloudformation. This was for a not yet production live application stack onsite at a client. I don’t like to make a habit of merging other peoples branches as I believe the merge should always be done by the owner. Sometimes with a simple merge complexities arise and it is better to have the person with the context doing the work (Spoiler alert - this is one of those times).

It was quite a large branch and after reviewing it, LGTM! Merged it in to master.

Shortly afterwards the main owner of the repo announced he didn’t want anything being merged to master as the VPC was about to be deployed into new environments via Amazon’s Service Catalog. He wanted to make sure the branch was stable. The call was made to revert the merge so deployments could be conducted.

Disclaimer: You were probably reading the above and thinking to yourself “Where is the development branch!” - A good point however alas there wasn’t one in this case. It may have helped, but also maybe not - the development workflow wasn’t very mature and linting was the only automatic validation so it would of ended up in master pretty quickly anyway.

The merge revert was done with the git revert -m 1 <merge-commit-sha> command, the -m 1 argument telling git we are doing a revert on a merge commit and we want to revert to the first parent of the merge commit on the master branch. If it was -m 2 it would revert to the first parent of the merge commit on our side branch. The VPC was deployed out to the new environments and then I performed a git merge <branch-with-my-changes> again to get my changes back into master and this is where the problems began…

So this is what the git progression in the repo looks like.

git flow

  1. The revert has been performed
  2. A new change has appeared on master
  3. Someone also noticed something that needed to also happen in the branch and added the new change

Alright the master branch has been deployed to whatever environments that team needed it to go, our branch is still up on Gitlab and now has one extra commit (was actually a few but for simplicity sake we will pretend it was just one). These extra branch changes should have occurred in a new branch but we live and we learn hey. Alright so from this point forward is where it is all my bad!

We needed this branch moved asap as our team needed the changes to continue forward, it could only be deployed with CI/CD due to AWS account restrictions and only master was being deployed. I was in panicked rush mode and quickly made a merge request and got it merged - soon after we figured out something was wrong and it seemed half of the code changes that were meant to be there weren’t…

It took me awhile to figure out what was wrong because things in git land had gotten messy and it wasn’t my code however once I realised it became so incredibly obvious.

When you do a git revert it creates a bunch of changes that are the inverse as what has happened. HOWEVER it doesn’t change git history at all, and when git compares branches it knows what commits have already been merged by their SHA. So all the commits from the previous merge were skipped merging in only the new commits on the branch.

This left me with only one option that I could see to get the branch back in:

  1. Create a new branch and cherry-pick all the commits from the old branch so all the commits get new SHAs
  2. Revert the Revert

From what I could see both have their pros and cons: * The revert was quite simple and I didn’t want anything complex with the chance of making things worse. Also as it was just performing all the changes the revert undid I didn’t have to worry about the extra changes that had been introduced from the branch on the second merge. On the other hand this repo doesn’t squash it’s merge commits so I figured valuable historical information from git blame will be lost as all those changes would get bunched under one commit and message. * The new branch approach meant you wouldn’t lose git blame power - however it is quite in depth and you would end up with extraneous blank commits from the changes already introduced between merge #1 and merge #2

Needless to say I went with option 1 as time pressure and confidence was more important than a bit of git history that me or may not get used at this point.

comments powered by Disqus