假设你刚接手一个项目,这时线上出现了一个 bug,因为对业务不熟悉加上老板施压,导致 bug 定位过程异常艰难,开始焦头烂额。
聪明的你转念一想:既然无法准确定位 bug,那不如先找到是哪个提交引入的 bug,这样再去定位 bug 就简单很多了。
于是你确认了bug 存在的 commit 区间,使用二分查找来找 bug commit ,假设 bug 存在的 commit 区间是(3 ~ 10),你开始了如下过程:

你确定了 3 是没有 bug ,10 有 bug,于是使用二分查找,找到 6,判断 6 有 bug。继续缩小范围,最终找到了第一次出现 bug 的 commit 是 4,就能确定 bug 是 4 引入的。
这个手动查找的过程比较繁琐,而恰好 git 是有命令来支持这个查找行为的,这个命令就是 git bisect。
bisect 命令参数使用
将上诉过程使用基本 Git 命令,来表示。
- 找到引入 bug 的 commit 范围
- 二分查找,并切换到分支
- 运行代码,判断是否有 bug 是否存在
- 重复 2、3,最终确认第一次引入 bug 的 commit
现在使用 Git Bisect来实现,步骤和上面都差不多,只不过省去了很多人为的动作。
- 告诉 Git 需要开启
bisect模式
git bisect start- 告诉 Git 需要查找的 commit 范围,需要指定一个 good (正常代码) 和一个 bad(存在 bug 的提交)
// 切换到 commit 10,并标记为 bad
git checkout 10
git bisect bad
// 切换到 commit 3,并标记为 good
git chekcout 3
git bisect good
// 这样 bisect 就确认了 commit 范围- 步骤 2 执行结束后,Git 会进行二分查找,并自动切换到对应分支。只需要再验证分支是否还存在 bug,存在标记为 bad,反之 good
// 根据实际情况标记为 bad 或者 good
git bisect good
或
git bisect bad- 只需重复 3 的过程,最后 bisect 会帮我们找到第一个出现 bug 的 commit。
In Action
这个项目有多个版本,从 v1.0 到 v9.1,其中我们能确定 v1.0 是没有 bug,v9.1 是有 bug 的,bug 的表现形式是一条输出语句I have a bug!,代码如下:
public class Product {
public static void main(String[] args) {
System.out.println("I'm version 9.1!");
System.out.println("I have a bug!");
System.out.println("A bad feature!");
}
}使用 git bisect 查找过程
# 切换到任意版本(需要提前确认该版本是否存在 bug),由于已确认 v9.1 有 bug,切换到 v9.1
git checkout v9.1
# 标记 git bisect 开始
git bisect start
# 存在 bug,标记为 bad
git bisect bad
# 切换到不存在 bug 的分支,标记为 good
git checkout v1.0
git bisect good
# 此时 git 已经自动切换到一个新的 commit,标记当前 commit
git bisect (good or bad)
# 重复上一步,直到 git,给出如下提示
xxxxxx is the first bad commit
commit xxxxxx
# git 已经帮我们找到了,第一次出现 bug 的 commit id,此时需要手动结束 bisect 过程
git bisect reset执行 git bisect reset 后,bisect 过程即结束。
使用脚本
bisect 支持使用脚本,使用脚本的目的是帮 git 来判断 bug。git 执行脚本,根据脚本的执行结果,自动帮我们标记 good 或 bad。
上面的例子,可以根据输出结果包含 bug字样来判断 bug,对应的 shell 脚本命令如下:
#!/bin/sh
javac Product.java
OUTPUT=$(java Product)
if [[ $OUTPUT == *"bug"* ]]; then
exit 1
else
exit 0
fi这个脚本执行 java Product,判断输出结果是否包含 bug 字样,包含则 exit code 为 1,反之为 0;
有了这个判断脚本之后,上面的查找流程可以大大简化
# 标记开始
git bisect start
# 把 v9.0 标记为 bad,把 v1.0 标记为 good
git checkout v9.0
git bisect bad
git checkout v1.0
git bisect good (in v1.0)
# 执行脚本自动化查找过程
git bisect run <脚本名称>
# 脚本执行完之后,手动关闭 bisect
git bisect reset至此,bisect 查找过程结束。
结论
git bisect 在业务开发方面使用的比较少,但是在开源项目中,使用相对比较频繁。
对于开源项目,可以使用 git bisect 帮助开发者定位 bug,贡献 issues。
对于棘手的 bug 定位,git bisect 为我们提供了另外一种解决思路。