Thursday, July 14, 2011

Fun with bisecting

You receive a series to fix an old bug, and the patches apply cleanly to your mainline. It even came with its own test suite to make sure that the fix is correct. It's a maintainer's heaven!

You apply them and run tests, and all seem to go well.

But this series is meant to fix an old bug, so you would want to back-port it on top of an older maintenance branch. After spending some time running "git rebase -i" and resolving conflicts, everything compiles. Victory!

Not so fast. The tests start to fail.

Perhaps you botched the back-porting effort? After merging the older maintenance branch with the series with the tip of the current mainline, you find that it gives the identical result as your earlier attempt to apply the series directly on top of the mainline. There wasn't any mistake in conflict resolution you did.

Now what?
The fix may have exposed an unrelated bug that has been fixed between the older maintenance branch and the current mainline. If this bug really needs to be fixed in the older maintenance branch, the other fix also needs to be back-ported. But you first need to find out which commit fixed the breakage.

 ---o---o---o---o---....---m mainline
      o maint

You can track it down by a (slightly) creative use of "git bisect". We know:
  • f is the fix that breaks something else;
  • When f is merged to m, the other breakage goes away.
The problem we would want to solve is to find the earliest commit x that makes the tests pass when merged with f; notice that if you merge f with f you get f itself, so the problem becomes:

$ git bisect start
$ git bisect bad m
$ git bisect good f 

and the test you would want to run during bisection is something like

$ git merge --no-commit f
$ make test
$ git reset --hard

That is, try merging the fix f with the candidate commit, run test to see if the candidate fixed the test, and clean up the merge. If you see that the other test is no longer broken, you would say

$ git bisect bad # already fixed

and if you see the other test is still broken, you would say

$ git bisect good # still broken

as we are trying to find the culprit that made the other test pass.

After finding the commit that fixed the unrelated other bug that the original fix exposed, you can merge it to f and then merge it back to the maintenance track.

1 comment:

Sverre Rabbelier said...

The suspense is killing me, which commit had the fix? :P