We use mercurial in kind of a hub and spoke manner.  A central repository that everyone pushes to, and branches for the main processes, development, QA, production, etc. All the developers pull the latest from the central repository, commit their changes and push them back to the central repository.

Managing the streams is fairly straightforward. The normal process is that features will get developed on a feature specific branch, then merged to dev. Then dev gets merged to QA for testing, QA gets merged to production as part of the release process.
 Production patches would happen in the prod branch and then get merged back down the pipeline. It looks something like this diagram.

The problem comes up when someone doesn’t merge to the correct stream.  Instead of merging a feature into the dev mainline to promote through the testing process, they might merge with prod instead.  So how do you fix the problem?

We had this problem arise recently. The solution that was implemented was to create a new prod branch off of the last good prod branch and have everyone start checking into that.  The approach seems heavy handed, but I was assured there was no better way to rollback the changes. Partly this was because changes that were made then rolled back couldn’t be promoted to that branch in the future. I thought this seemed a little weird, but I’m not a Mercurial expert so I thought I would research the problem and find a better way.

Basically, we’ve got a problem. Instead of merging feature2 into the dev mainline, it was accidentally merged to prod. Since it was merged and pushed to the central repository, you can’t just use a rollback statement to remove the change.

However, if you make a change to the branch you can make it so that the bad merge virtually didn’t happen without rewriting history. Basically, the approach is to revert to the previous version, make a change and the commit it. In this example, the last good revision number for prod was 14. So we update to version 14, revert all the subsequent changes, then commit.

~/work/mercurialtest/prod$ hg update -C -r 14
resolving manifests
removing feature2.txt
getting feature1.txt
getting list.txt
2 files updated, 0 files merged, 1 files removed, 0 files unresolved
~/work/mercurialtest/prod$ hg revert --all -r 14
[Make some change that will allow you to commit]
~/work/mercurialtest/prod$ hg commit -m "undoing merge"

Provided everyone makes sure to pull and merge with only the latest version of the code, this works fine.  It produces a little dead end version in the branches, but subsequent commits to prod will be based off of the new revision.

So what happens if someone decides to merge to that branch without getting latest first? Well, it’s like Ron White says “You can’t fix stupid.”

Mercurial will let you, and the whole process will start over again.