546

我读过一些文章说这git bisect很棒。但是,我不是母语人士,我不明白为什么它很棒。

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

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

6 回答 6

762

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

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

您知道您的程序在修订版中无法正常运行current,并且在修订版中可以正常运行0。所以回归很可能是在其中一个提交中引入的12, 3, 4, 5, current.

您可以尝试检查每个提交,构建它,检查是否存在回归。如果有大量提交,这可能需要很长时间。这是一个线性搜索。我们可以通过二分查找做得更好。这就是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 badgit 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 个版本 ( 3, 4, 5) 而不是 4 个 ( 1, 2, 3, 4)。这是一个小小的胜利,但这是因为我们的历史太短了。如果搜索范围是 N 次提交,我们应该期望git bisect使用线性搜索测试 1 + log2 N 次提交,而不是大约 N / 2 次提交。

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

于 2011-01-17T14:32:20.930 回答
199

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依赖于旧提交的测试基础设施中断,则没有解决方案,您将不得不手动执行操作,决定如何一一测试提交。

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

更多提示

在 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

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

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

这仅显示具有相应参考的提交,这极大地减少了噪音,但确实包括自动生成的类型参考:

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

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

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

失败很快,成功也很慢

有时:

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

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

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

这有效,因为timeoutexits124test-commandexits失败1

魔术退出状态

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

  • 任何高于 127 的值都会使二等分失败,例如:

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

    特别是,一个 Cassert(0)导致 aSIGABRT并以状态 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 上测试。

于 2014-03-23T15:04:58.577 回答
153

TL;博士

开始:

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

或者

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

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
于 2016-05-18T17:36:52.870 回答
46

再补充一点:

git bisect start如果我们知道错误来自特定文件,我们可以指定文件名或路径 。例如,假设我们知道导致回归的更改在 com/workingDir 目录中,那么我们可以运行git bisect start com/workingDir这意味着只有更改了该目录内容的提交才会被检查,这使事情变得更快。

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

于 2014-09-12T06:12:40.960 回答
16

$ 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 没有错误,您将其设置为新的“好点”,即(*好 -> v1.3 和坏 -> v1.6)。

$ git bisect good
于 2013-06-27T14:37:52.437 回答
5

注意:这些术语并不是您可以用来标记具有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 ( )的提交 21e5cfd(2015 年 6 月 29 日) 。(由Junio C Hamano 合并——提交 22dd6eb中,2015 年 10 月 5 日)moy
CanardChouChinois
gitster

于 2018-08-13T18:54:18.480 回答