git bisect 二分定位 bug 实战:从上千个 commit 中找出肇事者
一、为什么二分定位有效
线性检查 1000 个提交,最坏需要 1000 次构建。二分法每次选择中间版本,把范围缩小一半,复杂度是 (O(\log_2N))。1000 个提交只需要约 10 次判断。
前提是问题具有单调性:某个提交之前是 good,之后是 bad。若 bug 被引入后又修复,或测试结果不稳定,bisect 会给出误导结果。
二、基本流程
1 | |
Git 会切换到中间提交。测试后标记:
1 | |
完成后:
1 | |
reset 会回到开始 bisect 前的分支位置。过程中不要在 detached HEAD 状态下随意提交业务代码。
三、实战:pytest 自动定位
创建 scripts/check_bug.sh:
1 | |
赋予执行权限并运行:
1 | |
退出码有特殊含义:0 表示 good,1~127 表示 bad,125 表示当前提交无法判断、应跳过。依赖安装失败通常应返回 125,而不是把环境问题判成代码 bug。
四、编译失败和性能回归
编译检查脚本:
1 | |
性能脚本应使用固定输入,并避免把机器噪声当成回归:
1 | |
性能阈值要留出余量,最好重复多次取中位数,并在固定硬件、固定容器和固定 CPU governor 下比较。
五、高级技巧
git bisect log 可以保存过程,之后用 git bisect replay 重放。已知某些提交无法构建时使用:
1 | |
最后找到候选提交后:
1 | |
如果确认是独立错误,可创建修复提交;不要因为找到肇事者就直接回滚整个分支。git revert <commit> 会生成反向提交,适合公共分支。
六、踩坑记录
Merge commit 可能让搜索路径复杂。默认 bisect 会沿提交图查找,不一定符合“业务上线顺序”。可以选择一个明确的主线 good 点,并记录发布时间。
测试不稳定是最常见问题。先单独运行测试 20 次,确认失败概率,再进行 bisect。跨平台构建差异也会造成误判,应在与生产一致的容器中执行脚本。
七、小结
标准模板是:
1 | |
团队应维护稳定、快速、可重复的回归测试;只有这样,二分工具才能把十几分钟的故障定位压缩到几分钟。