几千次提交逐个查太慢:用git bisect约十步锁定坏改动

|作者: QUASA 编辑团队|2 分钟阅读
几千次提交逐个查太慢:用git bisect约十步锁定坏改动

用 git bisect 定位引入回归的提交,先确定一个检查正常的 good 提交和一个稳定出现目标故障的 bad 提交,再对 Git 选出的中间提交运行同一项检查:正常标 good,故障出现标 bad,直到范围收敛。Git 官方手册给出剩余 675 个版本约需 10 步的示例;若待查范围约有 2000 次提交,按逐轮减半估算,理想情况下约需 11 次判定。

这项检查决定搜索结果是否可信。它必须在两个端点给出相反且可重复的结果,并且每轮判断的都是同一个回归现象;一次偶发失败或旧版本缺少测试依赖,都不能直接当作代码变坏。

先验证 good 和 bad 的边界

把故障写成明确的检查:固定输入、运行条件和预期结果,区分目标回归与其他报错。例如,要找的是某项功能返回错误结果,就不要把任意构建失败也算作 bad。自动检查应在功能正常时退出 0,在目标故障出现时退出 1;无法得出结论的情况需要单独处理。

开始二分前,分别在已知正常版本和已知故障版本运行检查。GOOD_SHA 和 BAD_SHA 应填入这两个已验证提交的哈希,而不是根据提交说明猜测状态。如果故障依赖特定配置、测试数据或依赖版本,应让这些条件在整个搜索区间保持一致;否则提交变化与环境变化会混在一起。

先运行 git status --short,处理尚未保存的改动,并检查是否有上次构建留下的产物。若复现脚本是在故障出现后才写的,宜将它放在仓库外,并确认它能检查较早的源码。这样切换到旧提交时,脚本不会随仓库历史一起消失或变成另一个版本。

手动搜索:逐轮标记同一个现象

运行 git bisect start BAD_SHA GOOD_SHA,Git 会检出两端之间的候选提交。执行已经验证过的检查;正常就输入 git bisect good,出现目标故障就输入 git bisect bad。每次标记后,Git 会自动选择下一个待测提交。

Atlassian 的操作示例展示了反复标记 good 或 bad,直到 Git 报告 first bad commit 的过程;示例定位到的是一次合并冲突处理产生的提交。遇到类似结果时,应查看合并提交相对其父提交形成的改动,不能只凭提交说明判断故障原因。

  1. 在当前候选提交运行同一项检查,记录实际现象。
  2. 检查正常时输入 git bisect good;目标故障出现时输入 git bisect bad。
  3. 继续检查 Git 检出的下一提交,直到出现 first bad commit。
  4. 记下提交哈希,并对照相邻版本的行为和该提交的改动。

这里的“首个”由已选边界和检查条件共同界定:它是搜索范围内首次被这项检查判为坏的提交。若同一症状曾出现、消失又再次出现,应把端点收紧到要查的那次回归;否则单次 good/bad 划分可能无法表达真实的变化过程。

让 git bisect run 执行自动检查

检查能够稳定返回退出码时,可以让 git bisect run 在每个候选提交上重复执行。下面以 make 构建、~/repro.sh 检查目标故障为条件示例:先确认 ~/repro.sh 在 good 端点返回 0、在 bad 端点返回 1,再把以下内容保存为仓库外的 ~/bisect-check.sh。

#!/bin/sh

make || exit 125

~/repro.sh

随后运行 git bisect start BAD_SHA GOOD_SHA,再运行 git bisect run sh ~/bisect-check.sh。脚本先构建,成功后才执行复现检查;make 失败时返回 125,表示当前提交无法按这项检查分类。若要定位的回归本身就是构建失败,就不能在 make 失败时跳过,而应让构建结果直接决定 good 或 bad。

自动运行把退出码当作判定,而不会理解报错内容。0 表示 good,1 至 127 中除 125 以外的值表示 bad,125 表示跳过;其他退出码会中止搜索。因此,命令不存在、测试服务不可用或输入数据缺失时,脚本不能把普通失败码直接交给 Git,否则环境问题也可能被标为坏提交。

不可测试的提交与波动结果

手动检查遇到与目标回归无关、且无法构建的提交时,输入 git bisect skip;自动脚本返回 125 起同样作用。Git 会尝试其他候选提交,但如果被跳过的版本紧邻故障边界,结果可能是一组可疑提交,而非唯一的首个坏提交。此时需要修复该版本的测试条件,或针对边界附近的提交另做检查。

同一提交上测试时而通过、时而失败,应暂停标记。Martin Fowler 对非确定性测试的分析列举了测试隔离不足、异步行为、远程服务和时间依赖等成因。可在同一提交上重复运行,并固定随机种子、输入和外部依赖,查清结果变化来自哪里。

重复运行能暴露波动,却不能简单地把多数结果当作可靠标签。二分搜索会依据每次 good 或 bad 的判断排除一部分提交;错误标记可能把真正的边界排除在后续搜索之外。若检查暂时无法稳定区分目标故障,先修正检查条件,再继续二分。

纠正误标并结束搜索

发现标记有误时,运行 git bisect log 查看已输入的判断。需要保留其他判断时,可运行 git bisect log > /tmp/bisect.log,编辑该文件删除错误记录,然后依次运行 git bisect reset 和 git bisect replay /tmp/bisect.log。若搜索刚开始,从两个已验证端点重新启动通常更容易核对。

记下定位结果后,运行 git bisect reset 结束会话,Git 会返回开始二分前检出的提交。若结果仍包含因 skip 而无法排除的候选版本,应保留这一范围;只有检查能区分边界两侧时,才能把其中某个提交确定为首个坏提交。

相关阅读:

分享:

订阅我们的新闻通讯

将最新 Web3、AI 和加密货币新闻直接发送到您的邮箱。

0