git bisect:二分定位引入 bug 的提交(含自动化)

某次合并后线上崩了,可疑提交几十上百个,一个个 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:good
  • 1 ~ 124126 ~ 127:bad
  • 125:skip
  • 128+: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 重来。
精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。