Files
git-workshop/02_advanced/04-bisect/README.md
2026-01-07 23:46:32 +01:00

6.1 KiB

Module 14: Bisect - Finding Bugs with Binary Search

Learning Objectives

By the end of this module, you will:

  • Understand what git bisect is and when to use it
  • Use binary search to find the commit that introduced a bug
  • Mark commits as good or bad during bisection
  • Automate bisect with test scripts
  • Understand the efficiency of binary search for debugging

Challenge Description

A bug has appeared in your calculator application, but you don't know which commit introduced it. The project has many commits, and manually checking each one would take too long. You'll use git bisect to efficiently find the culprit commit using binary search.

Your task is to:

  1. Start a bisect session
  2. Mark the current commit as bad (bug exists)
  3. Mark an old commit as good (bug didn't exist)
  4. Test commits and mark them good or bad
  5. Let Git find the first bad commit
  6. Identify what change introduced the bug

Key Concepts

What is Git Bisect?

Git bisect uses binary search to find the commit that introduced a bug. Instead of checking every commit linearly, it cuts the search space in half with each test, making it extremely efficient.

Binary Search Efficiency

Linear Search (manual checking):

  • 100 commits = up to 100 tests
  • 1000 commits = up to 1000 tests

Binary Search (bisect):

  • 100 commits = ~7 tests
  • 1000 commits = ~10 tests

Formula: log₂(n) tests needed for n commits

How Bisect Works

Commits:  A---B---C---D---E---F---G---H
          ✓   ✓   ✓   ?   ?   ?   ?   ✗

1. Start: Mark H (bad) and A (good)
2. Git checks middle: E
3. You test E: bad ✗

Commits:  A---B---C---D---E
          ✓   ✓   ✓   ?   ✗

4. Git checks middle: C
5. You test C: good ✓

Commits:  C---D---E
          ✓   ?   ✗

6. Git checks: D
7. You test D: bad ✗

Result: D is the first bad commit!

When to Use Bisect

Use bisect when:

  • You know a bug exists now but didn't exist in the past
  • You have many commits to check
  • You can reliably test for the bug
  • You want to find exactly when something broke

Useful Commands

# Start bisect session
git bisect start

# Mark current commit as bad
git bisect bad

# Mark a commit as good
git bisect good <commit-hash>
git bisect good HEAD~10

# After testing current commit
git bisect good  # This commit is fine
git bisect bad   # This commit has the bug

# Skip a commit (if you can't test it)
git bisect skip

# End bisect session and return to original state
git bisect reset

# Visualize bisect process
git bisect visualize
git bisect view

# Automate with a test script
git bisect run <test-script>
git bisect run npm test
git bisect run ./test.sh

# Show bisect log
git bisect log

Verification

Run the verification script to check your solution:

.\verify.ps1

The verification will check that:

  • You completed a bisect session
  • You identified the correct commit that introduced the bug
  • You understand which change caused the problem

Challenge Steps

  1. Navigate to the challenge directory
  2. View the bug: run the calculator and see it fails
  3. Start bisect: git bisect start
  4. Mark current as bad: git bisect bad
  5. Mark old commit as good: git bisect good HEAD~10
  6. Git will checkout a middle commit
  7. Test the current commit (run the test or check manually)
  8. Mark it: git bisect good or git bisect bad
  9. Repeat testing until Git identifies the bad commit
  10. Note the commit hash and message
  11. End bisect: git bisect reset
  12. Check the identified commit: git show <bad-commit-hash>
  13. Create a file named bug-commit.txt with the bad commit hash
  14. Run verification

Tips

  • Always start with a known good commit (far enough back)
  • Keep a clear way to test each commit (script or manual steps)
  • Use git bisect log to see your progress
  • git bisect reset returns you to your original state
  • You can bisect on any criteria, not just bugs (performance, features, etc.)
  • Automate with git bisect run for faster results
  • Each bisect step cuts remaining commits in half
  • Skip commits you can't build/test with git bisect skip

Manual vs Automated Bisect

Manual Bisect

git bisect start
git bisect bad
git bisect good HEAD~20

# For each commit Git checks out:
npm test
git bisect good  # or bad

git bisect reset

Automated Bisect

git bisect start
git bisect bad
git bisect good HEAD~20
git bisect run npm test
# Git automatically tests each commit
git bisect reset

The test script should exit with:

  • 0 for good (test passes)
  • 1-127 (except 125) for bad (test fails)
  • 125 for skip (can't test this commit)

Bisect Workflow Example

Finding a Performance Regression

# App is slow now, was fast 50 commits ago
git bisect start
git bisect bad
git bisect good HEAD~50

# Create test script
echo '#!/bin/bash\ntime npm start | grep "Started in"' > test.sh
chmod +x test.sh

git bisect run ./test.sh
# Git finds the commit that made it slow

Finding When a Feature Broke

git bisect start
git bisect bad
git bisect good v1.0.0  # Last known good version

# For each commit
npm test -- user-login.test.js
git bisect good  # or bad

# Found! Commit abc123 broke login
git show abc123

Common Bisect Pitfalls

Pitfall 1: Testing Incorrectly

  • Make sure your test is consistent
  • Automate when possible to avoid human error
  • Use the same test for every commit

Pitfall 2: Wrong Good Commit

  • If the "good" commit actually has the bug, bisect will fail
  • Choose a commit you're confident was working

Pitfall 3: Multiple Bugs

  • Bisect finds one commit at a time
  • If multiple bugs exist, they might confuse the search
  • Fix found bugs and bisect again for others

What You'll Learn

Git bisect is a powerful debugging tool that turns a tedious manual search into an efficient automated process. By leveraging binary search, you can quickly pinpoint problematic commits even in repositories with thousands of commits. This is invaluable for debugging regressions, performance issues, or any situation where something that worked before is now broken. Mastering bisect makes you a more effective debugger and shows deep Git proficiency.