【Git 学习笔记_23】Git 实用冷门操作技巧(二)——妙用 git bisect 与 git blame 进行代码调试


本节所在位置:

  • 活用 git stash(一)
  • 保存并应用 stash(一)
  • 用 git bisect 进行调试(二)✔️
  • 使用 git blame 命令(二)✔️
  • 设置彩色命令行界面
  • 自动补全
  • 让 Bash 自带状态信息
  • 更多别名
  • 交互式新增提交
  • 用 Git 的图形界面交互式新增
  • 忽略文件
  • 展示与清理忽略文件

11.3 用 git bisect 进行调试

实际工作中经常遇到类似“究竟是哪次提交引入的某个 Bug”的问题,而底层应用二分算法的 git bisect 命令是该类问题的绝佳工具,尤其适用于在较长的提交历史记录中快速锁定引入 bug 具体 commit。二分查找法(binary search method)或二分法(bisection method),是一种在有序数组中查找目标位置的搜索方法。算法会在每一步与数组的中间值进行比较,如果匹配成功则返回该位置;否则,根据比较结果,选择中间值的右侧或左侧的子数组继续搜索,直至找到目标位置。在 Git 中,历史提交记录对应一组可供测试的值数组,若程序能在某个 commit 编译成功则为目标位置。二分查找的算法复杂度为 O(log n)

本节演示如何从一个提交了 23 次的带 bug 分支利用 git bisect 命令定位该 bug 的全过程(假设带 bug 提交的 SHA-183c22a39955ec10ac1a2a5e7e69fe7ca354129af):

# init repo
$ git clone https://github.com/PacktPublishing/Git-Version-Control-Cookbook-Second-Edition_tips_and_tricks.git bugHunting
$ cd bugHunting
$ git checkout bug_hunting

bug 位于 map.txt 文件第 38 行:

file with a bug

接下来是找到检测该 bug 的方法来测试不同版本。比如编译代码,执行测试等等。这里简化为定位代码中是否存在 oo 字样的行,编写测试脚本 test.sh 如下:(注:该测试脚本只适用于 Linux 环境下)

$ echo "! grep -q oo map.txt " > ../test.sh
$ chmod +x ../test.sh

为了避免测试脚本不受项目签出、编译等影响,最好将测试脚本放到 git 库外面。

小提示

经实测,测试脚本 test.sh 第一行末尾须加一个空格,否则脚本只能在 Linux 环境运行成功,而 Windows 下,Windows 的换行符号会与文件名 test.sh 连在一起,从而导致目标文件定位失败。

接下来开始测试:

$ git bisect start

将当前版本标记为【bad】:

# mark current commit as bad
$ git bisect bad

将上一个正常版本标记为【good】:

# mark the last known good commit (master HEAD) as good
$ git bisect good master
Bisecting: 12 revisions left to test after this (roughly 4 steps)
[7fb98059857aa0361cf14329d027573afd5a17b2] Build map part 10

启动第一轮排查:

# 1st round
$ ../test.sh; test $? -eq 0 && git bisect good || git bisect bad
Bisecting: 6 revisions left to test after this (roughly 3 steps)
[de6b768ceebd451591ee41ff4708ca889bafd3f2] Build map part 15

根据提示,启动第二轮排查:

# 2nd round
$ ../test.sh; test $? -eq 0 && git bisect good || git bisect bad
Bisecting: 2 revisions left to test after this (roughly 2 steps)
[865ff6f8a62d61e4a59f7bc1b2478aa6c71c345a] Build map part 13

再启动第三、四轮:

# 3rd round
$ ../test.sh; test $? -eq 0 && git bisect good || git bisect bad
Bisecting: 0 revisions left to test after this (roughly 1 step)
[27952d82d1f9067948b37e39b9bc4d0e6d846567] Build map part 14
# 4th round
$ ../test.sh; test $? -eq 0 && git bisect good || git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[83076f97e8312f8d59976c6373c897aef284c26d] Bugs...

此时,剩余步骤提示为 0,来到最后一轮排查:

# 5th round
$ ../test.sh; test $? -eq 0 && git bisect good || git bisect bad
83076f97e8312f8d59976c6373c897aef284c26d is the first bad commit
commit 83076f97e8312f8d59976c6373c897aef284c26d
Author: HAL 9000 <aske.olsson@switch-gears.dk>
Date:   Tue May 13 09:53:45 2014 +0200

    Bugs...

 map.txt | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

由此锁定引入 Bug 的那次提交(版本标识为:83076f97e8312f8d59976c6373c897aef284c26d)。最后是状态重置:

# reset bisect, get SHA-1 (83076f9): 
$ git bisect reset
Previous HEAD position was 83076f9 Bugs...
Switched to branch 'bug_hunting'
Your branch is up to date with 'origin/bug_hunting'.
$ git show 83076f9
commit 83076f97e8312f8d59976c6373c897aef284c26d
Author: HAL 9000 <aske.olsson@switch-gears.dk>
Date:   Tue May 13 09:53:45 2014 +0200

    Bugs...

diff --git a/map.txt b/map.txt
index 8a13f6b..1afeaaa 100644
--- a/map.txt
+++ b/map.txt
@@ -34,6 +34,6 @@ Australia:
                .-./     |.     :  :,
               /           '-._/     \_
              /                '       \
-           .'                         *: Brisbane
-        .-'                             ;
-        |                               |
+           .'        \__/             *: Brisbane
+        .-'          (oo)               ;
+        |           //||\\              |

演示过程的示意图如下:

git bisect result

注意:其实倒数第二轮时已经找到引入 bug 的版本了,但需要进一步确认 bug 是该版本自己产生的,还是其他版本传播到这里的,因此多运行了一轮。

发散

上述示例的一个弊端就是手动执行每一轮测试脚本,直到找出目标位置为止。若想让该过程自动进行,可以给 git 指定一个脚本(script),或 makefile,或一个测试脚本,使得二分查找自动进行。传入的脚本,会在命中目标时用返回一个为零状态(exit with a zero-status),或者在未命中时返回一个非零状态(a non-zero status):

# Automatic bisect
$ git bisect start HEAD master
Bisecting: 12 revisions left to test after this (roughly 4 steps)
[7fb98059857aa0361cf14329d027573afd5a17b2] Build map part 10
# pass the test script
$ git bisect run ../test.sh
running  '../test.sh'
Bisecting: 6 revisions left to test after this (roughly 3 steps)
[de6b768ceebd451591ee41ff4708ca889bafd3f2] Build map part 15
running  '../test.sh'
Bisecting: 2 revisions left to test after this (roughly 2 steps)
[865ff6f8a62d61e4a59f7bc1b2478aa6c71c345a] Build map part 13
running  '../test.sh'
Bisecting: 0 revisions left to test after this (roughly 1 step)
[27952d82d1f9067948b37e39b9bc4d0e6d846567] Build map part 14
running  '../test.sh'
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[83076f97e8312f8d59976c6373c897aef284c26d] Bugs...
running  '../test.sh'
83076f97e8312f8d59976c6373c897aef284c26d is the first bad commit
commit 83076f97e8312f8d59976c6373c897aef284c26d
Author: HAL 9000 <aske.olsson@switch-gears.dk>
Date:   Tue May 13 09:53:45 2014 +0200

    Bugs...

 map.txt | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
bisect found first bad commit
$ git bisect reset
Previous HEAD position was 83076f9 Bugs...
Switched to branch 'bug_hunting'
Your branch is up to date with 'origin/bug_hunting'.
$ git show 83076f9
# ... (omitted)

11.4 使用 git blame 命令

上一节的 git bisect 适用于只知道项目有 bug,但不知道 bug 在哪儿、是具体哪次提交引入的场景。如果知道了 bug 在代码中的位置,需要明确谁提交或改动的这行代码,就需要使用 git blame 命令。

继续以 Git-Version-Control-Cookbook-Second-Edition_tips_and_tricks 库为例:

$ git blame --date short -L 30,47 .\map.txt
7fb98059 (Dave Bowman      2014-05-13 30) Australia:
33016136 (Frank Poole      2014-05-13 31)
33016136 (Frank Poole      2014-05-13 32)                     _,__        .:
33016136 (Frank Poole      2014-05-13 33)             Darwin <*  /        | \
5a5f6ef7 (Frank Poole      2014-05-13 34)                .-./     |.     :  :,
5a5f6ef7 (Frank Poole      2014-05-13 35)               /           '-._/     \_
5a5f6ef7 (Frank Poole      2014-05-13 36)              /                '       \
83076f97 (HAL 9000         2014-05-13 37)            .'        \__/             *: Brisbane
83076f97 (HAL 9000         2014-05-13 38)         .-'          (oo)               ;
83076f97 (HAL 9000         2014-05-13 39)         |           //||\\              |
27952d82 (Dave Bowman      2014-05-13 40)         \                              /
27952d82 (Dave Bowman      2014-05-13 41)          |                            /
27952d82 (Dave Bowman      2014-05-13 42)    Perth  \*        __.--._          /
de6b768c (Heywood R. Floyd 2014-05-13 43)            \     _.'       \:.       |
de6b768c (Heywood R. Floyd 2014-05-13 44)            >__,-'             \_/*_.-'
de6b768c (Heywood R. Floyd 2014-05-13 45)                                  Melbourne
9b49c0f0 (Heywood R. Floyd 2014-05-13 46)         snd                     :--,
9b49c0f0 (Heywood R. Floyd 2014-05-13 47)                                  '/

其中的 -L 表示限定考察代码的行数范围,格式为 -L <from>,<to> 。从反馈的结果可知,bug 出现在 map.txt 文件的第 37 至 39 行,由 HAL 首次引入该 bug,对应 SHA-183076f97,与 git bisect 结果一致。

此外,还可以指定 -M 参数,查看文件被重构或移动到某处的情况;指定 -C 参数,则可以展示目标文件从当前 commit 包含的文件中复制或移入相关代码的情况;指定 -CCC 则范围不仅限于当前 commit,可包含所有 commit

  • 21
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安冬的码畜日常

您的鼓励是我持续优质内容的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值