I try to commit a lot, and I also try to write useful tests appropriate for the scope of work I'm focusing on, but sometimes I drop the ball...
Whether by laziness, ignorance, or accepted tech debt I don't always code perfectly and recently I was dozens of commits into a new feature before realizing I broke something along the way that none of my tests caught...
Before today I would've manually reviewed every commit to see if something obvious slipped by me (talk about a time suck 😩)
There must be a better way
Bisect?
git bisect
is the magic sauce for this exact problem...
You essentially create a range of commits to consider and let git bisect
guide you through them in a manner akin to Newton's method for finding the root of a continuous function.
How to do it?
Start with git bisect start
and then choose the first good
commit (ie. a commit you know the bug isn't present in)
sandbox bisect-post ×1 via v3.8.11(sandbox) on (us-east-1)
❯ git bisect start
sandbox bisect-post (BISECTING) ×1 via v3.8.11(sandbox) on (us-east-1)
❯ git bisect good 655332b
bisect-post HEAD main ORIG_HEAD
5b31e1e -- [HEAD] add successful print (52 seconds ago)
308247b -- [HEAD^] init another loop (77 seconds ago)
4555c59 -- [HEAD^^] introduce bug (2 minutes ago)
9cf6d55 -- [HEAD~3] add successful loop (3 minutes ago)
bcb41c3 -- [HEAD~4] change x to 10 (4 minutes ago)
3c34aac -- [HEAD~5] init x to 1 (4 minutes ago)
12e53bd -- [HEAD~6] print cwd (4 minutes ago)
655332b -- [HEAD~7] add example.py (10 minutes ago) # <- I want to start at this commit
59e0048 -- [HEAD~8] gitignore (23 hours ago)
fb9e1fb -- [HEAD~9] add reqs (23 hours ago)
sandbox bisect-post (BISECTING) ×1 via v3.8.11(sandbox) on (us-east-1)
❯ git bisect bad 5b31e1e
bisect-post ORIG_HEAD
HEAD refs/bisect/good-655332b6c384934c2c00c3d4aba3011ccc1e5b57
main
5b31e1e -- [HEAD] add successful print (5 minutes ago) # <- I start here with the "bad" commit
308247b -- [HEAD^] init another loop (6 minutes ago)
4555c59 -- [HEAD^^] introduce bug (6 minutes ago)
9cf6d55 -- [HEAD~3] add successful loop (7 minutes ago)
bcb41c3 -- [HEAD~4] change x to 10 (8 minutes ago)
3c34aac -- [HEAD~5] init x to 1 (9 minutes ago)
12e53bd -- [HEAD~6] print cwd (9 minutes ago)
655332b -- [HEAD~7] add example.py (14 minutes ago)
59e0048 -- [HEAD~8] gitignore (23 hours ago)
fb9e1fb -- [HEAD~9] add reqs (23 hours ago)
After starting bisect with a "good" start commit and a "bad" ending commit we can let git to it's thing!
Git checksout a commit somewhere about halfway between the good and bad commit so you can see if your bug is there or not.
sandbox bisect-post (BISECTING) ×1 via v3.8.11(sandbox) on (us-east-1)
❯ git bisect bad 5b31e1e
Bisecting: 3 revisions left to test after this (roughly 2 steps)
[bcb41c3854e343eade85353683f2c1c4ddde4e04] change x to 10
sandbox HEAD (bcb41c38) (BISECTING) ×1 via v3.8.11(sandbox) on (us-east-1)
❯
In my example here I have a python script with some loops and print statements - they aren't really relevant, I just wanted an easy to follow git history.
So I check to see if the bug is present or not either by running/writing tests or replicating the bug somehow.
In this session commit bcb41c38
is actually just fine, so I do git bisect good
sandbox HEAD (bcb41c38) (BISECTING) ×1 via v3.8.11(sandbox) on (us-east-1)
❯ git bisect good
Bisecting: 1 revision left to test after this (roughly 1 step)
[4555c5979268dff6c475365fdc5ce1d4a12bd820] introduce bug
And we see that git moves on to checkout another commit...
In this case the next commit is the one where I introduced a bug
git bisect bad
then gives me:
sandbox HEAD (4555c597) (BISECTING) ×1 via v3.8.11(sandbox) on (us-east-1)
❯ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[9cf6d55301560c51e2f55404d0d80b1f1e22a33d] add successful loop
At 4555c597
the script works as expected so one more git bisect good
yields...
sandbox HEAD (9cf6d553) (BISECTING) ×1 via v3.8.11(sandbox) on (us-east-1)
❯ git bisect good
4555c5979268dff6c475365fdc5ce1d4a12bd820 is the first bad commit
commit 4555c5979268dff6c475365fdc5ce1d4a12bd820
Author: ###########################
Date: Tue May 3 09:00:00 2022 -0500
introduce bug
example.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
What happened?
Git sliced up a range of commits based on me saying of the next one was good or bad and localized the commit that introduced a bug into my workflow!
I didn't have to manually review commits, click through logs, etc... I just let git checkout relevant commits and I ran whatever was appropriate for reproducing the bug to learn when it was comitted!