How to hunt down bugs with git bisect
Just imagine you're on the hunt of finding a bad commit. You could just randomly checkout some commits and check whether or not they are broken. If not you'll go on with the next one. This process is painfully slow and takes much time.
There is a better way of handling this: It's called
The idea behind
git bisect is that you it asks you whether or not a commit is broken and narrows down the window until you find your commit in question.
bisect operates on a binary search. So you only need log(n) steps when your window is n commits large.
bisect consists out of the following steps:
- You start git bisect
- You tell git which commits it should inspect. You do it in that way that you tell git
- What is a known good commit?
- What is a known bad commit?
- Go in the middle between good and bad and ask the user if the commit is good or bad
- If between good and bad are still unchecked commits go to 3.
- If there are no commits we found our bad commit
- If this commit is good then the last checked one was the first broken commit
- If this commit is broken the this one was the first broken commit
Here a more graphical way:
bisect in action
Let's have a look at the following history:
git log --oneline f135f6a (HEAD -> master) Refactored to defensiv input 525fb09 Added performance and of course no bugs 271310f Refactor to own function 06f2561 Multiply by 2 console application af61874 Init project ad2e654 Add gitigore file a4f0974 Initial empty commit
In this example the commit with the hash 525fb09 introduced the bug but we don't know yet. So we will check all commits. With check I mean for example that you run your application, click through until you encounter your bug or not ... very explorative.
Let's get started:
git bisect start- This will start the whole process. It does nothing visible.
git bisect good a4f0974- We know the initial commit has not the bug in question
git bisect bad f135f6a- We know the latest commit has the bug. But we are not sure where it was introduced.
3.1 Also git tells you the following:
Bisecting: 2 revisions left to test after this (roughly 2 steps)
3.2 If you check now via
git log --oneline you see something like that:
06f2561 (HEAD) Multiply by 2 console application af61874 Init project ad2e654 Add gitigore file a4f0974 (refs/bisect/good-a4f09747c40aca77fc459051ff5cd0d2c422947c) Initial empty commit
- We are in the middle of our commits. Now we have to tell git whether or not this commit is broken. In our case it is not, so:
git bisect good. We get the following output:
Bisecting: 0 revisions left to test after this (roughly 1 step) [525fb09c073a80bcd5dda3b6959520118da44214] Added performance and of course no bugs
- We checked the commit and it is a bad one, so:
git bisect badwith the output:
Bisecting: 0 revisions left to test after this (roughly 0 steps) [271310f061cff11a9190fe2fd7c1da19b74d8163] Refactor to own function
We narrowed down our to the last commit. If this commit is good, then we know it is the commit with the hash 525fb09, if not it is the current one (271310f).
- We checked and the commit with the has 271310f is good so:
git bisect goodand now git tells you all the information it has about the commit:
525fb09c073a80bcd5dda3b6959520118da44214 is the first bad commit commit 525fb09c073a80bcd5dda3b6959520118da44214 Author: Steven Giesel <firstname.lastname@example.org> Date: Thu Apr 16 11:04:34 2020 +0200 Added performance and of course no bugs bisect/Program.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
- At the end you should call
git bisect resetto go back from where you started. Otherwise you have a detached had at the last checked commit.
Here is a small list of limitations or what not to expect from git bisect
- git bisect is binary - it only operates on either good or bad commits. It can't tell you specific errors
- That said, if you have very very big commits it doesn't help that much as you still have to go through hunderds of files
- It works best with a linear tree. If you have a lot of merges it will make it a bit harder
- If you are not sure whether or not a commit is bad or good you can use:
git bisect skipto skip the current one. This is especially helpful when you can't build a commit
You can use a bash script or any kind of command line tool to automate the process.
git bisect run my_script args you can automate to whole thing. Your script should return 0 if the return is good and 1 to 127 (including), but not 125 if the commit is bad. 125 is used for skip.
git bisect is a nice tool to find a bad commit in your code base. Especially if it is a bit bigger and you don't know where to start.
If you want to know more checkout the documentation on git