Using git bisect to find bad commits in your code
A short and sweet guide to git-bisect
Sometimes when we are working on large codebases having lots of commits daily by many developers, we find out that a bug has been introduced to the application. But we do not know for sure at which point this bug was introduced. Going through individual commits is not practical in such a scenario as there can be a large number of commits. This is a situation where git-bisect can be used to find the commit which introduced the bug.
What is git-bisect?
Simply put, it is a command which uses a binary search algorithm to find which commit introduced a bug to the codebase. It can take different subcommands and options based on the subcommands.
How to use git-bisect?
In order to use git-bisect, we have to define what is called a ‘good commit’ and a ‘bad commit’. A good commit tells git that a particular commit is free from the bug and a bad commit tells git that a particular commit contains the bug.
Let us walk through the basic steps to finding the commit which introduced the bug using a set of example commits.
- As the first step, checkout to the branch in which you want to use git-bisect on and use the command git bisect start and then git starts the bisect operation as shown below,
Next, bring up the relevant commit history. Let us consider that the latest commit contains the bug and therefore, we can define it as a ‘bad commit’. The very first commit did not contain the bug and therefore, we can define it as a ‘good commit’.
Next, define the ‘bad commit’ using git bisect bad <commit id> and the ‘good commit’ using git bisect good <commit id>.
(Note: If we do not put the commit id, git will automatically define good or bad to the latest commit in the commit history of the branch).
2. Now we are in the detached head state and it will point to the middle point between the commits defined as good or bad in the history.
refs/bisect/good and refs/bisect/bad shows the good and bad commits respectively.
We can now checkout to the commit that the head is pointing to and verify whether the codebase still contains the bug. If the bug is present, then we should use git-bisect to the first half of the commit history (section before the head). If the bug is not present, then we should use git-bisect to the second half of the commit history (section after the head). In this manner, we can eliminate checking one half of the commit history.
3. Next, we have to define the current commit the head is pointing to as a good or bad commit depending on whether the bug is present or not. For the sake of this example, let us consider that the head is pointing to a good commit at this point. Therefore, we define the current commit the head points to as a good commit.
Now the head will again be pointing to the middle position of that particular segment as shown below,
4. Again we checkout to the commit the head is pointing to and verify whether the bug is present or not. Once verified for the presence of the bug, we define the commit the head is pointing to as a good or bad commit.
This procedure is continued until we come to a point where we have narrowed down the commit at which the bug was introduced. This is shown in the sample picture below,
Therefore, using a few steps, we can narrow down the commits to pinpoint which one introduced the bug. Git bisect therefore, greatly reduces the number of steps we have to go through to find out where exactly the bug was introduced.
For further information, refer the git documentation on git-bisect.