发布于 2026-01-06 6 阅读
0

Git Bisect 很简单(如何发起机器人起义)

Git Bisect 很简单(如何发起机器人起义)

当我们在应用程序中发现新的错误,或者在持续集成 (CI) 中发现测试随机失败时,通常情况下,该错误是由于另一次更改的意外副作用而产生的。

有很多博客文章和文档介绍了如何避免这些意想不到的后果,但由于没有人是完美的,因此了解如何诊断和找到此类错误的根本原因非常重要。

好消息是,如果你使用 Git,那就很简单。如果你没有使用 Git,请停止阅读本文,并前往git init你的项目目录运行命令。

由于开发者经常需要找到引入不良行为的更改,因此 Git 提供了一个名为 `bisect` 的命令bisect。`bisect` 的意思是“将代码切割或分割成两个相等或几乎相等的部分”。这正是该命令的作用。

二分是指“将物体切割或分割成两个相等或几乎相等的部分”。

在深入探讨之前git bisect,我们先来搭建一个简单的项目来演示这个问题。我将使用 Ruby 来演示,但示例使用哪种语言并不十分重要。

如果本文中的任何代码让你感到困惑,请直接跳过。我只是用它来解释为什么你可能需要使用它git bisect

我将创建一个目录并运行程序git init,然后编写一个函数并测试该函数。这就是我们的起点。

mkdir bisect-example && $_
git init
vim main.rb
Enter fullscreen mode Exit fullscreen mode
# main.rb

def square(number)
  number * number
end

# Testing Code

def assert_equal(actual, expected)
  unless actual == expected
    raise "Assertion failed: #{actual} does not match expected #{expected}"
  end
end

def test_square
  assert_equal(square(2), 4)
end

test_square
Enter fullscreen mode Exit fullscreen mode

所以,我们有一个函数square,它有一个参数,它的功能正如你所想(将传递给它的参数乘以自身)。我们还有几个不同的函数来测试这个square函数。

一切顺利,我们提交申请。

git add .
git commit -m "Add square function"
Enter fullscreen mode Exit fullscreen mode

世界原本处于完美的平衡状态,直到我们意识到数学发生了变化,所以我们的利益相关者来找我们说:“在这种新的数学规则下,当你计算某个数的平方时,你不能再用它自身乘以平方数,你需要改用加法。”

所以我们重构了函数。

# main.rb

def square(number)
  result = 0
  number.times do
    result += number
  end
  result
end

...

Enter fullscreen mode Exit fullscreen mode

现在我们已经完成了这个粗略的实现,但利益相关者都很满意,测试也通过了。所以我们决定提交。

git commit -am "Refactor square function to meet requirements"
Enter fullscreen mode Exit fullscreen mode

这种情况又发生了十几次(我们那位不重视提交信息的同事也参与了部分工作),然后又添加了一些新功能来帮助我们获得额外的提交信息。现在这个项目真的让人摸不着头脑,但因为我们是聪明的开发者,所以我们编写了测试!我们可以依靠这些测试来帮助我们发现问题。

有一天,我们正在愉快地编写main.rb文件,运行测试时却发现我们的square函数不知何故不再按测试预期的方式运行了!糟糕!

main.rb:15:in `assert_equal': Assertion failed: 5 does not match expected 4 (RuntimeError)
Enter fullscreen mode Exit fullscreen mode

我们目前的改动甚至没有涉及到这个square函数,我们正在开发一个全新的函数,overthrow_humanity这个函数甚至没有用到它square。因为我们是聪明的开发者,所以我们决定查看git历史记录,以了解这究竟是怎么发生的:

git log --oneline --no-merges

f25411d9e9 Add README
9a8225f6b3 Remove unused code in RobotOverlord#simulate_mercy
708a4955e0 workaround for legacy T-800 model
9a2f2805f6 this probably works
360689a0ec Add missing tests for AI features
cfcb762e91 fix whitespace
32ca0fa720 implement features because PO said to
26dd1ab393 Fix bug in cube function
11e118d981 Fix bug in square function that causes hostility
01da7be780 Add test coverage for RobotOverlord#enslave
59415871b2 Add dependency on skynet
31bb9ac6c2 do some work
92f4123391 Add RobotOverlord class
a1e8a90763 Improve test coverage for math functions
069ab50172 Refactor square_root? function
18512a7fef Add square_root? function
a15700afb3 Add cube function
c25f8ef431 Refactor square function to meet requirements
12e4ebffce Add square function
Enter fullscreen mode Exit fullscreen mode

最近四五个提交似乎对诊断问题帮助不大,事实上,乍一看,它们似乎都没有修改过main.rb文件。但这并不意味着这些更改不会产生意想不到的副作用,所以我们需要确定究竟是哪个更改导致测试失败。

我们终于到达了git bisect

修复导致此测试失败的代码(我们假设测试本身没有问题)的第一步,是找出它究竟是如何出错的。在某个提交中,项目中添加了一段代码,导致此测试失败。我们如何确定是哪个提交包含了这段代码呢?很简单,对每个提交运行测试,直到找到为止!但如果是 200 个提交呢?那就没那么容易了。

该命令git bisect利用二分查找快速找到引入特定错误的提交。让我们在这个项目中测试一下。

git bisect start
Enter fullscreen mode Exit fullscreen mode

我们已经开始了bisect,但是我们需要告诉 Git 项目当前状态没有按预期工作。为此,我们将当前提交标记为已修复bad

git bisect bad
Enter fullscreen mode Exit fullscreen mode

Git 需要的下一个信息是代码按预期运行的提交记录。我们记得第一次重构函数时运行测试并感到满意square,所以就选择进行重构的那个提交记录吧c25f8ef431 Refactor square function to meet requirements。我们需要告诉 Git,这次重构c25f8ef431成功了。

git bisect good c25f8ef431
Enter fullscreen mode Exit fullscreen mode

这是《星球大战3:西斯的复仇》中精彩剧情的开始(GIF图)

您应该会看到类似这样的回复:

Bisecting: X revisions left to test after this (roughly Y steps)
[some_commit_hash] Some commit message
Enter fullscreen mode Exit fullscreen mode

现在只需逐个查看每个版本,运行我们的测试,并将这些提交标记为成功good或失败即可bad

当你运行测试并确认当前提交没问题后,只需输入:git bisect good。当你发现某个提交有问题时,输入:git bisect bad。软件会自动找出导致测试失败的提交。

在我们的示例项目中,我们将逐步查看几个版本,我们会发现,在我们的某个提交中(11e118d981 Fix bug in square function that causes hostility)有人修改了该square函数。

Bisecting: 0 revisions left to test after this (roughly 0 steps)
[11e118d981] Fix bug in square function that causes hostility
Enter fullscreen mode Exit fullscreen mode

当我们深入了解一下这次提交中发生了哪些变化时,我们会发现某个开发人员(可能是我们自己)试图通过修改我们的square函数来修复项目中其他地方的错误。

# main.rb

def square(number)
  result = 1          # 🚨 This is the bug!
  number.times do
    result += number
  end
  result
end

...

Enter fullscreen mode Exit fullscreen mode

现在我们已经确切地知道了导致 bug 的原因。我们可以通过输入以下命令让 git 回到开始二分查找的位置:

git bisect reset
Enter fullscreen mode Exit fullscreen mode

现在我们只需要修复这个漏洞并提交更改即可。

这就是利用git bisect二分查找找到引入 bug 的提交的方法!这只是一个人为设计的例子,但实际上就是这么简单。

编写测试用例来利用git bisect这种方式是你的职责。

文章来源:https://dev.to/jacobherrington/git-bisect-is-easy-44ol