Many developers have heard about Git Bisect, but only few gave it a try. It’s a pity, because it can… find a bug without your attention!
Git Bisect is a tool which comes pre-installed with Git, which is meant to find particular commit. But unlike Git log it’s not meant
to find commit by its details, for example commit message or introduced changes, but by behaviour which it introduces to system.
How Git Bisect works
Git Bisect uses binary search algorithm to check commit. You need to set commits range and then Git Bisect checks commit in the middle of the range.
If behaviour which we are looking for exist in this commit, then Git knows that it was introduced in first part of the range, otherwise in the second part.
This way range gets smaller by half and then algorithm repeats up to the point, when Git is sure which commit has introduced given behaviour.
If you create small commits and each of commit has one, consistent change into the system, then you not only see which commit
introduced given behaviour, but also which changes in code are guilty.
How Git Bisect knows that particular commit has given behaviour
When Git Bisect has selected the commit, there are two modes to check if system at this point of time had given behaviour.
You can do it manually - simply run the system and tell Git the answer or…
You can do it automatically. Just pass to Git Bisect script or program which tests system and returns 0 exit code if system works good
or exit with a code between 1 and 127 (inclusive, except 125), if currently there is bad behaviour.
Can Git Bisect be used to other purpose than finding a bug
Usually we talk about bad behaviour - a bug, that’s why by default we operate with terms good and bad, but you can use Git Bisect to find other type of introduced behaviour,
for example improved performance, so you can use alternate terms. By default, you have
two alternate terms given: old and new, but you can set up your, to make bisecting easier to understand.
If you want to know how to achieve it, it’s described nice in documentation, I don’t want to lose focus on general usage here, so let’s move forward.
Git Bisect manual demo
Let’s imagine, that we have noticed, that our system at the newest version has regression bug, which for sure
did not exist in last release, tagged as v1.2.
To start bisecting we need to write
git bisect start.
Then we need to set end of commits range, by writing
git bisect bad *COMMIT_ID*.
As we know that the newest version has it, we can skip *COMMIT_ID*, to have HEAD taken.
Then we need to set beginning of commits range, by writing
git bisect good *COMMIT_ID*, which in our case
git bisect good v1.2.
When commits range is set, Git chooses first commit. Output may look like this:
Bisecting: 10 revisions left to test after this (roughly 4 steps)
[3007a714657da73173b7236d3821bbea0555bd2b] Adjust fonts
Now we need to check system: recompile, run, and test manually if bug exist.
If so, then write:
git bisect bad. If bug doesn’t exist, then write:
git bisect good.
This process repeats until commit which introduced the bug is found, which is indicated by commit details
At this moment we are still in bisecting mode. To finish it, we need to write
git bisect reset, which returns us to the
commit that was checked out before
git bisect start (unless you don’t specify other commit id as parameter to reset command).
There are other commands to use during bisecting, but they are additional. If you are curios which are those, they are described clearly in documentation.
I don’t want to distract us, so will skip them here, with exception to one convenient command:
git bisect visualize, which shows
us current commits range and currently selected commit. If you have gitk installed it will show you there, otherwise in terminal.
Git Bisect automated demo with CodeceptJS
Of course, checking if bug exist is a boring process which usually can be easily automated.
As our system is website, we will use CodeceptJS.
Initializing it, and creating test scenarios is out of scope of this article, so if you’re new to it, just follow theirs official manuals.
We need to describe behaviour in test scenario, which need to be initialized in directory outside of our repo, or in untracked
file in our repo, to have it unchanged when Git Bisect changes commits. Of course after finding bug, we will commit those new
scenarios to rest of our system test scenarios, to not have this regression any more in the future.
Let’s assume, that in our case we run webserver once, and it refreshes itself when files change, during commits switching (like for example Jekyll does).
Then we need to start bisecting and set commits range like before.
Next, instead of telling Git whether given commit is good or bad we run automated testing:
git bisect run node codeceptjs run --steps. The first argument after run keyword is the script/program to run, next
are arguments which will be passed along. In our case it’s CodeceptJS with its arguments. If you don’t have
CodeceptJS globally installed you can pass path for example from node_modules directory:
git bisect run node node_modules/.bin/codeceptjs run --steps.
That’s it, Git Bisect will check every chosen commit with this script to the point, when it knows which commit introduced given behaviour,
in our case - a bug. Like in manual process you need to reset bisecting mode at the end -
git bisect reset.
What if you need to compile system during every check, or do some other preparations? It’s not a problem, you simply need to put
those tasks in script/program which got executed by bisect.
In my view Git bisect with automated tests look spectacular, so I have created a video Demo, if you prefer to watch before trying yourself :)
To summarize, Git Bisect is a simple tool, which can be really helpful. When? In situations, when you have a bug (or other behaviour),
which is hard to debug, so it’s easier to find which commit introduced it, especially when you create small, consistent commits.
I have seen developers who do binary search manually, counting and switching commits.
It’s giant help especially, when you have good automated tests, so you can just add new test scenario, then Git Bisect will take care of rest of the job.
Do you use Git Bisect or have some thoughts about it? Please share it through comments. And send this article or video demo
to colleagues who hasn’t heard of it :)