This is an enhanced note taken from the Pluralsight course "Git Debugging Techniques" by Aaron Stewart.
Aside from the usual git commit
, git add
or git merge
commands that we are accustomed to, there are actually quite a number of useful Git commands out there that can help us to debug our codebase effectively. In this article, we will be exploring 3 useful Git commands, namely Git Bisect, Git Blame and Git Grep.
Git Bisect
A technique to narrow down the potentially bug-inducing commit from the commits history with the help of binary search. This process can be automated with just 1 or 2 lines of Git commands.
Step 1: Initiate a debugging session
To initiate a Git Bisect session, type the following Git command into the terminal
git bisect start
Step 2: Specify the Good and Bad commits
After that, find any of the known commit where the behaviour/bug is not introduced and any of the commit where the issue was identified. Take note of their hash (a shortened version is fine) as we will be using it to specify the good and bad commits respectively.
git bisect good <good-commit>
git bisect bad <bad-commit>
Step 3: Inspect the codebase, repeat
After setting the good and bad commits, we can start to debug with Git Bisect by using the following commands.
For each iteration, inspect the codebase for any signs before the bug was introduced. If the bug still exists in the iteration, type git bisect bad
to mark this commit as bad and git bisect good
otherwise. At the end of the iteration, the issue-inducing commit would be found.
After completing the debugging session, use git bisect reset
to checkout to the latest commit.
# debugging
git bisect bad
git bisect good
# exit
git bisect reset
Automated Git Bisect
We can automate Git Bisect using the git bisect run
command with a custom script or command that inspects the codebase and returns 0
for success and non-zero for failure.
To do that, we can specify the bad and good commits altogether in the git bisect start
command.
git bisect start <bad-commit> <good-commit>
Next, specify the custom script or command to be executed with the Run command.
git bisect run <custom script or commands>
This will automate the Git Bisect process and bring us to the troublesome commit directly if it is able to find one through the execution of the script/command.
For example, let's say I am trying to find a file called index.html
that no longer exists in the latest commit, all I need to do is get the latest commit hash as the bad commit, and randomly get the hash of a commit way back in the history that I am sure that the file exist as the good commit and uses the ls
command in Linux (dir
in Windows) as the command to run the automated Git Bisect.
git bisect run ls index.html
Here is the sample output of the automated Git Bisect above.
running dir index.html
dir: cannot access 'index.html': No such file or directory
Bisecting: 9 revisions left to test after this (roughly 3 steps)
[d054131fd6b00fbd5ea8ed8ffa64e3a8f6fb6182] Create dependabot.yml (#17)
running dir index.html
dir: cannot access 'index.html': No such file or directory
Bisecting: 4 revisions left to test after this (roughly 2 steps)
[da5816fa25c8f19134aaa0406d433ed699851dbe] Remove jshint dev dependency
running dir index.html
dir: cannot access 'index.html': No such file or directory
Bisecting: 1 revision left to test after this (roughly 1 step)
[d211368917874dc54729708ed99cdb73a32b9ba1] add config Gemfile and package
running dir index.html
index.html
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[2c672974501e69612fd1d0c97a5142f02d6af8a0] rename index.html to inde.html
running dir index.html
dir: cannot access 'index.html': No such file or directory
2c672974501e69612fd1d0c97a5142f02d6af8a0 is the first bad commit
commit 2c672974501e69612fd1d0c97a5142f02d6af8a0
Author: Cynthia Rich <crichID@users.noreply.github.com>
Date: Sat Jul 8 10:53:06 2017 -0400
rename index.html to inde.html
inde.html | 458 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
index.html | 458 -------------------------------------------------------------
2 files changed, 458 insertions(+), 458 deletions(-)
create mode 100644 inde.html
delete mode 100644 index.html
bisect run success
Git Blame
Git Blame is used to investigate the codes within a file line-by-line. It will display the commit hash, author and commit timestamp for each line. If you are using VSCode, you can install the GitLens extension to display Git Blame information on the file within the text editor.
To blame a file, simply provide the file name as the argument.
git blame <filename>
Blame Part of a File
More often than not, the code that we need to inspect might consist of hundreds if not thousands of lines. Hence, if we only need to know the info for only certain parts of the file, we can provide a range of line numbers with the -L
flag.
git blame -L <line-start>,<line-end> <filename>
Example:
git blame -L 67,83 index.html
Git Show
Additionally, we can use git show
to further inspect the info for a commit that we are interested in after being identified with git blame
. It will display a code diff within the command line.
git show <commit>
@@ -75,7 +75,7 @@
ctx = canvas.getContext('2d'),
ucanvas = get('upcoming'),
uctx = ucanvas.getContext('2d'),
- speed = { start: 0.6, decrement: 0.005, min: 0.1 }, // how long before piece drops by 1 row (seconds)
+ speed = { start: 0.5, decrement: 0.005, min: 0.1 }, // how long before piece drops by 1 row (seconds)
nx = 10, // width of tetris court (in blocks)
ny = 20, // height of tetris court (in blocks)
nu = 5; // width/height of upcoming preview (in blocks)
Git Grep
Git Grep is similar to Grep as they are used to search text with string or Regex. The difference is that Git Grep only searches the files that are tracked by Git and is able to operate on different branches within the repository.
git grep <pattern/text>
To search for specific file types only, provide the -- <glob-pattern>
flag as follows.
git grep <pattern> -- <glob-pattern>
The following example searches for the keyword return
in the tracked js
(JavaScript) files only.
git grep "return" -- "*.js"
To display the additional line numbers for matching pattern/text, provide the -n
flag.
git grep -n <pattern>
Search with Regex is obviously supported too.
git grep "[0-9]pattern"
To search for a specific branch, provide the branch name as the second parameter.
git grep <pattern> <branch>
We can chain everything into one command as shown below. It will show lines that has the word "speed" at the "origin/feat" branch in the index.html file only and display the line numbers along the way.
git grep -n "speed" origin/feat -- index.html