版本控制

用 git bisect 二分定位引入 Bug 的提交

适用于“凭感觉找提交太慢、checkout 试错费时”的老手与新人都能用的二分排查法:设定 good/bad、跑校验脚本把判定自动化。

作者:巧匠团队·7 分钟阅读·更新于 2026-09-06

为什么别再用名册式手工回退来试

假设一个 Bug 藏在 200 个提交里的某个,顺序 checkout 逐个测试最坏要试 200 次。git bisect 用二分法,每次切到中间提交,让你标记好与坏,大约 log2(200)≈8 次就能收敛。

它能利用 Git 的智能提交选择,自动在你的“好/坏区间”里跳到最省步数的提交,你只需在每个中间提交回答“这次是坏还不坏”。

注意前提:区间内历史不能有随机的环境抖动(如每次都随机失败、依赖外部服务),否则“坏/不坏”标签会互相矛盾导致 bisect 跑偏。

git bisect start
git bisect bad              # 当前 HEAD 是坏的

git bisect good v1.2.0     # 已知的某个正常历史点

# Bisecting: 99 revisions left to test after this (roughly 7 steps)

把好/坏判断自动化:用脚本换掉手动点按

手动在每个中间提交上跑一次应用、看一次结果,20 次也可能不耐烦。更好的做法是把“这个提交是否坏”封装成一个幂等命令——退出码 0 代表好、非 0 代表坏——然后用 git bisect run 让它自动跑完全程。

典型的脚本:切到该提交时用干净的临时拷贝构建并运行一个最小复现用例(如 pytest、npm test -- 单一用例),返回其退出码。关键是要让测试在失败时返回非零退出码(如设置 set -e 或用框架自带的失败码)。

脚本要能处理“构建失败”与“测试失败”,两者都要判为坏——否则一次临时编译错误会被当成好提交,把 bisect 引到错误方向。

cat > bisect_test.sh <<"EOF"
#!/usr/bin/env bash
set -euo pipefail

# 在当前提交下干净地构建并跑最小复现用例
rm -rf /tmp/bw && git worktree add /tmp/bw HEAD
(cd /tmp/bw && npm ci && npm run build)
(cd /tmp/bw && npm run test -- --grep "balance overflow")
EOF
chmod +x bisect_test.sh

git bisect start
git bisect bad HEAD
git bisect good v1.2.0
git bisect run ./bisect_test.sh

处理跑偏的典型案例:好/坏标签矛盾

如果 bisect 突然报“skipped or contradicts”,多半是区间里有环境相关的不稳定,或者第一次选的“good”实际上并不是好状态。

排查套路:先用 git bisect log 导出当前过程;确认 good 和 bad 是否真的是良/劣——若某个历史上“还算能跑”的版本其实坏了,会干扰判断;实在不稳定就换成确定性更强的复现脚本(固定依赖、锁定端口、mock 外部 HTTP)。

有时合理做法是用 git bisect skip 跳过无法判定的提交,比如该提交本身无法编译且和 bug 无关。skip 让 bisect 走到能判定的提交,代价是步数略多但更可信。

# 查看当前进度并核对标的
git bisect log

# 对无法判定的提交跳过
git bisect skip

# 想给区间加另一个 good 点收紧
git bisect good v1.1.0

git bisect reset   # 结束时务必退出 bisect 状态

更好的工程习惯:让 bisect 每次都能命中

提交尽量小而语义清晰,这样定位出来的“元凶提交”里改动少、易读,一眼能看出哪一行导致的回归。一次提交塞二十个无关改动,即使 bisect 找到它,你还要再手工拆。

给每个提交维持可编译、可通过你主测试的状态,别把“半成品”也提交。bisect 的整个区间都应当尽量可构建,否则中间步骤常因构建失败而浪费判定。

为关键路径写可快速运行的最小复现测试,git bisect run 才能秒级反馈;否则每步跑全量集成测试,8 次定位会演变成一天的活。

git log --oneline HEAD~10   # 小块提交的历史长这样:
# 7ad3 Add cache header to image route
# 61f2 Fix round calculation in quote
# 98aa Rename billing module files
# ...

# 配合单一用例快速复现:
# package.json "scripts": { "test:bw": "vitest run tests/repro.spec.ts" }
git bisect run ./bisect_test.sh

官方参考来源

下方为命令对应的官方权威文档,供你核对最新用法与深入查阅。