I recently implemented a "sticky toolbar" feature on our site. It was one of the first times I'd composed something new from the existing Vue components we'd built for the rest of the page, and it was a heck of a lot of fun. The features of the toolbar sort of came together without a lot of work other than the CSS to get the positioning right. I felt pretty good when I finally merged that code.

A day later, I was perusing the site near the end of the day and happened upon an issue. The styling was borked in one of the dropdown menus in the toolbar. I naturally worried my recent change had caused the issue and that I'd somehow missed it. I checked that the latest commit on master was indeed exhibiting the issue locally, and sure enough it was. A few other changes had come in since mine, so I checked out the last commit from my change. I ran the build and refreshed the page, but everything looked good.

I was relieved not to have caused the regression, but that meant I didn't know where the bug was introduced. I spent a good 30 minutes inspecting elements and CSS in Chrome DevTools trying to understand what changed before remembering there was a better way. git-bisect to the rescue!

git-bisect is a tool for running a binary search through your commit history. It allows you to place goalposts that mark a particular commit as good or bad, incrementally narrowing the search space until you arrive at the commit that introduced a change. Most often you will start a git-bisect session and mark the latest commit as bad, since you already know there's an issue there:

$ git bisect start $ git bisect bad

The next step is to figure out a time in the past when the bug wasn't there.

I had already found that my latest commit was okay, so I started there:

$ git bisect good a56f1b2 # The SHA of my latest commit Bisecting: 24 revisions left to test after this (roughly 5 steps) [ab6c7a77d...] Add social media icons (#197)

Here's where the magic happens. Git now knows that the latest commit has an issue, and a point in the past where the issue did not yet exist. I've set the bounds for the search space, and Git has dropped me onto a commit in the middle of the two I've specified.

✅ . . . . . . . . . . . . v . . . . . . . . . . . . 💔

Now I check: does the issue exist here? No. I can go ahead and tell Git:

$ git bisect good Bisecting: 12 revisions left to test after this (roughly 4 steps) [bb8c65a43...] Increase contrast for small text (#203)

Git has cut the search space in half and put me in the middle of it again.

✅✅✅✅✅✅✅✅✅✅✅✅✅✅ . . . . . . v . . . . . . 💔

Does the issue exist here? Ooh, it does! I let Git know:

$ git bisect bad Bisecting: 6 revisions left to test after this (roughly 3 steps) [97e12fab6...] Improve exception logging (#202)

We're getting closer.

✅✅✅✅✅✅✅✅✅✅✅✅✅✅ . . . v . . . 💔💔💔💔💔💔💔💔

After a few more steps, I finally have a single commit left and Git spits out the info for that specific commit to let me know:

$ git bisect bad 6c3853827... is the first bad commit commit 6c3853827... Author: Arthur Q. Dev Date: Thu Jan 10 16:11:12 2018 -0500 Update shared menu styling (#199)

✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅💥💔💔💔💔💔💔💔💔💔💔💔

I can now see precisely what changed in the commit in question, usually making it much easier to see what specific code change caused the bug to happen. These good/bad steps only take as long as the test to determine if the issue is present, so the whole git-bisect session can take as little as a couple of minutes. Once you find the offending commit, you no longer have to speculate about what might've happened--you can look directly at the change and think critically about it.

You can use git-bisect for more than just bugs. Sometimes you just want to know when something changed. git-bisect also understands git bisect [old|new] natively, but if you're tracking down something different or prefer different terms you can pass them to git bisect start using --term-[old|new] .

I don't always use git-bisect but, when I remember to, I love it. Check out the full documentation and fully rock this tool.