Git 中 bisect 二分查找引入 bug 的提交

FreeGuideOnline 最新 2026-07-06

bash git bisect start


这会让 Git 切换到 bisect 模式。此时你的工作区处于待标记状态。

### 第二步:标记已知的坏提交和好提交

你需要告诉 Git 至少一个坏提交(bug 已存在的地方)和一个好提交(bug 尚未存在的地方)。通常 `HEAD` 就是坏提交,你可以使用一个较早的标签或提交哈希作为好提交。

```bash
# 标记当前版本为坏提交
git bisect bad

# 标记一个确认无 bug 的旧版本为好提交(例如 v1.0 标签)
git bisect good v1.0

如果你知道具体某个提交是坏的或好的,也可以直接传入引用:

git bisect bad HEAD
git bisect good a1b2c3d

执行 good 标记后,Git 会立刻检出一个位于好与坏中间位置的提交,并显示类似信息:

Bisecting: 12 revisions left to test after this (roughly 4 steps)
[commit-hash] 中间提交的消息

第三步:测试当前版本并标记

现在你的工作区位于 Git 自动选出的一个中间提交。你需要对它进行测试(编译、运行单元测试、手动验证等),然后根据结果告诉 Git 这次提交是好的还是坏的。

# 如果 bug 在这个提交中不存在
git bisect good

# 如果 bug 已经存在
git bisect bad

每次标记后,Git 都会重新计算剩余的步数并检出下一个中间提交。

第四步:重复测试,直到锁定目标

不断重复测试 → 标记的过程,Git 每次都会帮我们排除掉一半的嫌疑提交。当只剩下最后一个可疑提交时,它会自动输出结果:

<commit-hash> is the first bad commit
commit <commit-hash>
Author: ...
Date:   ...

    有问题的提交信息

第五步:结束 bisect 会话

无论是否有结果,完成调试后必须退出 bisect 模式,否则你的仓库会一直处于一个分离头指针(detached HEAD)状态。退出命令:

git bisect reset

这会将你的工作区和分支恢复到开始 bisect 之前的状态。

实战演示:找出导致测试失败的提交

假设你维护一个 Python 项目,最新提交 3f45d2a 上运行 pytest 出现了一个回归测试失败,但你知道在两周前的标签 v2.0 时测试是全部通过的。我们来用 bisect 定位:

# 开启会话
git bisect start

# 当前 HEAD 有 bug,标记为 bad
git bisect bad HEAD

# v2.0 是好的
git bisect good v2.0

# 现在 Git 检出一个中间提交,我们立即运行测试
pytest test_regression.py

# 如果测试通过 → good,失败 → bad
git bisect good   # 或 git bisect bad

# 继续自动检出新提交,重复测试,直到锁定
...
# 找到后
git bisect reset

整个过程通常在 10 步以内(能处理多达 2^10 = 1024 个提交),效率远超逐行审阅。

自动化 bisect:让脚本替你测试

如果测试可以完全自动执行(例如跑一个测试脚本),你可以用 git bisect run 让 Git 自动完成整个标记循环,无需人工干预。

git bisect start
git bisect bad          # 当前是坏的
git bisect good v2.0    # 好提交

# 使用 run 执行一个脚本或命令
git bisect run pytest test_regression.py

run 后面的命令必须返回正确的退出码:

  • 退出码 0:表示这个提交是好的(没有 bug),Git 会将此提交视为 good
  • 退出码 1 ~ 127(除了 125):表示提交是坏的,Git 会将其视为 bad
  • 退出码 125:表示无法判断(例如构建失败),Git 会跳过当前提交并要求人工介入。
  • 其他退出码(>127):Git 会中止 bisect 流程。

例如,你需要先确保项目可以编译,然后再运行测试:

git bisect run sh -c "make && ./run_tests"

如果 make 失败(退出码非零且不是 125),该提交会被标记为 bad;如果编译成功但测试失败,同样是 bad。只有当编译和测试都通过时才是 good。这样可以妥善处理那些在该提交上无法正常测试的情况。

高级技巧与注意事项

跳过无法测试的提交

有时某个中间提交可能因为编译中断、缺少依赖等原因根本无法测试。此时不要勉强标记 good 或 bad,而是使用:

git bisect skip

这会告诉 Git 跳过该提交,从剩余的范围中另外选一个近似的中点。如果某个区域的提交都被跳过,bisect 仍能尽量给出一个可能的范围,但可能需要你手动排查。

反向使用:查找修复提交

如果你想找哪个提交修复了一个 bug(即新版本 good,旧版本 bad),只需反转标记:

git bisect start
git bisect good   # 新版本是好的
git bisect bad    # 旧版本是坏的

逻辑和正常流程完全对称。

结合 git log 缩小范围

在标记 good/bad 之前,可以通过 git log 先在心理上划定一个嫌疑区间。例如你怀疑问题出在 main 分支自某个功能分支合并后的某个改动,可以将好提交指向合并前的 commit。

git bisect start
git bisect bad main
git bisect good feature-branch-merge-base

使用 bisect 日志和可视化

在 bisect 过程中,你可以随时查看当前的尝试记录:

git bisect log

你也可以将日志保存下来,以便在出错时重置后继续:

git bisect log > bisect_log.txt
# 重置后重新应用
git bisect replay bisect_log.txt