如何使用git bisect?

2mbi3lxu  于 2024-01-04  发布在  Git
关注(0)|答案(7)|浏览(145)

我看过一些文章说git bisect很棒,但是我不明白为什么它很棒。
1.我该如何使用它?
1.是不是跟svn blame一样?

ktca8awb

ktca8awb1#

git bisect背后的想法是在历史中执行二进制搜索以找到特定的回归。假设您有以下开发历史:

... --- 0 --- 1 --- 2 --- 3 --- 4* --- 5 --- current

字符串
你知道你的程序在current版本下不能正常工作,而在0版本下工作。所以回归可能是在12345current中的一个提交中引入的。
你可以尝试检查每个提交,构建它,检查是否存在回归。如果有大量的提交,这可能需要很长时间。这是一个线性搜索。我们可以通过二进制搜索做得更好。这就是git bisect命令所做的。在每一步,它都会尝试将潜在错误的修订数量减少一半。
您将像这样使用命令:

$ git stash save
$ git bisect start
$ git bisect bad
$ git bisect good 0
Bisecting: 2 revisions left to test after this (roughly 2 steps)
[< ... sha ... >] 3


在这个命令之后,git将 checkout 一个提交。在我们的例子中,它将是commit 3。你需要构建你的程序,并检查回归是否存在。你还需要告诉git这个版本的状态,如果回归存在,就用git bisect bad,如果不存在,就用git bisect good
让我们假设回归是在提交4中引入的,那么回归在这个版本中不存在,我们告诉git

$ make
$ make test
... ... ...
$ git bisect good
Bisecting: 0 revisions left to test after this (roughly 1 step)
[< ... sha ... >] 5


然后,它将检出另一个提交。45(因为只有两个提交)。让我们假设它选择了5。在构建之后,我们测试程序,看看回归是否存在。然后我们告诉git

$ make
$ make test
... ... ...
$ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[< ... sha ... >] 4


我们测试最后一个版本4。由于它是引入回归的版本,我们告诉它git

$ make
$ make test
... ... ...
$ git bisect bad
< ... sha ... > is the first bad commit
< ... commit message ... >


在这种简单的情况下,我们只需要测试3个版本(345)而不是4(1234).这是一个小的胜利,但这是因为我们的历史是如此之小.如果搜索范围是N提交,我们应该期望用git bisect测试1 + log 2 N次提交,而不是用线性搜索测试大约N / 2次提交。
一旦你找到了引入回归的提交,你可以研究它来找到问题所在。一旦完成了,你可以在使用git bisect命令之前使用git bisect reset将所有东西恢复到原始状态。

uurity8g

uurity8g2#

git bisect run自动二分法

如果你有一个自动化的./test脚本,如果测试是OK,它的退出状态为0,你可以用bisect run自动找到bug:

git checkout KNOWN_BAD_COMMIT
git bisect start

# Confirm that our test script is correct, and fails on the bad commit.
./test
# Should output != 0.
echo $?
# Tell Git that the current commit is bad.
git bisect bad

# Same for a known good commit in the past.
git checkout KNOWN_GOOD_COMMIT
./test
# Should output 0.
echo $?
# After this, git automatically checks out to the commit
# in the middle of KNOWN_BAD_COMMIT and KNOWN_GOOD_COMMIT.
git bisect good

# Bisect automatically all the way to the first bad or last good rev.
git bisect run ./test

# End the bisect operation and checkout to master again.
git bisect reset

字符串
当然,这是假设测试脚本./test被git跟踪,它不会在二分法的早期提交中消失。
我发现,通常你可以通过将树内脚本从树中复制出来,并可能使用类似PATH的变量,然后从那里运行它。
当然,如果test所依赖的测试基础设施在较早的提交上出现故障,那么就没有解决方案了,您将不得不手动操作,决定如何逐个测试提交。
然而,我发现使用这种自动化通常是有效的,并且可以节省大量的时间,因为你可以让它在一夜之间运行,并且可能在第二天早上发现你的bug,这是值得一试的。

更多提示

在bisect之后停留在第一次失败的提交上,而不是回到master

git bisect reset HEAD


start +初始badgood一次性完成:

git bisect start KNOWN_BAD_COMMIT KNOWN_GOOD_COMMIT~


是相同的:

git checkout KNOWN_BAD_COMMIT
git bisect start
git bisect bad
git bisect good KNOWN_GOOD_COMMIT


查看到目前为止测试的内容(通过手动goodbadrun):

git bisect log


示例输出:

git bisect log
git bisect start
# bad: [00b9fcdbe7e7d2579f212b51342f4d605e53253d] 9
git bisect bad 00b9fcdbe7e7d2579f212b51342f4d605e53253d
# good: [db7ec3d602db2d994fe981c0da55b7b85ca62566] 0
git bisect good db7ec3d602db2d994fe981c0da55b7b85ca62566
# good: [2461cd8ce8d3d1367ddb036c8f715c7b896397a5] 4
git bisect good 2461cd8ce8d3d1367ddb036c8f715c7b896397a5
# good: [8fbab5a3b44fd469a2da3830dac5c4c1358a87a0] 6
git bisect good 8fbab5a3b44fd469a2da3830dac5c4c1358a87a0
# bad: [dd2c05e71c246f9bcbd2fbe81deabf826c54be23] 8
git bisect bad dd2c05e71c246f9bcbd2fbe81deabf826c54be23
# bad: [c536b1b7242d5fcf92cd87e9a534bedb1c0c9c05] 7
git bisect bad c536b1b7242d5fcf92cd87e9a534bedb1c0c9c05
# first bad commit: [c536b1b7242d5fcf92cd87e9a534bedb1c0c9c0


在git log上显示好的和坏的refs,以获得更好的时间概念:

git log --decorate --pretty=fuller --simplify-by-decoration master


这只显示了带有相应ref的提交,这大大减少了噪音,但确实包括自动生成的ref类型:

refs/bisect/good*
refs/bisect/bad*


它告诉我们哪些提交被标记为好或坏。
如果您想使用这个命令,请考虑this test repo

失败来得快,成功来得慢

有时:

  • 故障发生得很快,例如,第一个测试中的一个中断
  • 成功需要一段时间,例如,坏掉的测试通过了,所有其他我们不关心的测试都通过了

对于这些情况,例如,假设失败总是在5秒内发生,如果我们懒得让测试更具体,我们可以使用timeout

#!/usr/bin/env bash
timeout 5 test-command
if [ $? -eq 1 ]; then
  exit 1
fi


这是因为timeout退出124,而test-command的失败退出1

Magic退出状态

git bisect run对退出状态有点挑剔:

  • 任何大于127的值都会使二分法失败,如下所示:
git bisect run failed:
exit code 134 from '../test -aa' is < 0 or >= 128


特别是,C assert(0)导致SIGABRT并以状态134退出,非常烦人。

  • 125是魔术,使运行被跳过与git bisect skip

这样做的目的是帮助跳过由于不相关的原因而损坏的构建。
详情请参见man git-bisect
所以你可能想使用类似这样的东西:

#!/usr/bin/env bash
set -eu
./build
status=0
./actual-test-command || status=$?
if [ "$status" -eq 125 ] || [ "$status" -gt 127 ]; then
  status=1
fi
exit "$status"


在git 2.16.1上测试。

92vpleto

92vpleto3#

TL;DR

开始:

$ git bisect start
$ git bisect bad
$ git bisect good <goodcommit>

字符串

$ git bisect start
$ git bisect good
$ git bisect bad <badcommit>


第一个月

重复:

问题仍然存在吗?

  • 是:$ git bisect bad
  • 编号:$ git bisect good

结果:

<abcdef> is the first bad commit

完成后:

git bisect reset

vhipe2zx

vhipe2zx4#

再补充一点:
我们可以指定一个文件名或路径到git bisect start,以防我们知道bug来自特定的文件。例如,假设我们知道导致回归的更改在com/workingDir目录中,那么我们可以运行git bisect start com/workingDir这意味着只有更改该目录内容的提交才会被检查,这使得事情变得更快。
另外,如果很难判断某个提交是好是坏,你可以运行git bisect skip,它会忽略它。如果有足够多的其他提交,git bisect会使用另一个来缩小搜索范围。

uhry853o

uhry853o5#

$ git bisect ..基本上是一个 * 用于调试的Git工具 *。'Git Bisect'通过检查自上次(已知)工作提交以来的前提交来进行调试。它使用二进制搜索来检查所有这些提交,以找到引入回归/bug的提交。
$ git bisect start #开始平分
$ git bisect bad #说明当前提交(v1.5)具有回归/设置“坏”点
$ git bisect good v1.0 #提到它的最后一个良好的工作提交(没有回归)
这里提到的“bad”和“good”点将帮助git bisect(二进制搜索)选择中间的元素(commit v1.3)。如果在commit v1.3时回归在那里,你将把它设置为新的“bad”点,即(Good -> v1.0 and Bad -> v1.3

$ git bisect bad

字符串
或者类似地,如果commit v1.3没有bug,你将把它设置为新的“Good point”,即(*Good -> v1.3 and Bad -> v1.6)。

$ git bisect good

brgchamk

brgchamk6#

注意:术语goodbad并不是唯一可以用来标记带有或不带有某个属性的提交的术语。
Git 2.7(Q4 2015)引入了新的git bisect选项。

git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
                  [--no-checkout] [<bad> [<good>...]] [--] [<paths>...]

字符串
文件增加:
有时候,你并不是在寻找引入了中断的提交,而是在寻找一个导致了其他“旧”状态和“新”状态之间的变化的提交。
例如,您可能正在查找引入特定修复的提交。
或者,您可能正在寻找源代码文件名最终全部转换为公司命名标准的第一次提交。
在这种情况下,使用“好”和“坏”来指“变更前的状态”和“变更后的状态”可能会非常混乱。
因此,您可以分别使用术语“old“和“new“来代替“good“和“bad“。
(But请注意,您不能在单个会话中混合“good“和“bad“与“old“和“new“。
在这种更一般的用法中,您为git bisect提供了一个“new“提交,该提交具有某些属性,而“old“提交则没有该属性。
每次git bisect checkout 一个提交时,您都要测试该提交是否具有以下属性:
如果是,则将提交标记为“new“;否则,将其标记为“old“。
当二分法完成后,git bisect将报告哪个提交引入了该属性。
参见commit 06e6a74commit 21b55e3commit fe67687(2015年6月29日)by Matthieu Moy ( moy )
commit 21e5cfd(2015年6月29日)Antoine Delaite ( CanardChouChinois )
(由Junio C Hamano -- gitster --合并于commit 22dd6eb,2015年10月5日)
确保使用Git 2.39(Q4 2022)for git bisect run:它包括修复bisect-helper中的回归,该回归错误地将给'git bisect run'(man)的命令的参数视为帮助程序的参数。
参见commit e9011b6commit 464ce0acommit 58786d7(2022年11月10日)by Đoàn Trần Công Danh ( sgn )
(由Junio C Hamano -- gitster --合并于commit e3d40fb,2022年11月23日)

bisect--helper:使用OPT_SUBCOMMAND解析子命令

报告人:LukáDoktor
签字人:oàn Tr n ng Danh
签名人:Taylor Blau
目前,我们正在使用OPT_CMDMODE,解析子命令,即使找到了命令,它也会继续解析更多选项。
当我们运行“git bisect run“(man)时,命令需要一个--log--no-log参数,或者其中一个“--bisect-...“参数,bisect--helper可能会错误地认为这些选项是bisect--helper的选项。
我们可以通过在从git-bisect.sh调用时传递“--“来解决这些问题,并在bisect--helper中跳过“--“。
但是,它可能会干扰用户的“--”。
让我们使用OPT_SUBCOMMAND解析子命令,因为该API是为这个特定用例而生的。

mbzjlibv

mbzjlibv7#

git对分退出状态
Ciro Santilli的2014 answer将其称为“神奇退出状态”
它们在Git 2.36(Q2 2022)中的使用方式略有不同:一个不太常见的错误是编写一个脚本以feed“git bisect“(man)运行,而没有使其可执行,在这种情况下,所有测试都将退出,并返回126或127个错误代码,即使是在标记为良好的修订版上。
尝试识别这种情况并尽早停止迭代。
参见René Scharfe ( rscharfe )commit 48af1fdcommit ba5bb81commit 8efa2accommit 80c2e96(2022年1月18日)。
(由Junio C Hamano -- gitster --合并于commit e828747,2022年3月6日)

bisect--helper:在退出代码为126和127时,请仔细检查运行命令

签核人:René Scharfe
当运行命令无法执行或无法找到时,shell分别返回退出代码126或127。
由于历史原因,有效的运行命令也可以返回这些代码,以指示错误的修订。
这意味着错别字可能会导致在整个距离上进行虚假的平分运行,并最终报告无效结果。
最好的解决方案是保留退出代码126和127,就像71b0251(Bisect run:,2007-10-26,Git v1.5.4-rc 0--merge)(Bisect run:“skip”current commit if script exit code is 125.,2007-10-26)对125所做的那样,并在我们得到它们时中止bisect run
不过,对于那些依赖于文档说明126和127可以用于糟糕的修订的人来说,这可能是不方便的。
此修补程序使用的解决方法是在已知良好的修订版上运行该命令,如果仍然得到相同的错误代码,则中止。
这将为使用退出代码126和127的脚本的运行增加一个步骤,但仍支持它们,但有一个例外:它不适用于无法识别(手动标记的)已知良好版本的命令。
使用低退出代码的运行命令不受影响。
在执行缺失命令两次和三次检出(第一步、已知良好的修订版和返回第一步的修订版)后,会报告拼写错误。
请参见examples

相关问题