Git Bisect 快速上手

假设你刚接手一个项目,这时线上出现了一个 bug,因为对业务不熟悉加上老板施压,导致 bug 定位过程异常艰难,开始焦头烂额。

聪明的你转念一想:既然无法准确定位 bug,那不如先找到是哪个提交引入的 bug,这样再去定位 bug 就简单很多了。

于是你确认了bug 存在的 commit 区间,使用二分查找来找 bug commit ,假设 bug 存在的 commit 区间是(3 ~ 10),你开始了如下过程:

find-bug-commit.png

你确定了 3 是没有 bug ,10 有 bug,于是使用二分查找,找到 6,判断 6 有 bug。继续缩小范围,最终找到了第一次出现 bug 的 commit 是 4,就能确定 bug 是 4 引入的。

这个手动查找的过程比较繁琐,而恰好 git 是有命令来支持这个查找行为的,这个命令就是 git bisect

bisect 命令参数使用

将上诉过程使用基本 Git 命令,来表示。

  1. 找到引入 bug 的 commit 范围
  2. 二分查找,并切换到分支
  3. 运行代码,判断是否有 bug 是否存在
  4. 重复 2、3,最终确认第一次引入 bug 的 commit

现在使用 Git Bisect来实现,步骤和上面都差不多,只不过省去了很多人为的动作。

  1. 告诉 Git 需要开启 bisect 模式
1
git bisect start
  1. 告诉 Git 需要查找的 commit 范围,需要指定一个 good (正常代码) 和一个 bad(存在 bug 的提交)
1
2
3
4
5
6
7
8
9
// 切换到 commit 10,并标记为 bad
git checkout 10
git bisect bad

// 切换到 commit 3,并标记为 good
git chekcout 3
git bisect good

// 这样 bisect 就确认了 commit 范围
  1. 步骤 2 执行结束后,Git 会进行二分查找,并自动切换到对应分支。只需要再验证分支是否还存在 bug,存在标记为 bad,反之 good
1
2
3
4
5
// 根据实际情况标记为 bad 或者 good

git bisect good

git bisect bad
  1. 只需重复 3 的过程,最后 bisect 会帮我们找到第一个出现 bug 的 commit。

In Action

实战 Demo 地址: GitHubGitee

这个项目有多个版本,从 v1.0 到 v9.1,其中我们能确定 v1.0 是没有 bug,v9.1 是有 bug 的,bug 的表现形式是一条输出语句I have a bug!,代码如下:

1
2
3
4
5
6
7
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 查找过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 切换到任意版本(需要提前确认该版本是否存在 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 脚本命令如下:

1
2
3
4
5
6
7
8
9
10
#!/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;

有了这个判断脚本之后,上面的查找流程可以大大简化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 标记开始
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 为我们提供了另外一种解决思路。


Git Bisect 快速上手
http://wszzf.top/2022/11/09/Git Bisect 快速上手/
作者
Greek
发布于
2022年11月9日
许可协议