Git Course 0%

Bisect and blame

Expert ≈ 9 min

Before this lesson

What you will learn

  • How bisect finds a bad commit in a handful of steps
  • How to automate it with a test script
  • Three log searches that answer "when did this change?

After this lesson you can

  • I can find when something broke without reading every commit

Why this matters

"It worked last month" is a common report and a bad starting point. Bisect turns it into a specific commit in about ten checks, even across a thousand commits, and the searches after it answer the same question for a line of text.

Bisect

It is a binary search over history. You tell Git one commit where the problem exists and one where it does not; Git checks out the midpoint and asks; each answer halves the range.

Terminal
$ git bisect start <bad> <good>

Then, at each step, git bisect good or git bisect bad. Ten steps cover a thousand commits.

Automating it

If the check can be a script that exits zero for good and non-zero for bad, Git does the whole search:

Terminal
$ git bisect start HEAD HEAD~6
$ git bisect run ./check.sh
1e90067a1457fd9969319c1a97c12bedde7f7ea7 is the first bad commit
Terminal
$ git bisect reset

The script can be anything: a test suite, a build, a grep for a string that should not be there. For a documentation problem it might check that a link resolves or that a page builds.

Two rules make this work:

  • The script must not depend on uncommitted state, since Git checks out old commits underneath it.
  • git bisect reset at the end, which returns you to the branch you started from. Forgetting it leaves you detached (lesson 12.5).

git bisect skip handles a commit that cannot be tested, for example one that does not build.

Blame

Terminal
$ git blame -L 1,4 docs/faq.md
b81702c5 (You 2026-09-02 09:15:00 +0200 1) # Frequently asked questions
b81702c5 (You 2026-09-02 09:15:00 +0200 2) 
b81702c5 (You 2026-09-02 09:15:00 +0200 3) ## Is Trailguide free?
b81702c5 (You 2026-09-02 09:15:00 +0200 4) 

-L 1,4 limits it to a range of lines, which is what makes blame usable on a long file. Two options matter beyond that:

Option Does
-w Ignore whitespace changes, so a reformatting commit stops hiding the real author
-C Detect lines moved or copied from another file
--since=1.year Stop looking further back

Blame's weakness is that it reports the last commit to touch a line, which is often a reformatting or a rename. -w and -C are how you see past that, and the platform's blame views have an "ignore this commit" control for the same reason (lesson 10.4).

Two searches that beat blame

When did this string appear or disappear?

Terminal
$ git log --oneline -S 'BROKEN' -- data/trails.csv
1e90067 data: add row 4

-S finds commits that changed the number of occurrences of a string: exactly the question "who added this?". -G takes a regular expression and finds commits whose diff matches it.

How did these lines get like this?

Terminal
$ git log -L 1,3:docs/faq.md

That prints the history of those specific lines, with each diff that touched them, following the range as it moves. It is slower than blame and answers a better question, because it shows the sequence rather than only the last step.

Choosing

Question Tool
When did this behaviour break? git bisect
Who last changed this line, and why? git blame -L
Which commit added this string? git log -S
How did these lines evolve? git log -L
What changed in this file over time? git log -p -- <file>

How to do it

Terminal
$ git bisect start <bad> <good>
$ git bisect run ./check.sh
$ git bisect reset
$ git blame -w -C -L 20,40 <file>
$ git log -S '<string>' -- <path>
$ git log -L 20,40:<file>

Common mistakes

  • Forgetting git bisect reset, leaving the repository detached.
  • A bisect script that depends on uncommitted files.
  • Trusting blame's last-toucher when a reformatting commit sits on top. Use -w and -C.
  • Blaming a whole file rather than a line range, and drowning.
  • Using blame to answer "who added this string?" when log -S answers it directly.

Try it yourself

Goal: find a deliberately broken commit automatically.

  1. In a practice repository, make six commits, one of which adds the line BROKEN to a file.
  2. Write a script that exits 1 if the file contains BROKEN and 0 otherwise, and make it executable.
  3. Run git bisect start HEAD HEAD~6 then git bisect run ./check.sh.
  4. Read the "is the first bad commit" line and confirm it is the one you expect.
  5. Run git bisect reset, then find the same commit with git log -S 'BROKEN'.

Expected result: the same commit found two ways, one by binary search over behaviour and one by searching the content of the diffs.

Show solution

Step 5 is the comparison. log -S is faster when you know the exact string that appeared; bisect is the tool when you only know that behaviour changed. Most real investigations start with the second and end with the first.

Check yourself

1. What does git bisect run ./check.sh do?
2. Blame says a reformatting commit last touched every line. What helps?
3. Which command answers "which commit added this string?"

Key terms

Commit Log Hash (SHA) Bug