How to debug in Git by finding the culprit commit

In this article we look at how to search through one’s commit history to find a “culprit commit” where a bug has been introduced in the code. We are looking for a bug that has been introduced in an unknown commit somewhere in the recent Git history.

Summary

A summary of this article is that we can find the culprit commit using a few simple steps:

  1. Use “git checkout [commit hash]” to find a commit in the history where the error is not occurring.
  2. Use a binary search method to check commits from the latest commit to the found commit where the error is not occurring. The method is to first check a commit halfway between the latest commit and the found commit without the error. Then the next step is to check the commit halfway between those two commits, and so on until one has narrowed down the culprit commit.
  3. Analyze the changes in the culprit commit to find where the bug is taking place. This can be done using another type of binary search method, where all of the code changes are removed. The changes are then added back 1/2 of the total number of remaining lines at a time (e.g. for a total of 100 lines changed, first add back 50 lines, then 25, then 13, then 6, etc.).

The full process – Finding the culprit commit

I have had an experience where I found an error on one of the pages I was working on. I was trying to figure out how to find what recent change I had made in the code that caused the error. It was not the last commit that I made. What I did was to look back many commits, as far back as I needed to see where the error was (10-20 commits, equivalent to changes that were made the day before or several days ago). I used the command “git checkout [commit hash / commit ID]” to view the code at a certain point in the repository where one of the previous commits were made. For example, my command could have been:

git checkout 82fb80c

This would bring me to the commit in a Git state called the “detached HEAD.” Being in a detached HEAD means that there is no Git branch that points to the current Git state. Usually, a Git HEAD points to the current branch, but not in a detached HEAD (Patru). See the image below for a visual of what it looks like being in a detached HEAD in Git:

Git Detached Head: What Is It & How to Recover
Visual of a detached HEAD in Git (credit: Cloudbees)

When using the “git checkout” command to go to a specific commit, one will receive an output message (“You are in ‘detached HEAD’ state”) like the one below:

Screenshot of “detached HEAD” message in Mac Terminal

By navigating to different commits in this way, it’s possible to run the code at different points in the commit history. This will allow one to find the commit where the error originates. In my case, the actual error was manifesting as a visual layout issue on the front end of a page that I had developed.

It was possible to keep checking out commits back to the point where the error disappeared. However I did not do this one commit at a time. I did a sort of binary search method. This meant that I looked 20 commits back from the latest commit, and saw if the error was still present. It was not. Then, I looked 10 commits back from the latest commit. 10 commits back, the error was still present. Then I looked 15 commits back. The error was gone. Using this method, I was able to quickly find the commit where the error was occurring.

See the image below, which shows a visual of the list of commits in my recent history. I used the command “git log --oneline” to get a concise list of the most recent commits. I took a screenshot of the terminal with the Git log, and used an image editor to draw red and green lines next to the commits that I had checked. The commits were marked as either containing the bug (red) or not containing the bug (green).

Screenshot of terminal showing log of commits (can be found using command “git log --oneline“). I drew red, green, and yellow lines next to the commits to denote commits that contained the target bug, did not contain the bug, and that were not yet checked, respectively

Analyzing the code after the culprit commit was found

As far as the analysis of the code of the culprit commit, one easy way to find the bug is to eliminate all of the code changes, and then add them back one at a time. This could be done also in a sort of binary search way. For example, let’s say that the single commit contains changes to one large block of code in one file. The developer can remove all of the changes to that block of code, to first confirm that this commit’s changes are causing the bug. Then, the developer can add back approximately one-half of the lines of code (in a fashion that makes logical sense), to check if the bug resurfaces. One can keep adding back chunks of code this way until the line causing the error has been localized.

Using this method, I was able to find the specific line containing the bug in the code. In my case, the layout issue was resulting from a missing closing </div> tag towards the bottom of my page.

Summary

In summary, we can find the culprit commit using a few simple steps:

  1. Use “git checkout [commit hash]” to find a commit in the history where the error is not occurring.
  2. Use a binary search method to check commits from the latest commit to a far back commit where the error is not occurring. First check a commit halfway between the latest and the found commit without the error. Then check the commit halfway between those two, and so on until one has narrowed down the culprit commit.
  3. Analyze the changes in the culprit commit to find where the bug is taking place. This can be done using a type of binary search method, where all of the changes are removed, and then added back. The changes can be adde back one-half of the total number of remaining lines at a time. For example, for a total of 100 lines changed, first add back 50 lines, then 25, then 13, then 6, etc.

Thanks for reading, and good luck. ????

Works Cited

Patru, Georgiana. “Git Detached Head: What This Means and How to Recover.” CloudBees. 15 Jul. 2020. https://www.cloudbees.com/blog/git-detached-head . Accessed 24 Dec. 2023.

Subscribe to new blog posts