Recently I’ve come across a couple dev issues that were incredibly difficult to track down.
Normally, fixing a bug in JavaScript is as simple as reproducing the issue, viewing the error and the stack trace, finding the appropriate source file, and fixing the code.
But what if the error for whatever reason isn’t helpful? Or what if there is no error? Maybe nothing is technically “broken”, but the behavior that’s occurring in your app is not at all what you expected or intended. Or maybe the error is coming from a third-party dependency that you recently upgraded.
Whatever the case may be, if you aren’t able to easily track down where in your source code the error is coming from, git has a really neat command that can help.
Git Bisect
git bisect
is a command that uses a binary search to identify the commit that introduced the issue. Using it is really straightforward.
To begin the process, you enter git bisect start
.
Next, you need to identify two commits, one in which the issue exists and one in which the issue does not exist.
So, maybe you’re on your master
branch right now with the latest code pulled down, and you’re seeing the bug. So you can tell git that this current commit is bad. To do that, you enter git bisect bad
.
Now, you need to find a commit where the bug doesn’t exist. To do that, you can look through your repo’s recent commits by entering git log --oneline
. This will give you a list of past commits, working its way backwards from the commit you’re currently on, with each commit being on its own line.
Use your own judgment to pick a commit that you think might not have the bug. If you’re not sure, it’s ok to check out a commit, compile your code, look for the bug, and then try again with a different commit if you haven’t found a good commit yet.
Once you’ve found a good commit, you can tell git by entering git bisect good <good commit hash here>
.
Now, git starts its binary search. What it does is essentially grab the middle commit between your good and bad commits and checks it out. Now that the commit is checked out, you can compile your code and test to see if the issue exists or not.
If the issue exists on this commit, you tell git with git bisect bad
. If the issue does not exist on this commit, you tell git with git bisect good
.
Upon receiving this feedback, git checks out another commit in the middle of this commit and the good/bad starting points, depending on the feedback you gave it.
Again, you compile your code, test it, and let git know if you see the issue or not with git bisect bad
or git bisect good
.
This process repeats, and it’s in the most efficient way possible with the least number of steps because of the binary search.
Once you’ve finished, you’ll have identified the commit that introduced the issue. To tell git you’re done bisecting, you enter git bisect reset
.
At that point, you can go check out the pull request in GitHub (or whatever you’re using), read through the code, and get a better idea for where exactly the problem lies.
Conclusion
git bisect
has been a lifesaver for me in these instances. By using it, I’ve been able to track down the offending commit, get a better idea of what the root cause of the problem could be, and then start fixing the issue.
Thanks git!
You can read the full documentation from git here.
Top comments (13)
git bisect
is a life saver! It has saved my bacon several times. If you have automated tests, you can even run bisect on autopilot, if the test has a 0 exit status, it treats it as good, if it has a non-zero exit status, it treats it as bad. Just start it, go get some coffee, and come back to find your bad commit!see here for more details: lwn.net/Articles/317154/
Neat! Thanks for sharing.
It seems like if you're using a continuous integration model though where your tests are always running on each commit, and it's required that your tests pass before you're allowed to merge your new code into the master branch, you wouldn't end up in a situation in which you have failing tests on your master branch. Right?
So is automating
git bisect
assuming that you're not enforcing 100% passing tests? Or would you run it on your own branch in which you have multiple commits that haven't had their tests validated and you've introduced a bug somewhere in there, but you still haven't merged to master yet? Or what's the use case here?That's a good point.
I think the use case would be if you uncover a bug that you don't have an automated test for, but you can then write one that reproduces the bug. Then you run git bisect in automatic mode. Once the issue is fixed, you can add that new test to your test suite. 🤷♂️That's the best one I can think of.
Ooh didn't know about
git bisect run
.Wouldn't that be too slow for web apps that have no compilation step?
Love this article Tyler, thanks! You gave me an idea about using
rspec bisect
to track down bugs in Rails tests :-)Thank you! Glad you liked it.
Great explanation Tyler!
Damn. This was actually mindblowing! Thanks for sharing!
You’re welcome! Glad you enjoyed it.
Thanks for the great description Tyler. I had no idea how git bisect was used. It looks quite useful.
You're welcome! Thanks for reading.
I come back to this post over and over and over again. Good reference guide to something I use often. Thanks!
No prob!