git bisect

优质
小牛编辑
131浏览
2023-12-01

名称

git-bisect - 使用二进制搜索来查找引入错误的提交

概要

git bisect <subcommand> <options>

描述

该命令根据子命令采用各种子命令和不同的选项:

git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]          [--no-checkout] [<bad> [<good>...]] [--] [<paths>...]git bisect (bad|new|<term-new>) [<rev>]git bisect (good|old|<term-old>) [<rev>...]git bisect terms [--term-good | --term-bad]git bisect skip [(<rev>|<range>)...]git bisect reset [<commit>]git bisect visualize
git bisect replay <logfile>git bisect log
git bisect run <cmd>...git bisect help

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

事实上,git bisect可以用来发现改变提交的任何项目的财产; 例如,修正错误的提交或导致基准性能提高的提交。为了支持这种更一般的用法,可以使用术语“旧”和“新”来代替“好”和“坏”,或者可以选择自己的术语。有关更多信息,请参见下面的“替代条款”部分。

基本平分命令:开始,坏,好

作为一个例子,假设您正在尝试查找违反已知可在v2.6.13-rc2您的项目版本中工作的功能的提交。你开始平分会议如下:

$ git bisect start
$ git bisect bad                 # Current version is bad
$ git bisect good v2.6.13-rc2    # v2.6.13-rc2 is known to be good

一旦你指定了至少一个坏的和一个好的提交,git bisect在该历史范围的中间选择一个提交,检查出来,并输出类似于以下内容的提交:

Bisecting: 675 revisions left to test after this (roughly 10 steps)

您现在应该编译检出版本并对其进行测试。如果该版本正常工作,请键入

$ git bisect good

如果该版本已损坏,请键入

$ git bisect bad

然后git bisect会用类似的回应

Bisecting: 337 revisions left to test after this (roughly 9 steps)

继续重复这个过程:编译树,测试它,并根据它是好还是坏运行git bisect goodgit bisect bad询问下一个需要测试的提交。

最终将不会有更多的修订留下来检查,并且该命令将打印出第一个错误提交的描述。该引用refs/bisect/bad将指向该提交。

平分复位

在平分会话之后,要清理平分状态并返回到原始 HEAD,请发出以下命令:

$ git bisect reset

默认情况下,这会将您的树返回到之前签出的提交git bisect start。(一个新的git bisect start也会这样做,因为它清理了旧的二分法状态。)

使用可选参数,您可以返回到其他提交:

$ git bisect reset <commit>

例如,git bisect reset bisect/bad将检查第一个错误的修订,同时git bisect reset HEAD会让您处于当前的平分提交并避免切换提交。

替代条款

有时候你并不是在寻找导致破坏的提交,而是寻找导致其他“旧”状态和“新”状态之间发生变化的提交。例如,您可能正在寻找引入特定修补程序的提交。或者您可能正在寻找第一次提交,其中源代码文件名最终全部转换为贵公司的命名标准。管他呢。

在这种情况下,使用“好”和“坏”这两个术语来指代“改变前的状态”和“改变后的状态”可能会非常混乱。相反,您可以分别使用术语“旧”和“新”来代替“好”和“坏”。(但请注意,您不能在单个会话中将“好”和“坏”与“旧”和“新”混合在一起。)

在这个更通用的用法中,你提供git bisect了一个“新”提交,它有一些属性和一个没有该属性的“旧”提交。每次git bisect检出提交时,都会测试该提交是否具有该属性。如果是,则将提交标记为“新”; 否则,将其标记为“旧”。当平分完成后,git bisect将报告哪个提交引入了属性。

要使用“old”和“new”而不是“good”和 bad,你必须运行git bisect start没有提交作为参数,然后运行以下命令来添加提交:

git bisect old [<rev>]

表明某个提交是在寻求更改之前进行的,或者

git bisect new [<rev>...]

以表明它是在之后。

要获得当前使用条款的提醒,请使用

git bisect terms

你可以用git bisect term --term-oldor git bisect term --term-good来获得旧的(分别是新的)术语。

如果你想用你自己的条件,而不是“坏” /“好”或“新” /“老”,你可以选择你喜欢的任何名称(除现有对开子一样resetstart通过使用开始平分,...)

git bisect start --term-old <term-old> --term-new <term-new>

例如,如果您正在寻找引入性能回归的提交,则可以使用

git bisect start --term-old fast --term-new slow

或者,如果您正在寻找修复错误的提交,则可以使用

git bisect start --term-new fixed --term-old broken

然后,使用git bisect <term-old>git bisect <term-new>代替git bisect goodgit bisect bad标记提交。

平分可视化

要查看当前剩余的可疑对象gitk,请在对分过程中发出以下命令:

$ git bisect visualize

view也可以用作同义词visualize

如果DISPLAY没有设置环境变量,git log则用来代替。您还可以提供诸如-p--stat的命令行选项。

$ git bisect view --stat

平分日志和平分重播

在将修订标记为好或不好之后,发出以下命令以显示迄今为止所做的工作:

$ git bisect log

如果您发现在指定修订状态时出错,可以将此命令的输出保存到文件中,对其进行编辑以删除不正确的条目,然后发出以下命令以返回到更正的状态:

$ git bisect reset
$ git bisect replay that-file

避免测试提交

如果在平分会话中,您知道建议的修订版不适合测试(例如,它无法构建并且您知道失败与您正在追踪的错误没有任何关系),那么您可以手动选择一个附近的提交并测试那个。

例如:

$ git bisect good/bad                        # previous round was good or bad.Bisecting: 337 revisions left to test after this (roughly 9 steps)$ git bisect visualize                        # oops, that is uninteresting.$ git reset --hard HEAD~3                # try 3 revisions before what
                                        # was suggested

然后编译并测试所选修订版,然后以通常方式将修订版标记为好或不好。

平分跳过

您可以通过发出以下命令来请求 Git 为您执行:

$ git bisect skip                 # Current version cannot be tested

但是,如果您跳过与您正在寻找的相邻的提交,那么 Git 将无法准确地确定哪些提交是第一个不合格提交。

您也可以使用范围表示法跳过一系列提交,而不是仅提交一次提交。例如:

$ git bisect skip v2.5..v2.6

这告诉平分过程,在测试之后v2.5,直到并包括v2.6,都不应该进行提交。

请注意,如果您还想跳过该范围的第一次提交,则可以发出以下命令:

$ git bisect skip v2.5 v2.5..v2.6

这表明平分过程,应该跳过v2.5v2.6(包括)之间的提交。

通过提供更多的参数来平分开始,减少平分

如果通过发出bisect start命令时指定路径参数,您可以进一步减少试验次数,如果您知道您要追踪的问题涉及哪部分树,请执行以下操作:

$ git bisect start -- arch/i386 include/asm-i386

如果事先知道多个好提交,则可以在发出bisect start命令时通过在错误提交之后立即指定所有好提交来缩小对分空间:

$ git bisect start v2.6.20-rc6 v2.6.20-rc4 v2.6.20-rc1 --
                   # v2.6.20-rc6 is bad
                   # v2.6.20-rc4 and v2.6.20-rc1 are good

平分运行

如果您有一个脚本可以确定当前源代码是好还是坏,可以通过发出以下命令来平分:

$ git bisect run my_script arguments

注意,my_script如果当前源代码是好的/旧的,脚本(在上面的例子中)应该以代码0退出,并且以1和127之间的代码(包括125)退出,除非125,如果当前源代码是坏的/新的。

任何其他退出代码将中止对分过程。应该指出的是,一个程序,通过exit(-1) leaves $? = 255,(参见出口(3)手册页),因为值被& 0377切断。

当不能测试当前源代码时,应使用特殊退出码125。如果脚本以此代码退出,则当前修订将被跳过(参见git bisect skip上文)。125被选为用于此目的的最高敏感值,因为 POSIX shell 使用126和127来表示特定的错误状态(127表示找不到命令,126表示找到的命令但不可执行 - 这些细节不会问题,因为它们在脚本中是正常的错误,就其bisect run而言)。

你可能经常会发现,在平分会话期间,你希望进行临时修改(例如,在头文件中s/#define DEBUG 0/#define DEBUG 1/,或者“没有这个提交的修订版本需要这个补丁应用于解决方法另一个问题是这种平分不感兴趣“)适用于正在测试的版本。

为了应对这种情况,在内部git bisect发现下一个修订版测试之后,脚本可以在编译之前应用修补程序,运行真实测试,然后确定修订版(可能包含所需修补程序)是否通过测试,然后倒带树到原始状态。最后,脚本应该以真实测试的状态退出,让git bisect run命令循环确定对分会话的最终结果。

选项

--no-checkout

在平分过程的每次迭代中,不要签出新的工作树。相反,只需更新一个特定的引用BISECT_HEAD,使其指向应该测试的提交。

当您在每个步骤中执行的测试不需要检出树时,此选项可能很有用。

假设--no-checkout,如果存储库是裸露的。

示例

  • 在v1.2和HEAD之间自动平分一个破损的构建:$ git bisect start HEAD v1.2 - #HEAD不好,v1.2很好$ git bisect run make#“make”构建应用程序$ git bisect reset#quit平分会议
  • 自动平分原点和 HEAD 之间的测试失败:

$ git bisect start HEAD origin -- # HEAD is bad, origin is good $ git bisect run make test # "make test" builds and tests $ git bisect reset # quit the bisect session

  • Automatically bisect a broken test case: $ cat ~/test.sh #!/bin/sh make || exit 125 # this skips broken builds ~/check_test_case.sh # does the test case pass? $ git bisect start HEAD HEAD~10 -- # culprit is among the last 10 $ git bisect run ~/test.sh $ git bisect reset # quit the bisect sessionHere we use a test.sh custom script. In this script, if make fails, we skip the current commit. check_test_case.sh should exit 0 if the test case passes, and exit 1 otherwise. It is safer if both test.sh and check_test_case.sh are outside the repository to prevent interactions between the bisect, make and test processes and the scripts.
  • 通过临时修改自动平分(热修复):

$ cat ~/test.sh #!/bin/sh # tweak the working tree by merging the hot-fix branch # and then attempt a build if git merge --no-commit hot-fix && make then # run project specific test and report its status ~/check_test_case.sh status=$? else # tell the caller this is untestable status=125 fi # undo the tweak to allow clean flipping to the next commit git reset --hard # return control exit $status

这适用于在每次测试运行之前从热修复分支进行的修改,例如,如果您的构建或测试环境发生变化,以便旧版本可能需要修复哪些新版本已经修复。(确保烫分支是基于关闭提交其包含在你平分所有修订,使合并没有太多拉,或使用git cherry-pick替代git merge。)

  • 自动对分破损的测试用例:$ git bisect start HEAD HEAD〜10 - #ctrprit是最后10个$ git bisect运行sh -c“make || exit 125;〜/ check_test_case.sh”$ git bisect reset#quit平分会话这表明,如果您在单行上编写测试,则可以不使用运行脚本。
  • 在损坏的存储库中找到对象图的一个好区域

$ git bisect start HEAD <known-good-commit> <boundary-commit> ... --no-checkout $ git bisect run sh -c ' GOOD=$(git for-each-ref "--format=%(objectname)" refs/bisect/good-*) && git rev-list --objects BISECT_HEAD --not $GOOD >tmp.$$ && git pack-objects --stdout >/dev/null <tmp.$$ rc=$? rm -f tmp.$$ test $rc = 0' $ git bisect reset # quit the bisect session

在这种情况下,当git bisect run完成时,bisect / bad 将引用一个提交,该提交至少有一个可达图git pack objects可完全遍历的父对象。

  • 在代码中寻找修复而不是回归

$ git bisect start $ git bisect new HEAD # current commit is marked as new $ git bisect old HEAD~10 # the tenth commit from now is marked as old

or:

$ git bisect start --term-old broken --term-new fixed$ git bisect fixed
$ git bisect broken HEAD~10

获得帮助

使用git bisect得到一个简短的使用说明,以及git bisect helpgit bisect -h获得长期的使用说明。