一次提交把功能弄坏了,Git Bisect 官方怎么把出问题的提交找出来?

功能是某次提交之后才坏的,更早的某个点还正常。不想从那以后的每一条提交挨个 checkout 再编译试,只想用二分把那一次找出来。

git-bisect 手册 把这条命令写成:用二分搜索,找出项目历史里引入某个问题的那一次提交。你先告诉它一个已知「坏」的提交(里面已经有这个毛病),再告诉它一个已知「好」的提交(那时候功能还正常)。然后 git bisect 会在两端之间挑一个中间提交检出给你,问你这次是好还是坏,再把范围收窄,直到定位到引入改动的那一次。手册也写了,它也能用来找任何一种性质发生变化的提交,比如哪一次把问题修掉了。那种情况下可以把 good/bad 换成 old/new,或者自己指定用语,但同一轮里不能把两套词混用。

基本用法是先 git bisect start,再对当前版本执行 git bisect bad,然后对已知正常的点执行 git bisect good <提交>。手册里的例子用过 v2.6.13-rc2 这种标签当好的一端。Pro Git 的 Debugging with Git 则用 v1.0 做同样的标记。至少给出一端坏、一端好之后,命令会检出中间那次,并打出类似 Bisecting: 675 revisions left to test after this (roughly 10 steps) 的提示。接着编译并测试当前树:功能正常就 git bisect good,仍然坏就 git bisect bad。它会再给出下一个要测的提交,剩下的范围大约减半。一直重复,直到没有剩余修订要检查,命令会打印出第一次变坏的提交,并把 refs/bisect/bad 指过去。两端也可以在开始时一次写完,手册和 Pro Git 都支持 git bisect start <坏> <好> 这种写法,坏的写在前,好的写在后。如果你已经知道问题只跟某几处路径有关,还可以在 -- 后面加上 pathspec,把搜索收到这些路径上,少测一些无关提交。

测完要清理现场,手册写的是 git bisect reset。默认会回到执行 git bisect start 之前检出的那个提交。再开一次 git bisect start 也会清掉上一轮的二分状态。需要留在第一次坏提交上时,可以用 git bisect reset bisect/bad。Pro Git 也提醒过,查完若不做 reset,工作区会停在一个很别扭的状态上。中途标错了一次,手册提供了 git bisect log 查看到目前为止的标记,把输出存下来、改掉标错的那几行,再 git bisect reset 之后用 git bisect replay 把修正过的记录重放回去。某一跳编译失败、又跟你在追的那个毛病无关时,可以用 git bisect skip 跳过当前这次,也可以跳过一个区间。手册提醒过:如果跳过的提交紧挨着真正的第一次坏提交,Git 就无法精确指出是哪一次。二分过程中想看还剩下哪些嫌疑提交,手册写了 git bisect visualize(也可以写成 git bisect view)。在图形界面可用时会打开 gitk,否则退回 git log,也可以带 --stat 这类参数。忘了当前用的是 good/bad 还是 old/new 时,用 git bisect terms 就能查到。若中途检出了别的修订、把二分打断了,可以用 git bisect next 明确请求下一步。手册还有 --first-parent:遇到合并提交时只沿第一父提交走,在查找「合并进来时才引入的回归」时,会把合并提交本身标成引入点,而不再往被合并分支里那些祖先上追。

如果每一次好还是坏,已经有脚本能判定,手册和 Pro Git 都写了 git bisect run。脚本退出码 0 表示当前提交是好的(或 old),1 到 127(不含 125)表示是坏的(或 new)。退出 125 表示这次没法测,效果相当于 git bisect skip。其他退出码会中止二分。Pro Git 的例子是先 git bisect start HEAD v1.0,再 git bisect run test-error.sh,让脚本在每一次检出上自动跑,直到找出第一次坏掉的提交。

这套流程只管在提交历史上把引入问题的那一次找出来,不是在教完整的 Git 工作流。想用交互界面翻历史,可以看站内这篇 lazygit。需要同时留着一份干净工作区继续干活,则是 git worktree 的范围,和二分不是同一件事。

你们现在是手工编译测每一跳,还是已经有脚本可以交给 git bisect run?