Both intro and advanced content on how to use git bisect
.
Some links:
git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>] [--no-checkout] [<bad> [<good>...]] [--] [<paths>...] git bisect (bad|new|<term-new>) [<rev>] git bisect (good|old|<term-old>) [<rev>...] git bisect terms [--term-good | --term-bad] git bisect skip [(<rev>|<range>)...] git bisect reset [<commit>] git bisect (visualize|view) git bisect replay <logfile> git bisect log git bisect run <cmd>... git bisect help
Any of the following (bad, followed by good):
$ git bisect start v4.20 v4.19
or
$ git bisect start $ git bisect good v4.19 $ git bisect bad v4.20
after which you should see:
Bisecting: 7499 revisions left to test after this (roughly 13 steps) [ec9c166434595382be3babf266febf876327774d] Merge tag 'mips_fixes_4.20_1' of git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux
At any time, examine the current commit with:
$ git show commit ec9c166434595382be3babf266febf876327774d (HEAD) Merge: 685f7e4f1614 c61c7def1fa0 Author: Linus Torvalds <torvalds@linux-foundation.org> Date: Fri Oct 26 14:39:22 2018 -0700 Merge tag 'mips_fixes_4.20_1' of git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux Pull MIPS fixes from Paul Burton: "A couple of MIPS fixes that should have ideally made it for v4.19, but hey-ho here they are now: - A fix for potential poor stack placement introduced in v4.19-rc8. - A fix for a warning introduced in use of TURBOchannel devices by DMA changes in v4.16" * tag 'mips_fixes_4.20_1' of git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux: MIPS: VDSO: Reduce VDSO_RANDOMIZE_SIZE to 64MB for 64bit TC: Set DMA masks for devices
After testing the current checkout for the error, do one of:
$ git bisect good $ git bisect bad
to refine the interval containing the error.
After binary search, you will end up with something like:
$ git bisect good 130d6f946f6f2a972ee3ec8540b7243ab99abe97 is the first bad commit commit 130d6f946f6f2a972ee3ec8540b7243ab99abe97 Author: Thomas Gleixner <tglx@linutronix.de> Date: Sun Nov 25 19:33:40 2018 +0100 x86/l1tf: Show actual SMT state ... snip ...
Once you're done:
$ git bisect reset
Description:
git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>] [--no-checkout] [<bad> [<good>...]] [--] [<paths>...] git bisect (bad|new|<term-new>) [<rev>] git bisect (good|old|<term-old>) [<rev>...] git bisect terms [--term-good | --term-bad] ... snip ...
Pre-defined (you can't mix them):
good
/bad
old
/new
To see the current terms:
$ git bisect terms
Or just make up your own (to see where performance suddenly tanked):
$ git bisect start --term-old fast --term-new slow
$ git bisect good/bad # previous round was good or bad. Bisecting: 337 revisions left to test after this (roughly 9 steps) $ git show # oops, that is uninteresting. $ git reset --hard HEAD~3 # try 3 revisions before what # was suggested
Skip the current commit:
$ git bisect skip
Skip entire ranges:
$ git bisect skip v2.5..v2.6 # (v2.5,v2.6] $ git bisect skip v2.5 v2.5..v2.6 # [v2.5,v2.6]
If you're having trouble with a USB-serial converter:
$ git bisect start -- drivers/usb/ drivers/serial/
$ git bisect start v2.6.20-rc6 v2.6.20-rc4 v2.6.20-rc1 -- # v2.6.20-rc6 is bad # v2.6.20-rc4 and v2.6.20-rc1 are good $
If you have a command or program (say, ~/bin/check_test_case.sh
) that returns 0 (success) or 1 (failure) based on testing the checked out repo, use that to automatically bisect:
$ git bisect run ~/bin/check_test_case.sh
For example, you can find the first unbuildable commit with:
$ git bisect run make
since make
returns 0 on success and 2 on failure.
Any exit code from 1 to 127 represents failure, except for the special exit code of 125, which should be used when the current source code cannot be tested. If the script exits with this code, the current revision will be skipped (see git bisect skip
).
$ cat ~/bin/test.sh #!/bin/sh make || exit 125 # this skips broken builds ~/check_test_case.sh # does the test case pass? $
How to use it:
$ git bisect start <bad> <good> $ git bisect run ~/test.sh $ git bisect reset
Do not checkout the new working tree at each iteration of the bisection process. Instead just update a special reference named BISECT_HEAD
to make it point to the commit that should be tested.
This option may be useful when the test you would perform in each step does not require a checked out tree.
NOTE: This explanation is based on the writeup here.
If we have two commits directly on the master
branch, and we try to bisect using those two commits as good and bad endpoints, it's entirely possible that we could end up processing many, many, many commits based on the number of (potentially lengthy) feature branches that were merged into master
between the two commits.
Rather than bisecting completely on those lengthy feature branches, we can restrict the search to just those commits where feature branches were merged into master
, so the ultimate goal is to say, “That merged feature branch is the one that caused the problem,” possibly inspiring another round of more focused bisection. (This also considers the possibility of a stand-alone commit directly on the master
branch as well.) Let's demonstrate this using the Linux kernel source repository, defining v4.18
as the good commit, and v4.19
as the bad commit.
First, let's count the number of commits we would normally need to process if we used basic bisection based on those two endpoints:
$ git rev-list v4.18..v4.19 | wc -l 15204 $
That's a lot of commits so let's see if we can reduce that number. The basis for ignoring all of the commits on feature branches is to generate a list of commits to ignore, and feed that list to git bisect skip
to cut down on the work. The first step is to generate a list of merge commits between the two endpoints
$ git rev-list v4.18..v4.19 --merges | wc -l 1161 $
So there are 1161 merge commits in the testable range, but we need to further disqualify merge commits exclusively on feature branches, so let's restrict ourselves to just the first parents of those merge commits, which guarantees that all of those merge commits are on the master
branch:
$ git rev-list v4.18..v4.19 --merges --first-parent | wc -l 426 $
What the above set of merge commits represents are those merge commits for which we want to totally ignore all commits on the feature branch that contributed to each of those commits. For each merge commit above (call it rev
), we list all commits reachable from the second parent (the one on the feature branch, rev^2
), but not reachable from the parent on the master branch (rev^
), and pass all such commits to git bisect skip
.
for rev in $(git rev-list v4.18..v4.19 --merges --first-parent); do git rev-list $rev^2 --not $rev^ done | xargs git bisect skip
Given that our basic search would have processed 15204 commits, we can count how many commits will now be pruned from the bisection:
for rev in $(git rev-list v4.18..v4.19 --merges --first-parent); do git rev-list $rev^2 --not $rev^ done | sort | uniq | wc -l 14714 $