使用 Git Bisect 超级加速调试

本文详细介绍了如何使用 Git Bisect 命令通过二进制搜索算法快速定位代码中引入错误的提交。作者通过实际案例展示了如何开始搜索、标记好坏提交,以及处理构建错误等挑战,帮助开发者高效调试复杂问题。

使用 Git Bisect 超级加速调试

Git Bisect 速查表

1
2
3
4
5
$ git bisect start
$ git bisect bad                 # 当前版本有问题
$ git bisect good v2.6.13-rc2    # v2.6.13-rc2 已知是好的
# 重复执行 git bisect [good|bad] 直到找到引入错误的提交
$ git bisect reset

陷入困境的调试经历

我在学习 Blazor 时构建了一个简单应用。在功能分支工作一段时间后,我决定测试登出场景。当我尝试重新登录时,登录页面陷入了无限重定向循环。

我尝试了所有能想到的方法:在所有重定向代码处设置断点、允许登录页面匿名访问、调整授权策略,甚至向 Copilot 寻求帮助,但都无济于事。

当我切换回主分支时,发现这个错误也存在!这意味着这个错误已经在代码中存在一段时间了,只是因为我一直保持登录状态而没有注意到。

Git Bisect 救援行动

从文档中可知:

该命令使用二进制搜索算法来查找项目历史中哪个提交引入了错误。你需要先告诉它一个已知包含错误的"坏"提交,以及一个已知在错误引入之前的"好"提交。然后 git bisect 在这两个端点之间选择一个提交,并询问你所选提交是"好"还是"坏"。它会持续缩小范围,直到找到引入更改的确切提交。

关键点是这是二进制搜索。即使你要搜索的提交范围有 128 个提交,最多也只需要 7 步就能找到引入错误的提交(2^7 = 128)。

实际使用示例

1
2
3
4
5
6
$ git bisect start # 开始
$ git bisect bad   # 当前提交有问题

$ git bisect good 543ada5
Bisecting: 7 revisions left to test after this (roughly 3 steps)
[9736e3f90b571bebf512c2acb1f7ef14f3a77df4] Update all the NPMs

在几次迭代后,git bisect 找到了引入错误的提交:

1
2
3
4
4e08eb48956b80a7a33987df272d30acb5bd6ee2 is the first bad commit
commit 4e08eb48956b80a7a33987df272d30acb5bd6ee2
Author: Phil Haack <haacked@gmail.com>
Date:   Thu Oct 10 15:38:21 2024 -0700

问题出现在 App.razor 文件的更改中:

1
2
- <Routes />
+ <Routes @rendermode="InteractiveServer" />

Git Bisect 的挑战

建议阅读 git bisect 的文档,了解其他重要的子命令。

例如,有时某个提交无法测试(比如构建失败)。在这种情况下,可以调用 git bisect skip 跳过该提交。

在实践中,我发现有时需要对提交进行一些调整才能运行。例如,某个提交出现了构建错误:

1
error NU1903: Warning As Error: Package 'System.Text.Json' 8.0.4 has a known high severity vulnerability

由于我只需要在本地构建和测试,我忽略了该警告以测试该提交。

自动化 Git Bisect

git bisect 有自动化的潜力。你可以编写一个脚本来构建和测试每个提交。如果提交构建或测试失败,脚本可以为你调用 git bisect skip

例如,可以这样做:

1
git bisect run dotnet test

这将在每个提交上运行 dotnet test,如果测试失败则自动调用 git bisect skip

然而,在实践中,效果可能不如预期。当你编写时正常的提交可能不再构建。而且,你可能真正想做的是在 git bisect 过程中注入新的测试。

更不用说如果你有访问数据库的集成测试。你必须在 git bisect 过程中运行数据库迁移的上上下下。

读者评论

Max 提到:如果你在使用带有 CI 测试和合并提交的 PR 工作流,可以在主分支上使用 git bisect 的 first-parent 标志,首先确定哪个 PR(合并提交)引入了错误。我发现这个步骤通常可以轻松自动化(因为你的 CI 系统已经强制它们构建)。有时知道是哪个 PR 就足够了。如果你需要深入了解 PR 中的哪个提交,可以分支"第二个父级",并再次受益于 first-parent 标志,仅测试 PR 的"自有"提交,而不是从其他分支带来的任何提交。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计