Skip to content

Bisecting bugs with binary search

The problem

A couple of days ago while working on my blog, I found out that the full-text site search didn’t work anymore. On the desktop, I couldn’t see the search UI at all as seen in the following screenshot.

Desktop search UI missing

And mobile search UI looked pretty strange with two search inputs…

Two search input in mobile view...

Something was wrong and I needed to get to the bottom of it. At the time I noticed the issue I was setting up astro-compress to compress static assets after the build. I thought maybe something went wrong during the compression so I tried excluding pagefind (Starlight uses Pagefind to implement full-text site search):

astro.config.mjs
import compress from "astro-compress";
export default defineConfig({
integrations: [
compress({
Exclude: [(file) => file.includes("pagefind")],
}),
],
});

Neither excluding these assets nor disabling the compression altogether helped. It was high time to turn to git bisect for help.

Bisecting the bug with Git

git bisect is (obviously) a git command that uses binary search to find the commit that introduced a bug. It does so by asking for good and bad commits and once these commits are identified it starts halving the commits between these until the commit that introduced the bug is found.

Locating and fixing the bug

I knew that the latest commit had the bug so it was time to start the bisecting process so I ran the following command in the terminal:

Terminal window
git bisect start

Here is the output of the command:

Terminal window
bugron.github.io git:(test) git bisect start
status: waiting for both good and bad commits
bugron.github.io git:(test) git bisect bad
status: waiting for good commit(s), bad commit known

Notice there is no need to mark the very first commit as bad when starting to bisect as git bisect start already assumes that the current commit is bad. All I needed was to find a good commit and to do that I went back to the last known good commit (by git checkouting older commits and checking for the existence of the bug) and ran git bisect good:

Terminal window
bugron.github.io git:(test) git checkout 69da06aa1c9e2d620ac3ebba25ddf956c31d8cf5
Note: switching to '69da06aa1c9e2d620ac3ebba25ddf956c31d8cf5'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 69da06a Add light and dark logo variations
bugron.github.io git:(69da06a) git bisect good
Bisecting: 2 revisions left to test after this (roughly 2 steps)
[d1eef597555defb5c235013142a42c82fed8120b] Add lastUpdated config

As you can see above I’ve git checkouted to the 69da06aa1c9e2d620ac3ebba25ddf956c31d8cf5 commit and as it did not contain the bug I marked it as a good commit with git bisect good. Git immediately started the bisection process by checking out the d1eef597555defb5c235013142a42c82fed8120b commit and even showing a rough estimation of the number of remaining steps.

From now on here is the algorithm I followed:

1. Check if a commit contains the bug
2. If it does mark it bad with `git bisect bad`
3. If it does not mark it good with `git bisect good`
4. Repeat steps 1-3 until the commit that introduced the bug is found

After a few iterations as the final step git presented me with the following commit:

Terminal window
bugron.github.io git:(fc71196) git bisect good
fb05597e11b01673c133b977542c9360b9ad2619 is the first bad commit
commit fb05597e11b01673c133b977542c9360b9ad2619
Author: Arsen Melikyan <bugron@mail.ru>
Date: Sun Jan 7 19:41:24 2024 +0400
Enhance current header for mobile experience
astro.config.mjs | 3 +-
src/components/Header.astro | 126 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 128 insertions(+), 1 deletion(-)
create mode 100644 src/components/Header.astro

Interesting. The Enhance current header for mobile experience commit is exactly where I made some changes to the header component but I didn’t change the search functionality at all. Or so I thought…

OK, let’s see what this bad commit did with git show fb05597e11b01673c133b977542c9360b9ad2619. For the sake of brevity I won’t show the whole output of the command but here is the relevant part:

git show fb05597e11b01673c133b977542c9360b9ad2619
diff --git a/src/components/Header.astro b/src/components/Header.astro
new file mode 100644
index 0000000..aaed8ae
--- /dev/null
+++ b/src/components/Header.astro
+<div class="header sl-flex">
+ <div class="sl-flex">
+ <SiteTitle {...Astro.props} />
+ </div>
+ <div class="sl-flex right-group md:sl-hidden">
+ <div class="search">
+ <Search {...Astro.props} />
+ </div>
+ <div class="blog-link">
+ <a href="/blog">Blog</a>
+ </div>
+ <LanguageSelect {...Astro.props} />
+ </div>
+ <div class="sl-hidden md:sl-flex">
+ <Search {...Astro.props} />
+ </div>
+ <div class="sl-hidden md:sl-flex right-group">
+ <div class="sl-flex social-icons">
+ <SocialIcons {...Astro.props} />
+ </div>
+ <div class="blog-link">
+ <a href="/blog">Blog</a>
+ </div>
+ <ThemeSelect {...Astro.props} />
+ <LanguageSelect {...Astro.props} />
+ </div>
+</div>

Hmm, at this moment all I had were assumptions but one thing I saw was I had two “instances” of <Search /> (remember the mobile search UI had two search inputs?) one of them should only be visible in mobile view the other one in desktop view.

I mistakenly thought that it would be OK to do this and then just use CSS to control the visibility of the search UI but Starlight had another idea so I attempted to remove the second instance of <Search /> and that fixed the issue. Afterwards, I simply polished the UI and committed my changes. Everything worked as expected and I deployed my changes 🚀

Conclusion

git bisect is an incredibly powerful tool in the git arsenal for finding bugs if git is used correctly. It takes some discipline and practice to keep commits atomic and small but it’s worth it in the long run. I highly recommend using it.

I hope you found this article useful. Until next time 👋