如何使用git bisect?


437

我读了一些文章说那git bisect太好了。但是,我不是母语人士,而且我不明白为什么它很棒。

有人可以用一些代码示例进行演示:

  1. 如何使用它?
  2. 就像svn blame吗?

@ 01:正如git书中所述:在项目的历史记录中执行暴力搜索
eckes,2011年

7
并非如此/// brute :-),它使用二进制搜索。
cojocar 2011年

“ git blame”类似于“ svn blame”。“ git bisect”是完全不同的东西
William Pursell 2011年

值得一提的是,Pro Git中对bisect进行了很好的描述。Sylvain的答案是另一个很好的答案。如果在查看所有内容后仍不了解,建议您提出一个更具体的问题。一般问题产生一般答案。
卡斯卡贝尔2011年

Answers:


654

背后的想法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将检出提交。在我们的例子中,它将是commit 3。您需要构建程序,并检查是否存在回归。您还需要git通过git bisect bad是否存在回归来告知此修订的状态git bisect good

让我们假设回归是在commit中引入的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( ,12,)。3 4这是一次小小的胜利,但这是因为我们的历史太小了。如果搜索范围是N次提交,我们应该期望git bisect使用线性搜索测试1 + log2 N次提交,而不是大约N / 2次提交。

一旦找到引入回归的提交,就可以对其进行研究以发现问题。完成此操作后,您可以git bisect reset在使用git bisect命令之前将所有内容恢复为原始状态。


5
我在这里要反驳,这是bisect的很好解释,但实际上并不能帮助我使用它。特别是因为我设法找到了一个很好的提交者,并且我现在在那个分支上。从这个位置来看,这种解释完全没有帮助。我如何在不检出错误分支的情况下指定它,例如
PandaWood

4
您可以git bisect bad <rev> [<rev>...]用来将特定修订标记为不良(或标记为良好git bisect good <rev> [<rev>...])。rev可以是任何修订标识符,例如分支名称,标记,提交哈希(或提交哈希的唯一前缀),...
Sylvain Defresne

39
...完成后,您键入git bisect reset将所有内容放回最近的提交中
peetonn 2014年

17
堆栈上最棒的答案之一。清晰表达。多年来,我一直在手动执行此过程,只是在好与不好的提交之间选择一个任意的中点,然后再根据是否是好/坏本身在该提交与好/坏之间进行选择。一直以来,这一直是一个巨大的痛苦,直到今天,我什至从未听说过这个git子命令...哈哈哈
Chev 2014年

3
@Nemoden,是的,它可以,基本上,它对于任何类型的项目都是有用的。您只需要用“部署网站并重现问题”替换“进行测试”步骤
alex.b,2015年

157

git bisect run 自动二等分

如果您的自动化./test脚本的退出状态为0(如果测试正常),则可以使用以下命令自动查找错误bisect run

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-like变量并从那里运行来逃脱。

当然,如果test依赖的测试基础结构中断了较早的提交,则没有解决方案,您将必须手动进行操作,决定如何逐个测试提交。

但是我发现使用这种自动化通常可以工作,并且可以节省大量的时间,可以节省积压的任务中的较慢测试,您可以让它整夜运行,并且有可能在第二天早上发现错误,这是值得的尝试。

更多提示

在二等分之后停留在第一个失败的提交,而不是返回到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上显示好坏参考以获得更好的时间观念:

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

这仅显示带有相应ref的提交,这将大大降低噪声,但确实包括类型为自动生成的ref:

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

告诉我们哪些标记为好或坏的提交。

如果您想使用该命令,请考虑此测试仓库

失败快,成功慢

有时:

  • 失败很快发生,例如第一个测试中断
  • 成功需要一段时间,例如失败的测试通过,以及我们不关心的所有其他测试

对于这些情况,例如,假设故障总是在5秒钟内发生,并且如果我们懒于按照我们的实际意愿更具体地进行测试,则可以按以下方式使用timeout

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

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

魔术退出状态

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

  • 高于127的任何部分都会使二等分失败,并显示类似以下内容:

    git bisect run failed:
    exit code 134 from '../test -aa' is < 0 or >= 128
    

    特别是,C assert(0)导致a 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上测试。


7
git如何知道在恢复/平分到以前的/错误的版本(没有新编写的测试)时,防止新测试消失?
thebjorn 2014年

8
@thebjorn,您有一个要点:据我所知,测试必须在PATH中的外部可执行文件上,或者在存储库中的未跟踪文件中。在许多情况下,这是可能的:将测试放在单独的文件中,包括带有精心制作的test_script+模块化测试套件的必要测试样板,并在二等分的同时从单独的文件运行它。修复后,将测试合并到主测试套件中。
Ciro Santilli郝海东冠状病六四事件法轮功

1
@CiroSantilli六四事件法轮功纳米比亚威视很抱歉,我在下面回滚了您的编辑内容,因为我觉得它对新手来说更具解释性,它只是给您的答案加了一点(不是您的确切答案,因此提及添加一点)
尼克斯(Nicks

1
'git bisect run'有很多出错的方法-例如,查看良好的提交如何被错误的合并逆转。它进进出出再来回,只有最后一个“出局”不好。但是,您始终可以手动执行“ git bisect”。因为是二等分的,所以只需要几个步骤-例如10个步骤中的1024次提交。
combinatorist

1
@combinatorist,您可能会失败是正确的。bisect run当测试需要很长时间才能完成时,我发现它特别有用,而且我很确定测试系统不会中断。这样,我可以让它在后台运行,如果占用过多资源,则可以整夜运行,而不会浪费任何大脑上下文切换时间。
西罗Santilli郝海东冠状病六四事件法轮功

123

TL; DR

开始:

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

Bisecting: X revisions left to test after this (roughly Y steps)

重复:

问题仍然存在?

  • 是: $ git bisect bad
  • 没有: $ git bisect good

结果:

<abcdef> is the first bad commit

完成时间:

git bisect reset

3
确保您位于git repo的根目录,否则您将得到一个奇怪的“您需要从工作树的顶层运行此命令。” 错误。
PR Whitehead,

所以当错误不存在时,我的头上的git不好并且第一次提交时的git很好。那么下一步该怎么办?当不存在错误时,git bisect可以移至下一个提交吗?
Gobliins

@Gobliins如果不存在错误,请更正git bisect good以转到下一个提交。
杰弗里·黑尔

40

补充一点:

我们可以指定文件名或路径 git bisect start,以防我们知道该错误来自特定文件。例如,假设我们知道导致回归的更改在com / workingDir目录中,然后可以运行。git bisect start com/workingDir这意味着将仅检查更改此目录内容的提交,这将使处理速度更快。

另外,如果很难确定某个特定提交的好坏,可以运行git bisect skip,它将忽略它。如果有足够的其他提交,则git bisect将使用另一个提交来缩小搜索范围。


15

$ git bisect ..基本上是用于调试Git工具。“ Git Bisect”通过遍历自上次(已知)工作提交以来的先前提交进行调试。它使用二进制搜索来遍历所有这些提交,最后到达引入了回归/错误的提交。

$ git bisect start #开始二等分

$ git bisect bad #说明当前提交(v1.5)具有回归/设置“坏”点

$ git bisect good v1.0 #提及它最后一次有效的提交(不进行回归)

提到“坏”和“好”点将有助于git bisect(二进制搜索)选择中间元素(提交v1.3)。如果在提交v1.3处存在回归,则将其设置为新的“坏”点,即(好-> v1.0和坏-> v1.3

$ git bisect bad

或类似地,如果提交v1.3没有错误,则将其设置为新的“好点”,即(* Good-> v1.3和Bad-> v1.6)。

$ git bisect good

2

注意:术语goodbad不是您可以用来标记带有或不带有某些属性的提交的唯一术语。

Git 2.7(2015年第四季度)引入了新git bisect选项。

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

随着文档的添加:

有时,您不是在寻找导致损坏的提交,而是在导致其他“旧”状态和“新”状态之间发生更改提交

例如,您可能正在寻找引入了特定修复程序的提交。
或者,您可能正在寻找第一次提交,在该提交中,源代码文件名最终全部转换为公司的命名标准。管他呢。

在这种情况下,使用术语“好”和“坏”来指代“变更前的状态”和“变更后的状态”会非常令人困惑。

因此,您可以分别使用术语“ old”和“ new”代替“ good”和“ bad”。
(但是请注意,您不能在单个会话中将“ good”和“ bad”与“ old”和“ new” 混合使用。)

在这个更一般的使用情况,您提供git bisect一个“ new”犯有某些属性和“ old”承诺不具有该属性。

每一次 git bisect检出一个提交时,您都要测试该提交是否具有以下属性:
如果有,则将该提交标记为“ new”;否则,将其标记为“ old”。

二等分完成后,git bisect将报告哪个提交引入了该属性。


参见Matthieu Moy()的提交06e6a74提交21b55e3提交fe67687(2015年6月29日。 参见Antoine Delaite()的commit 21e5cfd(2015年6月29日(由Junio C Hamano合并--commit 22dd6eb中,2015年10月5日)moy
CanardChouChinois
gitster

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.