【Git教程】(十五)二分法排错 — 概述及使用要求,执行过程及其实现(用二分法人工排错或自动排错),替代解决方案 ~

Git教程 · 二分法排错

  • 1️⃣ 概述
  • 2️⃣ 使用要求
  • 3️⃣ 执行过程及其实现
      • 3.1 用二分法人工排错
      • 3.2 用二分法自动排错
  • 4️⃣ 替代解决方案

在这里插入图片描述

在开发过程中,我们经常会突然遇到一个错误,是之前早期版本在成功通过测试时没有出现过的。这时候,时下较被看好的调试策略是先搜索出我们第一次发现错误时所在的提交。 由于在使用 Git 开展工作时,我们往往会产生许多小型提交,因此可以通过分析其中的变化来快速查找错误的成因。

Git 支持用二分法来搜索引发问题的提交。
二分法是基于二分搜索的一种查找方法。查找的起点是已确认没问题的提交,终点是已明确有错误的提交,两者之间这段提交历史将会被“分半”,位于“中间”的提交会在工作区 中被激活。然后我们会对被激活的提交进行错误检查。接着,再根据是否在被激活提交中找到该错误的情况,再对错误必然会隐藏的那段剩余的提交历史进行重新“分半”,并检查新的 “中间”提交。如此反复,我们最终就会找到第一次出现该错误的提交。在该工作流中,我们会为你演示以下操作。

  • 如何有效地用二分法找出引发问题的提交。
  • 如何用二分法实现自动化排错。

1️⃣ 概述

在下图中,我们将会看到一段提交历史,其中有一个确认无误的提交和已明确出了问题的提交。虽然提交历史并不非得要线性发展,但在出了问题的提交到没有问题的提交之间必须要有一条路径,以说明它们之间的父系关系。

当二分查找进程被启动之后, Git 就会在相关的提交历史的中间位置选择一个合适的提交。该提交将会被执行某种人工测试或脚本测试,然后根据其结果被标记为“good”或“had”。 接着,该二分查找任务会去挑选另一个提交对象,对其进行测试并标记。这个进程会一直重复该动作,直至找到其直系父提交中没有错误的那次提交。

在这里插入图片描述


2️⃣ 使用要求

  • 可重现的错误检测:我们必须要证明相关错误行为的一致性。也就是说,我们要能清楚地识别一个版本是正确还是不正确。对于自动化错误,无论它采用的是测试用例还是脚本,它都必须要能检测到错误。
  • 误差检测的成本不能太高:误差检测必须即快速又便宜。使用二分法进行多故障检测的成本取决于我们要测验的提交数量。如果其需要的时间过长或成本过高,对错误的成因来一次分析性搜索显然会更有效率。

3️⃣ 执行过程及其实现

在开发过程中,我们经常会遇到之前版本中不曾出现过的错误。二分法可以帮助我们在提交历史中定位那个包含错误的提交。

为了演示接下来的这些操作,我们做了一个小型的示范性项目。在该项目中,我们实现了各种数学函数。其中值得一提的是一个计算阶乘的功能。该功能会以列表的形式返回从1到5所有数的阶乘。

> java FactorialMain
Factorial of 1 = 1
Factorial of 2 = 1
Factorial of 3 = 2
Factorial of 4 = 6
Factorial of 5 = 24

3.1 用二分法人工排错

首先,我们要对二分查找的基本过程有个交代,以说明在该测试中所要人工查找的错误成因。

  • 第1步:定义错误标志
    一般情况下,错误往往都是由开发者、测试者或者用户发现的。我们的第一步是要对该错误进行分析和理解,找出该错误的某种标志。
    下面我们来看几个错误标志的例子。

    • 当某个动作或函数调用引发某种异常时,该程序就会被取消执行或显示错误消息。
    • 某个函数返回了包含错误结果的信息项。
    • 某个测试用例执行失败。

    具体到我们这个例子中,3的阶乘可以被视为是一个标志,代表它出错了。
    如你所见,多数情况下我们用单凭分析就足以发现问题的成因了,无须进行二分查找。

  • 第2步:分别找出没问题的和有问题的提交
    该二分查找过程需要我们提供一个没问题的提交和一个出了错的提交。 一个不错的选择是我们可以用最新发行版或者最新历程碑来充当那个确认无误的提交。
    如果我们发现被选中来充当没有问题的提交中也包含了该错误,那就必须去回溯更久远的历史了。
    由于相关错误的信息已经被上报,我们要想找到一个问题提交并不难,但如果想要在一堆没有问题的提交中搜索更多问题提交,我们就务必要找出那个最古老的问题提交了。
    下面是上述例子的日志输出,我们来看看它的提交历史。

    > git log --oneline
    202d25d modulo finished
    e36fead multiply finished
    918ed2f sub finished
    ebe74ld add finished
    87ac59e ComputeFactorial finished
    39cbdc0 init
    

    分析结果表明,提交 87ac59e ComputerFactorial finished 应该是没有问题的,出错的应该是提交202d25d modulo finished

  • 第3步:执行二分法排错
    现在,既然我们已经将错误局限在了提交历史一个区间内,就可以开始用二分查找来进行实际的错误搜索了。
    我们可以通过bisect start 命令来开始二分查找。在这里,我们必须要将问题提交指定为第一个参数,而没有问题的提交则是第二个参数。

    > git bisect start 202d25d 87ac59e
    Bisecting: 1 revision left to test after this(roughly 1 step)
    [918ed2f29a44e468d690fb770aablad2dbaela5a]sub finished
    

    bisect start 命令会将第一个提交标志成 “bad” 提交,第二个则标志为 “good” 提交。 然后接下来,位于这两个提交之间的那个提交(具体到我们的例子中就是提交 918ed2f sub finished) 会被激活。
    现在,工作区中包含了来自某个提交的文件,我们还不不能确定它有没有出问题。由于我们之前已经找到了该错误的标志,该版本的状态目前是可以被测试的。

    > java FactorialMain
    Factorial of  1 =1
    Factorial of  2 =1
    Factorial of  3 =2
    Factorial of  4 =6
    Factorial of  5 =24
    

    但从在工作区中运行 FactorialMain 的结果表明,该错误仍然存在于其中,这意味着当前提交依然是有问题的。
    现在,我们用以下命令中的一个对当前提交进行标志。

    • bisect good: 错误不在其中,该提交确认无误。
    • bisect bad: 错误就在其中,该提交有问题。
    • bisect skip: 当前提交无法被测试。 一般是因为没有被编译或缺失了一些文件,这时 候二分查找进程就会去激活另一个提交来测试。
      在我们的例子中,由于错误还存在于该提交中,所以我们会将其标志为 “bad” 提交。
    >git bisect bad
    Bisecting: 0 revisions  left  to  test  after  this(roughly  0  steps) 
    [ebe741de3366a3fc08fbedfdfa408517dd172ca3]add finished
    

    在 Git 的响应报告中,我们看到目前被激活的是提交 ebe741d add finished。此外, Git 还报告说这是它必须要测试的最后一个提交。
    我们对FactorialComputer 的重新测试表明,该提交中是确认无误的,因此被标志为 “good”提交。

    > git bisect good
    commit 918ed2f29a44e468d690fb770aablad2dbaela5a
    Author:Rene Preissel <rp@eToSquare.de>
    Date: Fri Jun 2408:04:432011 +0200
    	sub finished
    :040000 040000 0e5bfb07e859072a564eaca07346le4a12a0ed61  \
    329e7f864bac874c69be4531452c753cf56be794 M    src
    

    现在,Git 告诉我们提交918ed2f sub finished 才是该错误第一出现的地方。我们现在可以用Git 命令来分析该提交做了哪些修改了(例如git show 918ed2f)。
    最后,我们发现这个例子中阶乘计算只能计算到n-1。
    请注意,在我们启动排错过程之前,必须要将工作区重新设置到当前分支的HEAD 上。
    关于这一点我们将会在下一步骤中做说明。

  • 第4步:停止或取消二分查找
    在成功分析出错误根源,或者决定取消某个二分查找之后,我们还必须要用 bisect reset 命令将工作区中的内容重置回正常的开发版本。

    > git  bisect  reset
    Previous HEAD position was ebe74ld...add finished
    Switched to branch 'master'
    

3.2 用二分法自动排错

在之前的操作序列中,我们测试的是某个提交中是否包含了某个错误,用的是人工测试。

如果我们连对的一个很长历史的区间或者人工测试的成本非常高昂,也可以通过一段脚本来进行自动化测试,让二分查找算法自己去完成它的工作。

  • 第1步:定义错误标志
    错误标志的的方法与人工的二分法排错一样,我们只要确保这些错误标志能被脚本自动检测到即可。

  • 第2步:准备好测试脚本
    如果我们想要进行自动化的二分法排错,就必须要提供一段 shell 脚本。为了能实现错误标志的自动检测,这段shell 脚就本必须要根据指定错误是否存在的情况返回以下不同的退出码。

    • Exit code 0: 表示没有找到错误,二分查找进程应该会将该提交标志为“good”。
    • Exit codes 1-124,126,127 : 表示错误被找到,二分查找进程应该会将该提交标志为“bad”。
    • Exit code 125: 表示测试由于程序可行性的原因没被执行。 一般情况下,是该版本无法被编译。二分查找过程会直接跳过该提交。

    在这里,我们的计算器应用是用Java 编写的。下面我们就将其作为一个例子来演示一下 如何在这类环境中对自动化二分查找进程进行调试。对于其他的开发环境,这段独立的脚本通常要做些相应的调整。

    事实上,我们这段自动化错误检验是通过 JUnit 测试来执行的 ( 你可以从 http://wwwjunit.org 网站上下载到JUnit) 。 它只负责检测3阶乘是否真的是6。如果返回结果为 false, 即视为测试失败。

    public class FactorialBisectTest {
    	@Test
    	public void testFactorial(){
    		long result = Computer.factorial(3);
    		Assert.assertEquals(6,result);
    	}
    }
    

    特别提醒:不要忘记该测试要在一个新文件中实现,它不应该被 Git 纳入版本控制。在 二分查找进程中,工作区中会有不同的提交被激活,而且是一个接一个地进行。如果该测试文件也处于 Git 的控制之下,它在旧提交被激活时就不存在了。而且从另一方面来说,非版本文件也应该被抽离在工作区的修改之外。

    另外,自动化的二分查找进程需要我们提供一段 shell 脚本。该 shell 脚本首先必须能编 译我们的Java 源文件,然后再启动test.Ant, 将其用作本例中的构建系统。在该计算机项目中,

    我看可以通过一个名为 build.xml 的构建文件来执行一次纯净的构建过程 (ant clean compile)。另外为了执行二分查找测试,我们还需要另一个名为bisect-build.xml 的构建文件, 它只提供了一个用于启动测试的 target。再次提醒,该文件不能被 Git 纳入版本控制。

    <target name="test">
    	<junit>
    		<classpath refid= "build.classpath" />
    		<test name= "FakultaetsBisectTest" haltonerror="true" haltonfailure="true" />
    	</junit> 
    </target>
    

    如果我们想访问不同的 Ant target, 就要有一个名为 bisect-test.sh 的 shell 脚本,这个脚本也不能被 Git 纳入版本控制。

    #!/bin/bash
    ant clean compile
    if [ $? -ne 0]; then
    	exit 125;
    fi
    ant -f bisect-build.xml
    if [ $? -ne 0]; then
    	exit 1;
    else
    	exit 0;
    fi
    

    该脚本会去调用构建文件中的各种构建 target,并检测 Ant 的退出码。测试失败时 Ant 会返回一个大于0的退出码。我们需要将其转换成二分查找进程所需要返回的代码。

    • 如果构建失败,就返回退出码125。
    • 如果测试成功,就返回退出码0。
    • 如果测试失败,就返回退出码1。
  • 第3步:分别找出没问题的和有问题的提交
    在对没问题和有问题提交的搜索方面,这里的流程和人工的过程并没有什么不同。但是, 你也可以用 JUnit 测试来检查错误。举例来说,我们选择提交 87ac59e FactorialCompute finished 来验证一下它确实是没有问题的。

    > git checkout 87ac59e
    > ant -f bisect-build.xml
    Buildfile:bisect-build.xml
    test:
    BUILD SUCCESSFUL
    Total    time:0    seconds
    

    特别提醒: 在完成上述过程之后,请不要忘记将master分支设置成当前活跃分支。

    > git  checkout  master
    
  • 第4步:执行二分法的自动化排错
    在使用自动化排错时,第一次二分查找进程也得要用bisect start 命令来启动。另外,我们还需要将有问题的提交指定为第一参数,没问题的提交为第二参数传递给该命令。

    > git bisect start 202d25d 87ac59e
    Bisecting:1 revision left to test  after this(Roughly 1 step)
    [918ed2f29a44e468d690fb770aablad2dbaela5a]sub finished
    

    然后在用 bisect run命令来执行名为 bisect-test.sh 的 shell 脚本。

    > git bisect run ./bisect-test.sh
    

    下面我们将输出截断,只显示bisect run 命令的最后几行内容。你会很高兴地看到该命令找到了 918ed2f sub finished 是第一个出错的提交。

    ..
    Buildfile:bisect-build.xml
    test:
    
    BUILD SUCCESSFUL
    Total time:0 seconds
    918ed2f29a44e468d690fb770aablad2dbaela5a is the first bad commit
    commit 918ed2f29a44e468d690fb770aablad2dbaela5a
    Author:Rene Preissel <rp@eToSquare.de>
    Date: Fri Jun 2408:04:432011 +0200
    	sub finished
    :040000 040000 Oe5bfb07e859072a564eaca07346le4a12a0ed61 \
    329e7f864bac874c69be4531452c753cf56be794 M   src
    bisect run success
    
  • 第5步:完成二分查找操作
    在成功完成排错之后,我们还必须要用bisect reset 命令来结束整个二分查找进程。

    > git bisect reset
    Previous HEAD position was ebe741d...add finished
    Switched to branch 'master!
    

4️⃣ 替代解决方案

用合并操作将测试脚本添加到旧提交中去
上面这个过程的优势在于 Git 在激活新提交的时候将一些未被版本化的文件留在了工作区中。这样一来,我们在旧提交中也可以执行这些“新”的测试脚本了。
当然,我们也可以采用另一种解决方案,就是将测试脚本纳入到一个新分支中(见下图中的 bisect-test 分支)。

在该二分查找的 shell 脚本中,二分查找进程会在每次测试运行之前将bisect-test 分支合并到当前提交中。然后用--nocommit 选项防止其变成一个永久性的提交。

然后待测试完成之后,再用 reset 命令重置掉合并操作所带来的修改。这个操作序列和示例脚本可以在 bisect 命令的在线文档的 Example一节中找到。

这个使用 bisect-test 分支的解决方案不仅可以在我们拥有一个测试用例和新增一个新的测试脚本时发挥作用。也可以用于测试必须要适应现有代码的,例如可能是因为测试中某种审核需要访问的数据在旧提交是不可见的。
但在大多数情况下,我们之前所描述的非版本化文件的方案已经够用了,而且它实现起来相对要更容易一些。



温习回顾上一篇(点击跳转)
《【Git教程】(十四)基于特性分支的开发 — 概述及使用要求,执行过程及其实现,替代方案 ~》

继续阅读下一篇(点击跳转)
《》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/575514.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于实现地图弹窗轮播功能及遇到的问题解决

基本使用 获取地图 geojson 数据 链接&#xff1a; 阿里云数据可视化平台 获取ECharts npm install echarts 或者是使用地址链接 <script src"https://registry.npmmirror.com/echarts/5.4.3/files/dist/echarts.min.js"></script> <script src…

关于螺栓的注意事项和正确操作方法——SunTorque智能扭矩系统

智能扭矩系统-智能拧紧系统-扭矩自动控制系统-SunTorque 螺栓&#xff0c;作为一种常见的紧固件&#xff0c;广泛应用于各种机械设备和结构中。在日常生活和工作中&#xff0c;我们经常需要接触到螺栓&#xff0c;因此了解螺栓的一些注意事项和正确操作方法对于确保设备的安全…

【C#】Stopwatch计时器

使用Stopwatch检查C#中代码块的执行时间&#xff0c;比如歌曲&#xff0c;图片的下载时间问题 首先&#xff0c;我们可看到Stopwatch 类内部的函数。 根据需求&#xff0c;我们具体可使用到 Start() 开始计时&#xff0c;Stop() 停止计时等 //创建 Stopwatch 实例 Stopwatch …

Intersection Observer API探索

我们经常遇到这样的需求——检测一个元素是否可见或者两个元素是否相交&#xff0c;如 ● 图片懒加载——当图片滚动到可见时才进行加载 ● 内容无限滚动——也就是用户滚动到接近内容底部时直接加载更多&#xff0c;而无需用户操作翻页&#xff0c;给用户一种网页可以无限滚动…

分布式密钥生成

可验证且无经销商 分布式密钥生成 (DKG) 是一种加密协议&#xff0c;使多方能够协作生成共享密钥&#xff0c;而无需任何一方完全了解密钥。 它通过在多个参与者之间分配信任来增强各种应用程序的安全性&#xff0c;从而降低密钥泄露的风险。 我们引入了一种可验证且无经销商的…

深度学习从入门到精通—Transformer

1.绪论介绍 1.1 传统的RNN网络 传统的RNN&#xff08;递归神经网络&#xff09;主要存在以下几个问题&#xff1a; 梯度消失和梯度爆炸&#xff1a;这是RNN最主要的问题。由于序列的长距离依赖&#xff0c;当错误通过层传播时&#xff0c;梯度可以变得非常小&#xff08;消失…

mybatisplus3.5.4基础生成代码完整步骤(超详细)

在网上看了很多自动生成的例子本地不是很好使&#xff0c;最后找到了一套好用的&#xff0c;适合版本&#xff1a; idea:2024.1 springboot2.6.12 java17 mybatisplus3.5.4 废话不多说&#xff0c;直接上步骤&#xff1a; 新建项目&#xff1a; 结构如下&#xff1a; 添加依…

Calibre Ebook Management 一款功能强大操作简单的电子书管理神器(V7.9.0)

Calibre Ebook Management Calibre 软件被广泛认可为一款功能全面并且用户友好的电子书管理工具。其卓越性能和必备性得到了用户的高度评价。该软件提供了一套综合性的功能&#xff0c;允许用户执行各种电子书相关的操作。此外&#xff0c;Calibre 软件遵循免费和开源的原则&a…

spring基本使用

文章目录 1. ioc(Inversion of Control) | DI(Dependency Injection)(1) maven坐标导包(2) 编写配置文件bean.xml(3) 配置bean(4) 配置文件注入属性 2. DI(dependency injection) 依赖注入(setter)其他属性(1) 对象属性注入(2) 数组属性输入(3) 集合属性注入(4) map集合注入(5)…

【一刷《剑指Offer》】面试题 9:斐波那契数列(扩展:青蛙跳台阶、矩阵覆盖)

力扣对应链接&#xff1a;LCR 126. 斐波那契数 - 力扣&#xff08;LeetCode&#xff09; 牛客对应链接&#xff1a;斐波那契数列_牛客题霸_牛客网 (nowcoder.com) 核心考点&#xff1a;空间复杂度&#xff0c;fib 理解&#xff0c;剪枝重复计算。 一、《剑指Offer》内容 二、分…

ThingsBoard处理设备上报的属性并转换为可读属性

一、前言 二、案例 1、AI生成JSON数据体 2、将json数据体直接通过遥测topic发送查看效果 3、可查看目前整个数据都在一起 ​编辑 4、配置附规则链路 5、对msg的消息值&#xff0c;进行数据的转换&#xff0c;并从新进行赋值。 6、规则链路关联关系 7、再次通过MQTT发送遥…

618大促有哪些值得买的家居好物?618五款必Buy好物

来了&#xff01;来了&#xff01;万众瞩目的618购物狂欢节即将拉开帷幕&#xff0c;我们的目标清晰而坚定&#xff0c;那就是用最实惠的价格尽情享受购物的乐趣。然而&#xff0c;面对各种纷繁复杂的促销活动和琳琅满目的商品&#xff0c;选择困难症似乎也在悄然滋生。因此&am…

【自定义渲染通道】

自定义渲染通道 2023-09-07 14:58 How to Create Masks With the Custom Depth Buffer Tips - Tricks Unreal Engine.mp4 后期材质ppm_customDepth 要加入通道的物体设置 render customdepth pass postprocessvolue 设置post process materials 为上面的ppm_customDepth 不同…

【信安评估】2024年全国职业院校技能大赛高职组“信息安全管理与评估”安徽省选拔赛赛项规程

培训、环境、资料、考证 公众号&#xff1a;Geek极安云科 网络安全群&#xff1a;624032112 网络系统管理群&#xff1a;223627079 网络建设与运维群&#xff1a;870959784 移动应用开发群&#xff1a;548238632 极安云科专注于技能提升&#xff0c;赋能 2024年广东省高校的技…

PLL深度解析第一篇——PLL的知识图谱

在硬件电路中&#xff0c;时钟就像心脏一样&#xff0c;在时钟的节拍下&#xff0c;不同的芯片、不同的电路、不同的接口都可以有序的进行工作或者通信&#xff08;类似流水线一样&#xff0c;必须有节奏的运行&#xff09;。 但是在芯片中&#xff0c;不同的模块和接口工作的频…

基于SSM的物业管理系统(含源码+sql+视频导入教程+文档+PPT)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的物业管理系统2拥有三种角色 管理员&#xff1a;用户管理、物业管理、房产信息管理、小区概况管理、开发商管理、收费标准管理、物业公司管理等 物业&#xff1a;住户管理、收费…

C语言求 MD5 值

MD5值常被用于验证数据的完整性&#xff0c;嵌入式开发时经常用到。md5sum命令可以求MD5码&#xff0c;下面介绍如何用C语言实现MD5功能。 一、求字符串MD5值 1、md5sum命令 $ echo -n "12345678" | md5sum //获取"12345678"字符串的md5值 结果&…

1小时学会SpringBoot3+Vue3前后端分离开发

首发于Enaium的个人博客 引言 大家可能刚学会Java和Vue之后都会想下一步是什么&#xff1f;那么就先把SpringBoot和Vue结合起来&#xff0c;做一个前后端分离的项目吧。 准备工作 首先你需要懂得Java和Vue的基础知识&#xff0c;环境这里就不多说了&#xff0c;直接开始。 …

Neo-reGeorg明文流量

Neo-reGeorg 1 同IP对&#xff0c;同一个URI&#xff0c;第一个TCP流是“GET”请求&#xff0c;随后的TCP流请求为“POST”。&#xff08;jsp\jspx\php&#xff09; 2 第一个TCP流中&#xff0c;GET只有一个会话。&#xff08;jsp\jspx\php&#xff09;&#xff0c;响应body79…

stm32HAL库-GPIO

一 什么是 GPIO: GPIO(general porpose intput output), 通用输入输出端口 . 二 我们先认识芯片控制 GPIO 输出控制。 2.1LED 硬件原理如图&#xff1a; 当电流从这根电线流通&#xff0c; LED 亮。当电流不通过这根电线&#xff0c; LED 灭。 上面 PF** &#xff0c;芯片电…