首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >用 git bisect 快速定位你想找的 commit

用 git bisect 快速定位你想找的 commit

作者头像
神说要有光zxg
发布于 2023-08-29 07:41:08
发布于 2023-08-29 07:41:08
30700
代码可运行
举报
运行总次数:0
代码可运行

当项目报错,你想定位是哪个 commit 引入的错误的时候,会怎么做呢?

有的同学说可以看文件的历史呀,看文件是在哪个 commit 改的。

但这种方式前提是你确定是哪行代码引起的错误。

如果不知道哪里导致的呢?

那可以一个个 commit 试呀。

但如果 commit 很多,有几十个呢?难道要全部试一遍么?

对列表搜索最快的方式是二分查找,用这种方式来查找会不会更高效呢?

没错,我们确实可以通过二分的方式来查找,先确定查找的 commit 范围,试下中间的 commit 有没有问题,然后缩小范围,再试下中间的,逐步缩小范围直到只有一个 commit。

这个 commit 就是引入问题的 commit。

自己来做二分查找的话,每次都要计算 commit 列表中间的 commit 是哪个,这还是比较麻烦的,所以 git 内置了命令来支持,就是 git bisect 命令。

bisect 就是二分的意思。

我们创建个 demo 项目试一下:

git init 初始化,然后新建 a.js,里面每一行提交一个 commit:

b.js 也是每行创建一个 commit:

这样就有 7 个 commit 了:

这里是 333 的 commit 引入的错误。但如果我们不知道是哪个 commit 引入的问题,该怎么定位呢?

这时候就可以用 git bisect 命令了。

执行 git bisect start 开始搜索:

提示是等待指定 bad 和 good 的 commit。

最新的 commit 是有问题的,所以是 bad:

指定 bad 之后,会告诉你已经知道了,还缺一个 good 的。

我们把 111 那个 commit 做为 good 的:

这时候提示你还有 2 个 commit 要测试,也就是还有 2 步。

你指定了 good、bad 的范围,那中间的 commit 算出来就是 444。

再取两次 bad、good 的范围,就能定位到目标 commit 了。

这时候 git log 可以看到已经切换到 444 的 commit。

我们跑下代码:

这时候还是报错的,所以继续指定当前 commit 为 bad:

这时候中间的 commit 是 333,还需要 1 步了。

这时候确实切换到了 333:

这时候执行 git bisect view 是这样的:

git bisect view 就是查看还剩下的 commit。

再跑下代码看看:

这时候代码还是报错,所以再标记为 bad。

然后切换到了 222,这时候没报错了,标记为 good:

这样就找到了哪个 commit 出的错,是 333 这个 commit

我们执行 git show 这个 commit:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
git show fd4dbe4

可以看到,确实是这个 commit 引入的错误:

这就是 git bisect 命令的使用方式。

如果你知道啥是二分查找,这个 git bisect 命令还是很容易学会的。

我们可以执行 git bisect log 回顾下整个查找过程:

通过一步步的二分查找,最终定位到了第一个出错的 commit。

当然,它的作用不只是查找出错的 commit 这么简单,你可以用它来查找任意的 commit,比如想查找是哪个 commit 导致的性能降低,是哪个 commit 实现的某个功能等。

不过这时候再用 good、bad 来标识就不合适了。

所以 git bisect 还提供了另一套标记:old、new

使用方式和 good、bad 没区别。

但是不能混用,你用了 good 就只能用 bad,用了 new 就只能用 old。

你可以用 git bisect terms 来查看当前的标记是啥:

这个标记也可以自己指定。

我们 git bisect reset 结束查找过程,commit 会回到原来的 777:

然后 git bisect start 的时候用 --term-good、--term-bad 来指定一套新标记:

这时候 git bisect terms 可以看到确实标记变了:

这时候就是用 aaa、bbb 来标记区间了:

标记的过程中,不只是有 good、bad 两种情况,有的时候你没法确定是 good 还是 bad 怎么办呢?

这时候可以 git bisect skip:

本来是在 444,因为无法确定是否是对的,所以 skip 了,这时候就移到了下一个 commit:

然后继续二分的过程。

那如果我全部都 skip 了呢?

那 bisect 也没办法,它会把所有剩下的 commit 列出来,告诉你这些还没测试:

这样是能快速找出目标 commit,但每个 commit 都要手动测试也太麻烦了,能不能自动化执行一个脚本来测试呢?

当然是可以的。

我们建个可执行文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#!/usr/bin/env node
const { execSync } = require('child_process');
const fs = require('fs');

if(!fs.existsSync('./a.js')) {
    process.exit(125);
}

try {
    execSync(' node ./a.js');
    process.exit(0);
} catch(e) {
    process.exit(1);
}

返回 0 代表当前 commit 是 good,返回 1 到 125 之间的数字代表是 bad,而返回 125 代表 skip。

现在直接执行这个文件会没权限:

这样加一下权限就好了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
chmod a+x ./test.js 

a+x 代表所有的(all)用户增加执行权限的意思。

然后我们开始一个新的 bisect 过程:

这里我加了两个参数,就是指定了 bad、good 的 commit 的意思,这样就可以直接开始二分了。

此外,你还可以指定哪些路径的 commit:

这样, 666、777 没有涉及到 a.js 的更改,所以不在二分查找的范围内,查找的 commit 范围就缩小了很多:

这里我们还是和之前一样的 commit 范围来测试:

然后执行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
git biset run ./test.js

通过执行脚本来自动标记 bad、good:

可以看到,444 跑脚本报错了,然后回到 333 跑, 还是报错,然后去 222 跑,这是不报错了,就定位到了第一个出错的是 333 这个 commit。

这和前面我们手动测试和标记 good、bad 是一样的流程,只不过自动执行的,方便了很多。

而且,这个过程是可以重做任意次的,你可以把 git bisect log 输出到某个文件,然后 git bisect replay 这个文件:

git bisect 会重新按照日志跑一次:

这就是 git bisect 的全部用法。

总结

想定位是哪个 commit 引入的问题,引起的某些变化,可以用 git bisect 来二分查找。

它有这些命令:

  • git bisect start:开启一个二分查找过程
  • git bisect good/new:指定某个 commit 为 good/new
  • git bisect bad/old:指定某个 commit 为 bad/old
  • git bisect skip:跳过某个 commit
  • git bisect reset:回到 git bisect 前的状态
  • git bisect view:当前二分查找过程还剩下多少 commit
  • git bisect log:查看 bisect 过程的日志
  • git bisect run:通过可执行文件来自动测试和打 good、bad 标记
  • git bisect replay:根据日志文件重新跑二分查找过程

想想如果没有 git bisect 命令呢?你需要一个个 commit 的测试,那样也太低效了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-05-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 神光的编程秘籍 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档