git bisect is your time machine for finding regressions, but it’s not about finding when a bug appeared; it’s about finding the commit that introduced it.
Let’s say you have a feature that worked perfectly in commit a1b2c3d but is now broken in your current HEAD. You want to pinpoint the single commit that caused this breakage.
# Start the bisecting process
git bisect start
# Tell Git the current state is bad
git bisect bad HEAD
# Tell Git a known good state
git bisect good a1b2c3d
Git will now check out a commit roughly halfway between your good and bad points. Your job is to test this commit.
# Example: Compile your code. If it works:
git bisect good
# Example: Run your test suite. If it fails:
git bisect bad
Git will repeat this process, narrowing down the range of potential commits by half each time. It’s a binary search on your commit history.
Eventually, git bisect will isolate the single commit that introduced the bug. It will print the commit hash and its message.
<commit-hash> is the first bad commit
<commit-message>
To clean up and return to your original HEAD and state:
git bisect reset
Common Pitfalls and Advanced Usage:
- Non-linear History (Merges): If your history isn’t a straight line,
git bisectmight check out a merge commit. If that merge commit itself isn’t directly testable (e.g., it doesn’t compile on its own), you can mark it asskip.git bisect skiptells Git to ignore this commit and try a different one. - Automating the Test: If you have a script that can automatically determine if a commit is "good" or "bad" (e.g., a test suite that exits with 0 for good, non-zero for bad), you can automate the entire process.
This is incredibly powerful for large histories or complex build/test steps.git bisect run <your-test-script.sh> - Skipping Commits with
git bisect skip: Sometimes, a commit might be unbuildable due to unrelated issues (e.g., a temporary dependency failure). You can tellgit bisectto ignore that commit and try another one.
Git will then pick another commit from the remaining range.# After git bisect checks out a commit, if it's broken for reasons # unrelated to the bug you're hunting, or simply doesn't build: git bisect skip - Bisecting a Specific File/Feature: If you know the bug only affects a specific file or a particular feature, you can narrow down the bisect range to commits that touched that file or feature.
This tellsgit bisect start -- <file_path> # or git bisect start -- path/to/your/file.jsgit bisectto only consider commits that have modified the specified file(s). - Bisecting by Tags: You can also use tags instead of commit hashes.
git bisect start git bisect bad HEAD git bisect good v1.2.0 - Bisecting with a Different Metric:
git bisectisn’t just for functional bugs. You can use it to find when performance regressions were introduced, or when a certain warning started appearing. The key is having a consistent way to measure "good" vs. "bad." For instance, if a test now takes 10 seconds longer:
This allows you to hunt for performance regressions as effectively as functional ones.# In your bisect script: start_time=$(date +%s) # Run your test end_time=$(date +%s) duration=$((end_time - start_time)) if [ "$duration" -gt 20 ]; then # If test duration exceeds 20 seconds git bisect bad else git bisect good fi - Viewing the Bisect Log: If you get lost or want to see what commits
git bisecthas already considered, you can use:
This shows the history of your bisecting decisions.git bisect log
The core idea is that git bisect leverages the fact that your commit history is a directed acyclic graph (DAG). By repeatedly asking Git to check out a commit in the middle of the remaining range and then telling it whether that commit is "good" or "bad," you’re effectively performing a binary search on the commit history, dramatically reducing the number of commits you need to manually inspect.
Once you’ve found the offending commit, the next step is usually to revert it or fix it and then re-apply the fix to your current branch, potentially using git revert <offending-commit-hash> or cherry-picking the fix from a new branch.