Using hg bisect to hunt down bugs
The “bisect” command in Mercurial (git has it too) is a great way to quickly find which version of your code introduced a bug. This post will show you how to use the bisect command along with handling a small “gotcha” I encountered while using it.
I recently had to hunt down a bug in a very old version of an application. While the application had gone through numerous releases after the bug had been introduced, nobody had noticed. And as luck would have it, this particular bug was also in a section of code without unit tests – I’d have to load up the code in Visual Studio and run the app to verify the bug. Since I tag each revision in Mercurial that is used to produce a release, I knew which revision the users first noticed the bug – and I knew the previous release didn’t have the bug. The problem was that between the bad release and the good release was about 26 commits. I used Mercurial’s bisect command to make locating the bug quicker.
The first thing to do when doing a bisect is to ensure your working directory has no uncommitted changes. The bisect command will perform an hg update automatically as it helps you zero in on the bug so it’s important you don’t have any uncommitted code.
Next, I went to my working directory and initialized the bisect operation by running:
hg bisect –r
Now, I had to tell bisect which version was bad (where I knew the bug exists) and which version was good (where I know the bug does not exist). In this case, revision 581 was good and revision 606 was bad:
hg bisect –g 581
hg bisect –b 606
Mercurial responded:
Testing changeset 593:e3c0018aef4d (25 changesets remaining, ~4 tests)
37 files updated, 0 files merged, 23 files removed, 0 files unresolved
Mercurial had immediately updated my working directory to revision 593. This revision is about halfway between the good revision and the bad revision. Here’s a graphical view of what I told Mercurial:
I opened Visual Studio to load the project up and test the application. I only have Visual Studio 2010 installed and this was a Visual Studio 2008 solution (see, I told you it was old). I let Visual Studio perform the conversion. I then ran the app and found that the bug exists in this version. At this point, I needed to tell Mercurial that this version was bad. At the command prompt I entered:
hg bisect –b
Mercurial’s response was an error:
Testing changeset 587:d6e96f87a104 (12 changesets remaining, ~3 tests)
abort: outstanding uncommitted changes
Since 593 was now the first bad revision, Mercurial tried to update to a version between 593 and 581 but couldn’t because there were uncommitted changes. Remember how I said this an old VS2008 solution? Since I upgraded it to VS2010, the solution was changed and Mercurial stopped the automatic update that the bisect command wanted to do.
For this situation, I didn’t care about the change to my working directory. So I had to revert all of the changes before I could continue with the bisect:
hg revert –a
hg bisect –b
Now the bisect worked and put me at revision 587, which is just about right in between the last known good version and the last known bad version:
Testing this version in VS2010 (again, the VS2008 solution was updated to 2010), I found the bug didn’t exist. So I went back to my command prompt, reverted my changes and marked this version as good:
hg revert –a
hg bisect –g
I was getting closer! I’m now at revision 590 and the bug still existed. I revert and mark it as bad:
hg revert –a
hg bisect –b
After this, Mercurial updated my working directory to revision 588. I tested it and found the bug. I did my revert and marked it bad:
hg revert –a
hg bisect –b
Mercurial now reports 588 as the first revision where the bug appeared (since 587 was already marked good).
The bisect command is very useful for quickly hunting down bugs. In this case, it took a little longer since I had to load the code up into Visual Studio (and convert it), run the code and then revert my changes afterward. If the code didn’t need to be converted and had a full set of unit tests, identifying the revision where the bug first appeared would have been even quicker. In fact, the whole process can be automated with the “-c” option which allows you tell Mercurial the name of the command to run to validate the revision!