让 Git Bisect 帮助你

英文原文:Letting Git Bisect Help You

 

Git 提供来很多的工具来帮助我们改进工作流程。 bisect 命令就是其中之一, 虽然由于使用得不多而不广为人知,但是当你想知道一个本来好的分支从什么时候开始变坏时,它就能派上用场了。到底是哪一次提交把事情搞砸了呢,让 bisect 来告诉你吧。

Bisect 基于二分查找算法。给定一个有序的元素序列,它会返回要你要查找的元素的序号(或者告诉你该元素是否在序列中)。它基于如下的不变式:当你对比完一个元素,你就能根据比较的结果抛弃掉它之前或者之后的所有元素(从而大大缩小下次查找的范围)。

 

如果你有去过图书馆,那你可能注意到了每个专区内的书籍都是按作者姓名来排序的。比方说你要找的一本书的作者的姓为 Martin,而你刚刚看到某个书架上的最后一本书的作者的姓是 F 开头的,那么你就能确定你要找的书一定放在后面的某个书架上。依此方法继续下次,你就能快速的缩小搜索范围直到找到你要的书为止。

二分查找法亦是如此。如果你想在有 n 个元素的序列(有序的)中查找元素 x,你挑出第 n/2 个元素并将其与元素 x 比较。如果 x 大,那么就对从 n/2+1 到 n 的子序列重复上述步骤,反之,就对从 1 到 n/2-1 的子序列重复上述步骤, 这样一直递归下去。这里就有一个 关于此算法的有趣的演示

 

二分查找算法所需的比较次数通常都比你傻傻的去拿序列中的每个元素与 x 比较所需的比较次数少得多。事实证明在有序序列中查找元素,二分查找更快更有用。

Bisect

Bisect 就是利用二分查找发来查找在你的某一分支中到底是哪一次提交引入了特定的变更。

首先,也是最起码的,你得有办法检测出一个给定的提交点是不是有 bug。通常你可以写个测试代码,如果这也不可能的话,那至少你得有一套步骤来使 bug 再现出来。

 

有了这套检测方法,你就可以开始用 bisect 查找你的提交历史来以最快的速度发现引入 bug 的时间点了。等等,你还得准备好两个提交点:一个是你确定 bug 还没有被引入的提交点,另一个则是确定 bug 已经引入了的提交点(这样就缩小了初始的查找范围)。在 bisect 命令上,你可以用哈希值(hash)或者标签(tag)来引用这两个提交点。bisect 查找时,查找范围里的提交点要么被标记为好的(通过测试,没有 bug 的),要么被标记为坏的(通不过测试,有 bug 的)。

上图演示了 bisect 的执行步骤(绿点是好的提交点,红点的就是坏的):bisect 被要求在提交点 1 到 8 这个范围内查找首次引入 bug 的提交点(看图一目了然是提交点 6),这里提交点 1 和 8 就是上段中提及的,我们需要首先准备好给 bisect 命令的两个(一好一坏)提交点。bisect 首先用调用者提供的检测方法来测试 1 到 8 中间的提交点 4,如果它是好的(图上的情况),那么 bisect 就缩小范围为它右边的区域,重复上述步骤,反之,则选择其左边的区域为新的搜索范围。如此递归下去,在上图的例子中,只需要 3 步就确定了罪魁祸首是提交点 6.

 

我这里创建了一个 git 仓库,里面共有 1024 个提交记录,每个提交都往一个文本文件后面添加一个递增的数字,从 1 到 1024,一行一个数字。我们的任务是要找出是哪个提交点向文本文件附加了 1013 这个数字(我们假定它就是一个 bug)。

首先,我们定出一个方法来判断文件里面有没有这个数字。这就是一个测试了。方法很简单:

1
grep 1013 file.txt

有了这条命令,我们就可以开始 bisect 了:

1
$ git bisect start

对 master 分支(的头部提交点)运行这个测试,没有得到想要的结果(就是没有任何输出),所以我们把它标记为坏的。

1
$ git bisect bad

现在让我们指定一个好的提交点:假设第一个提交点(7c0dcfa)是没有 bug 的。我们可以检出(check out)这个提交点并标记它为好的提交点(git bisect good + 该提交点的哈希值)。

1
2
3
$ git bisect good 7c0dcfa 
Bisecting: 511 revisions left to test after this (roughly 9 steps) 
[8950f7db7e7cad0b2dc394ff9b75fc3d38c9d72a] added 512

也可以用下面的命令,完成上面的所有步骤:

1
$ git bisect start master 7c0dcfa

git bisect start 命令可以接受两个参数,第一个是坏的提交点,第二是好的提交点。

 

很好,git bisect 自动检出了正中间的提交点,运行我们的检测命令,没有任何的输出(没有 bug),因此我们把该提交点标记为好的。

1
2
3
4
5
grep 1013 file.txt 
 
$ git bisect good 
Bisecting: 剩余 255 个修订待测试 (大概还需要 8 步) 
[a01ba83f3500b48da97c5f5c33052623aaa4161a] added 768

译者注:按照二分查找算法,bisect 根据你标记的结果,决定是检出左半边的中间提交点(如果你标记为 bad)还是右半边的中间提交点(如果你标记为 good)

在 bisect 新检出的提交点上,我们再次运行测试命令。这次依然是个好的提交点。

1
2
3
4
5
grep 1013 file.txt 
 
$ git bisect good 
Bisecting:剩余 127 个修订待测试 (大概还需要 7 步)
[4a4a668bf3363d09af5fd1906bc4272aacdb4495] added 896

再次检测,还是好的提交点。

1
2
3
4
5
grep 1013 file.txt 
 
$ git bisect good 
Bisecting: 剩余 63 个修订待测试 (大概还需要 6 步) 
[9059c5b8b898159e8d1d797bff3b1febd1fd6a1c] added 960

让我们看看这些消息:除了告诉你当前新检出的提交点以及新的搜索范围内有多少个待测试的提交点外,它还估计出你最多还需要重复多少次测试命令就能找到你要的提交点。这次又是一个好的提交点。

1
2
3
4
5
grep 1013 file.txt
 
$ git bisect good
Bisecting: 剩余 31 个修订待测试 (大概还需要 5 步)
[0c844d0b33ef297b742206ebc293f4925705b083] added 992

继续,依然是一个好的提交点。

1
2
3
4
5
grep 1013 file.txt 
 
$ git bisect good 
Bisecting: 剩余 15 个修订待测试 (大概还需要 4 步) 
[0ee17eb17bd96b321a01c73eb13a8929a68b1239] added 1008

还有 4 步,这次依然 good。

1
2
3
4
5
grep 1013 file.txt 
 
$ git bisect good 
Bisecting: 剩余 7 个修订待测试 (大概还需要 3 步) 
[dfb1e71736dcfffa2a30aecd7299f45f757c057e] added 1016

这次测试命令终于有了输出,bug 现身了!我们把这个提交点标记为坏的。

1
2
3
4
5
grep 1013 file.txt 
1013
$ git bisect bad 
Bisecting: 剩余 3 个修订待测试 (大概还需要 2 步) 
[6e6d08c374df5162fed65fed82859b69f86b936e] added 1012

终点近在咫尺,测试通过,我们把它标记为好的。

1
2
3
4
5
grep 1013 file.txt 
 
$ git bisect bad 
Bisecting: 剩余 1 个修订待测试 (大概还需要 1 步) 
254efa859d7fc66f1f58a59f0] added 1014

坏的提交点。

1
2
3
4
5
grep 1013 file.txt 
1013
$ git bisect bad 
Bisecting: 剩余 0 个修订待测试 (大概还需要 0 步) 
8e16d98ec7039a7c53855dd9ed6] added 1013
 

最后一步,这次是坏的。

$ git bisect bad 
458eab0eb8d808e16d98ec7039a7c53855dd9ed6 is the first bad commit
commit 458eab0eb8d808e16d98ec7039a7c53855dd9ed6
Author: Rodrigo Flores <mail@rodrigoflores.org>
Date:   Tue Oct 21 22:31:05 2014 -0200     added 1013 :100644 100644 7bc3db7f48a43ccf1a8cc7c26146912cc88c1009 b393a2138a96c1530f41f70
1ab43cca893226976 M  file.txt

我们终于得到了那个引入 1013 数字的提交点。命令 git bisect log 可以回放整个过程。

$ git bisect start 
# bad: [740cdf012013dc41a39b41d4b09b57a970bfe38f] added 1024
git bisect bad 740cdf012013dc41a39b41d4b09b57a970bfe38f
# good: [7c0dcfa7514379151e0d83ffbf805850d2093538] added 1
git bisect good 7c0dcfa7514379151e0d83ffbf805850d2093538
# good: [8950f7db7e7cad0b2dc394ff9b75fc3d38c9d72a] added 512
git bisect good 8950f7db7e7cad0b2dc394ff9b75fc3d38c9d72a
# good: [a01ba83f3500b48da97c5f5c33052623aaa4161a] added 768
git bisect good a01ba83f3500b48da97c5f5c33052623aaa4161a
# good: [4a4a668bf3363d09af5fd1906bc4272aacdb4495] added 896
git bisect good 4a4a668bf3363d09af5fd1906bc4272aacdb4495
# good: [9059c5b8b898159e8d1d797bff3b1febd1fd6a1c] added 960
git bisect good 9059c5b8b898159e8d1d797bff3b1febd1fd6a1c
# good: [0c844d0b33ef297b742206ebc293f4925705b083] added 992
git bisect good 0c844d0b33ef297b742206ebc293f4925705b083
# good: [0ee17eb17bd96b321a01c73eb13a8929a68b1239] added 1008
git bisect good 0ee17eb17bd96b321a01c73eb13a8929a68b1239
# bad: [dfb1e71736dcfffa2a30aecd7299f45f757c057e] added 1016
git bisect bad dfb1e71736dcfffa2a30aecd7299f45f757c057e
# good: [6e6d08c374df5162fed65fed82859b69f86b936e] added 1012
git bisect good 6e6d08c374df5162fed65fed82859b69f86b936e
# bad: [1d23b7045a8accd254efa859d7fc66f1f58a59f0] added 1014
git bisect bad 1d23b7045a8accd254efa859d7fc66f1f58a59f0
# bad: [458eab0eb8d808e16d98ec7039a7c53855dd9ed6] added 1013
git bisect bad 458eab0eb8d808e16d98ec7039a7c53855dd9ed6
# first bad commit: [458eab0eb8d808e16d98ec7039a7c53855dd9ed6] added 1013

这个例子里一共有 1024 个提交点,遍历他们我们只用了 10 步。如果提交点数量再多一倍变成 2048 个,根据二分查找算法,我们仅仅需要多加一步就能找到想要的提交点,因为二分查找算法的时间复杂度为 O(log n)。

尽管已经如此高效,一遍又一遍的运行测试命令还是很枯燥的。因此,让我们再进一步,将这个过程自动化吧。

 

自动化

Git bisect 能接受一个脚本文件名作为命令参数,通过它的返回代码判断当前提交点的好坏。如果返回 0,就是好的提交点,返回其他值就是坏的提交点。凑巧的是,grep 命令如果找到了匹配行就会返回 0,反之返回 1。你可以使用 echo $? 命令来检查测试命令的返回值。

grep 的返回值正好和我们的测试标准相反,所以我们需要写一个脚本来反转它的值(我不太习惯写 shell 脚本,如果大家有更好的方法,感谢告知)。

1
2
3
4
5
6
7
#!/bin/sh
 
if [[ `grep 1013 file.txt` ]]; then
  exit 1
else
  exit 0
fi

保存上述代码为 test.sh,并赋予它执行权限。

接着在给 bisect 命令指定了初始的好/坏提交点之后,你只需要运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
git bisect run ./test.sh
running ./ola.sh
Bisecting: 255 revisions left to test after this (roughly 8 steps)
[a01ba83f3500b48da97c5f5c33052623aaa4161a] added 768
running ./ola.sh
Bisecting: 127 revisions left to test after this (roughly 7 steps)
[4a4a668bf3363d09af5fd1906bc4272aacdb4495] added 896
running ./ola.sh
Bisecting: 63 revisions left to test after this (roughly 6 steps)
[9059c5b8b898159e8d1d797bff3b1febd1fd6a1c] added 960
running ./ola.sh
Bisecting: 31 revisions left to test after this (roughly 5 steps)
[0c844d0b33ef297b742206ebc293f4925705b083] added 992
running ./ola.sh
Bisecting: 15 revisions left to test after this (roughly 4 steps)
[0ee17eb17bd96b321a01c73eb13a8929a68b1239] added 1008
running ./ola.sh
Bisecting: 7 revisions left to test after this (roughly 3 steps)
[dfb1e71736dcfffa2a30aecd7299f45f757c057e] added 1016
running ./ola.sh
Bisecting: 3 revisions left to test after this (roughly 2 steps)
[6e6d08c374df5162fed65fed82859b69f86b936e] added 1012
running ./ola.sh
Bisecting: 1 revision left to test after this (roughly 1 step)
[1d23b7045a8accd254efa859d7fc66f1f58a59f0] added 1014
running ./ola.sh
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[458eab0eb8d808e16d98ec7039a7c53855dd9ed6] added 1013
running ./ola.sh
458eab0eb8d808e16d98ec7039a7c53855dd9ed6 is the first bad commit
commit 458eab0eb8d808e16d98ec7039a7c53855dd9ed6
Author: Rodrigo Flores <mail@rodrigoflores.org>
Date:   Tue Oct 21 22:31:05 2014 -0200
 
    added 1013
 
:100644 100644 7bc3db7f48a43ccf1a8cc7c26146912cc88c1009 b393a2138a96c15
30f41f701ab43cca893226976 M  file.txt
bisect run success

不费吹灰之力,你就得到了出问题的提交点。

 
 

结论

你很可能不会每天都用到 bisect。也许一周用一次,甚至一个月只用到一次。但是当你想要排查出带入问题的提交点时,bisect 确实能帮你一把。尽管并非必须,控制好每次提交的改动规模不要太大将对 bisect 排查大有帮助。如果每个提交都包含了大量的改动,那么就算 bisect 帮你找到这个提交,你也不得不埋头于大量的改动中苦苦搜寻 bug 的踪迹。因此,我推荐的提交策略是嫌大不嫌多。

你呢?你经常使用 bisect 吗?

让 Git Bisect 帮助你的更多相关文章

  1. 查找问题的利器 - Git Bisect

    原文:http://gitbook.liuhui998.com/5_4.html 假设你在项目的'2.6.18'版上面工作, 但是你当前的代码(master)崩溃(crash)了. 有时解决这种问题的 ...

  2. [Practical Git] Diagnose which commit broke something with git bisect

    Sometimes you find a bug in your project that has been around for a while without being noticed; it ...

  3. [转]查找问题的利器 - Git Bisect

    转自:http://gitbook.liuhui998.com/5_4.html 假设你在项目的'2.6.18'版上面工作, 但是你当前的代码(master)崩溃(crash)了. 有时解决这种问题的 ...

  4. Fighting regressions with git bisect---within git bisect algorithm

    https://www.kernel.org/pub/software/scm/git/docs/git-bisect-lk2009.html Fighting regressions with gi ...

  5. 使用 git bisect 定位你的 BUG

    Git 是开发者的好帮手,今天跟大家分享的是用 git bisect 来找到你代码中的 bad commit . 背景 你可能遇到过这种情况, 昨天下班前把模块开发完了, 单元测试验证通过, git ...

  6. git bisect

    reference : http://www.ruanyifeng.com/blog/2018/12/git-bisect.html git bisect 命令教程 作者: [12]阮一峰 日期: [ ...

  7. git bisect:让你闭眼都能定位疑难 bug的利器

    摘要:git bisect命令使用二分搜索算法来查找提交历史中的哪一次提交引入了错误.它几乎能让你闭着眼睛快速定位任何源码导致的问题,非常实用. 本文分享自华为云社区<利用好 git bisec ...

  8. GIT界的神探--bisect

    GIT界的神探--bisect 今天我们需要使用git命令中的bisect破获一起凶杀案,下面请看具体案件: 在一个git仓库中有a,b,c,d,e,f,g,h,i,j,k这几个提交,在k提交中发现软 ...

  9. 常look的Git命令

    常用的Git命令   命令  简要说明 git add 添加至暂存区 git add–interactive 交互式添加 git apply   应用补丁 git am  应用邮件格式补丁 git a ...

随机推荐

  1. hdu 4643 GSM(暴力)

    GSM Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total Submis ...

  2. Ubuntu16.04 启用root权限

    装了Ubuntu 16.04之后想使用超级权限对系统进行操作 使用命令 su - 切换超级用户,提示输入密码,却怎么都不对,网上找的资料说是没有启用root权限,于是根据网上提供的方法启用root权限 ...

  3. NOIP原题板刷

    update 10.11 我可能已经刷完大部分了,可是这篇blog我也不想更了 这个人很懒,做了很多题但是不想写题解,也不想更blog,所以这篇blog又咕咕了. 把从 \(1997-2017\) 近 ...

  4. Jmeter BeanShell前置处理器、取样器、后置处理器

    前置处理器:BeanShell PreProcessor取样器 :BeanShell Sampler后置处理器:BeanShell PostProcessor 1.前置 import org.apac ...

  5. ActiveMQ点对点模式

    1.安装ActiveMQ服务器(略) 2.启动ActiveMQ,浏览器访问8161端口,默认账号admin/admin 3. 生产者代码 package test001; import org.apa ...

  6. visual studio 编译报错:未能向文件“....csproj.FileListAbsolute.txt”写入命令行,对路径的访问被拒绝

    在网上开始查找出错的解决方法,终于找到了,原来解决方法这么简单,当初以为是权限的问题,后来发现不是权限问题,在VSS中比以前多了两个目录“bin”和“obj”,可能是有人上传的时候将这两个文件夹一起上 ...

  7. 【POJ2893&HDOJ6620】M × N Puzzle(n*m数码判定)

    题意:给定一个n*m的矩阵,其中不重复地填[0,n*m-1],问是否能通过有限步数将0移到右下角 n,m<=1e3 思路:结论题 当板子了 #include<bits/stdc++.h&g ...

  8. UE4 中的Blutilities

    该功能是为编辑器中的简单扩展功能而设置的. 一般而言用蓝图在编辑器中做功能扩展都会用到Construction Script,但该功能有一些缺陷: 首先在actor发生任何变化(包括Transform ...

  9. 万能的gitignore文件模版

    ## .gitignore for Grails 1.2 and 1.3 # .gitignore for maven target/ *.releaseBackup # web applicatio ...

  10. composer国内镜像

    composer国内镜像 一.总结 一句话总结: 直接百度 “composer 国内镜像” 即可 直接运行:composer config -g repo.packagist composer htt ...