CLI AI

git bisect:找出弄坏代码的那个提交

2026-07-20

git bisect start && git bisect bad && git bisect good <旧哈希> 会在历史里开始二分查找,而 git bisect run ./check.sh 能不靠一次手动回答就把它跑完。判定看脚本的退出码:0 表示好,非 0 表示坏,125 表示"这个提交没法判断,跳过"。

1. 划定边界

clai
$ clai 开始查找弄坏加法的提交,已知好的提交是 fa5873a→ git bisect start && git bisect bad && git bisect good fa5873aBisecting: 4 revisions left to test after this (roughly 2 steps)[37a14229ef73e1801a49defb16a86a26c122aa41] refactor step 5

不带参数的 bad 指当前 HEAD。git 立刻检出区间中点,并告诉你还剩几步:十个提交要查三四次,一千个提交也只要十次。

2. 每一步都要回答

clai
$ clai 这个提交是好的→ git bisect goodBisecting: 2 revisions left to test after this (roughly 1 step)[cbae02ada1b9e211691957bc7daff38a214a5a13] speed up addition

测试、回答 good 或 bad,git 接着往下走。每个回答都会丢掉剩余范围的一半。

3. 结果

clai
$ clai 这个提交是坏的→ git bisect badcbae02ada1b9e211691957bc7daff38a214a5a13 is the first bad commitcommit cbae02ada1b9e211691957bc7daff38a214a5a13Author: Demo <[email protected]>Date:   Thu Aug 27 22:52:21 2026 +0700    speed up addition calc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)

git 说出第一个坏提交,并顺手打印它的改动统计。再执行一次 git show <哈希> 就会发现,所谓"加速加法"把加号换成了减号。

4. 同样的事,零手动回答

clai
$ clai 自动找出问题提交,测试脚本是 ./check.sh→ git bisect run ./check.shrunning './check.sh'Bisecting: 0 revisions left to test after this (roughly 0 steps)[e0991601aa7cd6c25d3ac78b486319a7ee55713e] refactor step 6running './check.sh'cbae02ada1b9e211691957bc7daff38a214a5a13 is the first bad commitbisect found first bad commit

这才是值得记住的部分。脚本对好提交返回 0,对坏提交返回任意非 0 值,而且它完全可以只有一行:test "$(./calc.sh 2 3)" = "5"。

5. 那些根本编译不过的提交

clai
$ clai 跳过编译不过的提交→ git bisect run ./check125.sh

退出码 125 很特别:它表示"这个提交没法评判,跳过"。这一步会在 git bisect log 里以 # skip: 行出现。常见写法是:构建失败 → exit 125,测试失败 → exit 1。

6. 回到出发的地方

clai
$ clai 结束二分查找并让我回到自己的分支→ git bisect resetPrevious HEAD position was cbae02a speed up additionSwitched to branch 'main'

二分查找期间仓库处于 detached HEAD 状态。reset 会把你送回起始分支。忘了这一步,你会整晚都在纳闷工作副本里怎么全是旧代码。

Gotchas

  • 一次答错毁掉整轮查找。 在真实仓库上验证过:某一步把 good 答成 bad,git 就指认了 e099160,而真凶是 cbae02a。补救办法是 git bisect log:保存下来,改掉错的那行,再用 git bisect replay 重放。
  • git bisect run 看的是退出码,不是输出。 一个打印"FAILED"却以 0 退出的脚本会被算作成功。大于 127 的码会直接中止整个二分查找。
  • 测试脚本最好放在仓库之外。 未被跟踪的文件能挺过检出,但只存在于近期提交里的脚本,一旦 git 检出老提交就会消失——把你的二分查找一起带走。

Related questions

要是我不知道哪个提交是好的? 挑一个明显够老的:git bisect good HEAD~200。如果它也是坏的,git 会在第一步就告诉你。

能按输出里的某个字符串来二分吗? 可以,任何命令都行:git bisect run sh -c '! ./app 2>&1 | grep -q "NullPointer"'。唯一要求是 0 代表好。

怎么看找到的提交改了什么? git show <哈希> 打印完整差异,git show --stat <哈希> 只给文件列表。

See also

CliAI 会根据普通描述写出二分查找的命令和一行测试脚本,并在执行前先显示出来。一行安装。