某次合并后线上崩了,可疑提交几十上百个,一个个 checkout 太慢。
git bisect 做二分搜索:log N 次 checkout 就找出元凶。
基本流程
git bisect start
git bisect bad HEAD # 现在的状态是坏的
git bisect good v1.5.0 # 这个 tag 是好的
# Git checkout 中间提交,让你测:
# Bisecting: 38 revisions left to test after this (roughly 6 steps)
# [a1b2c3d] Some commit
你测一下(手动或脚本):
# 如果坏:
git bisect bad
# 如果好:
git bisect good
# 测不了(编译失败 / 不相关):
git bisect skip
Git 自动 checkout 下一个二分点。重复直到:
# a1b2c3d is the first bad commit
# commit a1b2c3d
# Author: ...
# Date: ...
# feat: refactor user permission check
完事:
git bisect reset # 回到原本的 HEAD
自动化(最好用的方式)
写一个返回 0 / 1 的检查脚本:
# check.sh
#!/usr/bin/env bash
set -e
make build > /dev/null 2>&1 || exit 125 # 编译失败 → skip
./run_test.sh > /dev/null 2>&1 # exit 0 = good, !=0 = bad
退出码约定:
0:good1~124、126~127:bad125:skip128+:abort bisect
然后:
git bisect start HEAD v1.5.0
git bisect run ./check.sh
Git 自动跑完所有二分步骤,结束时打印 first bad commit。
20 个提交需要约 5 次 checkout + 测试。
真实例子:找性能回归
线上某 endpoint 从 50ms 涨到 200ms:
# benchmark.sh
#!/usr/bin/env bash
make build
./serve &
PID=$!
sleep 2
LAT=$(curl -s -o /dev/null -w '%{time_total}' http://localhost:8000/api/x)
kill $PID
# 50ms 以下算好
awk "BEGIN { exit ($LAT < 0.10) ? 0 : 1 }"
git bisect start HEAD v1.5.0
git bisect run ./benchmark.sh
20 分钟后告诉你是哪个提交把性能拖坏的。
一次同时盯多个文件
bisect 时如果你只想看某子目录:
git bisect start --term-good=fast --term-bad=slow -- src/api/
-- 后面限定路径范围;只把改了 src/api/ 的提交纳入二分。
搜索空间小很多。
skip 多个提交
某段提交都 build 失败(比如有人合错了破坏分支),全 skip:
git bisect skip $(git rev-list bad-broken^..good-broken)
bisect 算法会跳过这段继续。
可视化 bisect 当前状态
git bisect log
# git bisect start
# # bad: [abcdef] description
# git bisect bad abcdef
# # good: [123456] description
# git bisect good 123456
# # skip: [xyzxyz] ...
# git bisect skip xyzxyz
把 log 输出存文件,回头能重放 bisect:
git bisect log > /tmp/bisect.txt
git bisect reset
# ... 时间过去 ...
git bisect replay /tmp/bisect.txt # 还原到之前的进度
与 git worktree 配合
bisect 期间不能在主目录干别的。开个 worktree 让 bisect 单独跑:
git worktree add ../bisect-tmp HEAD
cd ../bisect-tmp
git bisect start HEAD v1.5.0
git bisect run ./check.sh
# 完事
cd ../myapp
git worktree remove ../bisect-tmp
不止 commit —— bisect tag / branch
git bisect start v2.0 v1.0 # 在两个 tag 之间二分
git bisect start origin/main origin/main~100 # 最近 100 个 commit
几个手动 bisect 的小 trick
中途调整范围
git bisect bad <some-commit> # 把 "bad" 边界从 HEAD 改到更早
git bisect good <other> # 把 "good" 边界改到更晚
bisect 重新计算二分。
临时 fix forward 后继续
某中间 commit build 失败需要小补丁才能跑你的 check:
# 在二分到的 commit 上
git cherry-pick <fix-commit> # 临时打补丁
./check.sh
git bisect good/bad # 根据结果
git reset --hard HEAD~ # 撤销 cherry-pick
git bisect <next>
踩过的坑
- bisect 用的 check 脚本不稳定(偶尔 false bad / false good)→ 二分到
错误结论。check 脚本必须 deterministic。 git bisect bad漏写 commit 参数 → 自动用 HEAD(当前 bisect 选的)
当 bad。手抖容易标错。- bisect 跨越了 merge commit,结果指到一个 merge commit。看
git log --first-parent复审;真正 bug 在 merge 的某个 parent 分支里,
对那个分支再做二级 bisect。 - 整套流程用
git bisect run自动化才省力。手动 100 次每次手测,
错一次就要 reset 重来。
登录后参与评论。