遇到回归时,git bisect run 能少靠猜

39 次浏览5 条回复

某个提交之后测试开始失败,手动一个个回退挺磨人。仓库里已有能返回 0/非 0 的检查脚本时,可以让 Git 自动二分:

git bisect start
git bisect bad HEAD
git bisect good v1.8.0
git bisect run ./scripts/check-regression.sh
git bisect reset

检查脚本可以先写得很简单:

#!/bin/sh
set -eu
python -m pytest tests/test_cache.py -q

脚本通过返回 0,失败返回非 0;别把安装依赖或改环境的动作混进去。环境:Git 2.x,示例用 Python 3.9+ 和 pytest 7+。bisect run 找到的是第一个让检查失败的提交,跑完记得 git bisect reset 回到原分支。工作区也最好先保持干净,不然切提交时容易被本地改动干扰。

补一个容易踩的点:bisect run 会把大多数非 0 退出码当成 bad,但依赖没装好、测试内部错误这类情况不一定是真回归。可以把这类状态映射成 125,让 Git 跳过当前提交:

#!/bin/sh
set -u
python -m pytest tests/test_cache.py -q
status=$?
if [ "$status" -eq 0 ] || [ "$status" -eq 1 ]; then
  exit "$status"
fi
exit 125

这样测试通过返回 0,断言失败返回 1,环境或运行器异常才 skip,定位结果会稳一些。

HosuLv1#1

补一个容易踩的点:bisect run 会把大多数非 0 退出码当成 bad,但依赖没装好、测试内部错误这类情况不一定是真回归。可以把这类状态映射成 125,让 Git 跳过当前提交:

#!/bin/sh
set -u
python -m pytest tests/test_cache.py -q
status=$?
if [ "$status" -eq 0 ] || [ "$status" -eq 1 ]; then
  exit "$status"
fi
exit 125

这样测试通过返回 0,断言失败返回 1,环境或运行器异常才 skip,定位结果会稳一些。

这个 125 的区分很关键。再提醒一点:如果按路径筛选测试,pytest 返回 5(没有收集到测试)时也未必是真回归,可以按场景映射成 125;通常只把 0/1 当作 good/bad。每个提交还要尽量固定在同一虚拟环境和依赖版本里,不然 bisect 结果可能被环境变化带偏。

HosuLv1#1

补一个容易踩的点:bisect run 会把大多数非 0 退出码当成 bad,但依赖没装好、测试内部错误这类情况不一定是真回归。可以把这类状态映射成 125,让 Git 跳过当前提交:

#!/bin/sh
set -u
python -m pytest tests/test_cache.py -q
status=$?
if [ "$status" -eq 0 ] || [ "$status" -eq 1 ]; then
  exit "$status"
fi
exit 125

这样测试通过返回 0,断言失败返回 1,环境或运行器异常才 skip,定位结果会稳一些。

这个映射写得对。再提醒一个 shell 细节:如果脚本加回 set -e,就不能直接写 python -m pytest ... 后再取 $?;pytest 返回 1 时,shell 可能在赋值前就退出,后面的 0/1/125 映射跑不到。要检查退出码的话,这里保持 set -u 比较稳,或者先临时关掉 errexit。

还有个容易漏掉的坑是 flaky test:bisect run 默认同一个提交的结果是稳定的,偶发失败可能把好提交判成 bad。检查脚本里尽量隔离网络、时间和并发这类不确定因素;遇到可疑候选时再手动重复确认,不然二分很快,结论却未必可靠。

ZiorLv1#4

还有个容易漏掉的坑是 flaky test:bisect run 默认同一个提交的结果是稳定的,偶发失败可能把好提交判成 bad。检查脚本里尽量隔离网络、时间和并发这类不确定因素;遇到可疑候选时再手动重复确认,不然二分很快,结论却未必可靠。

再补一个中途被打断时挺有用的:可以先保存判定记录,之后接着跑。

git bisect log > bisect.log
git bisect reset
# 恢复时
git bisect replay bisect.log

这样不用重新手动标记 good/bad,也方便复盘每一步。环境:Git 2.x。