Git
英语 ▾ 主题 ▾ 最新版本 ▾ git-bisect 最后更新于 2.44.0

名称

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

概要

git bisect <subcommand> <options>

描述

该命令接受各种子命令,以及取决于子命令的不同选项

git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]
	  [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
git bisect (bad|new|<term-new>) [<rev>]
git bisect (good|old|<term-old>) [<rev>...]
git bisect terms [--term-(good|old) | --term-(bad|new)]
git bisect skip [(<rev>|<range>)...]
git bisect reset [<commit>]
git bisect (visualize|view)
git bisect replay <logfile>
git bisect log
git bisect run <cmd> [<arg>...]
git bisect help

此命令使用二分搜索算法来查找项目历史记录中引入错误的提交。您需要先告诉它一个已知包含错误的“错误”提交,以及一个已知在错误引入之前存在的“良好”提交。然后,git bisect 会在这些端点之间选择一个提交,并询问您所选的提交是“良好”还是“错误”。它会不断缩小范围,直到找到引入更改的确切提交。

事实上,git bisect 可以用于查找更改项目任何属性的提交;例如,修复错误的提交,或导致基准测试性能提高的提交。为了支持这种更通用的用法,可以使用“旧”和“新”来代替“良好”和“错误”,或者您可以选择自己的术语。有关更多信息,请参阅下面的“备用术语”部分。

基本 bisect 命令:start、bad、good

例如,假设您正在尝试查找破坏了项目 v2.6.13-rc2 版本中已知有效的特性的提交。您可以按如下方式启动 bisect 会话

$ 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 引用将保留指向该提交。

Bisect 重置

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

$ git bisect reset

默认情况下,这会将您的树返回到 git bisect start 之前检出的提交。(新的 git bisect start 也将执行此操作,因为它会清理旧的 bisect 状态。)

使用可选参数,您可以返回到其他提交,而不是返回到原始提交

$ git bisect reset <commit>

例如,git bisect reset bisect/bad 将检出第一个错误修订版本,而 git bisect reset HEAD 将使您停留在当前 bisect 提交上,并且根本不会切换提交。

备用术语

有时您并非要查找引入中断的提交,而是要查找导致某些其他“旧”状态和“新”状态之间发生更改的提交。例如,您可能正在查找引入特定修复的提交。或者您可能正在查找源代码文件名最终全部转换为公司命名标准的第一个提交。等等。

在这种情况下,使用“良好”和“错误”来表示“更改之前的状态”和“更改之后的状态”可能会非常令人困惑。因此,您可以分别使用“旧”和“新”来代替“良好”和“错误”。(但请注意,您不能在单个会话中混合使用“良好”和“错误”与“旧”和“新”。)

在这种更通用的用法中,您需要为 git bisect 提供具有某些属性的“新”提交,以及不具有该属性的“旧”提交。每次 git bisect 检出一个提交时,您都需要测试该提交是否具有该属性。如果它具有该属性,则将该提交标记为“新”;否则,将其标记为“旧”。当二分完成时,git bisect 会报告引入该属性的提交。

要使用“旧”和“新”而不是“良好”和“错误”,您必须运行没有提交作为参数的 git bisect start,然后运行以下命令来添加提交

git bisect old [<rev>]

以指示提交是在所查找更改之前,或者

git bisect new [<rev>...]

以指示它是在更改之后。

要获取当前使用术语的提醒,请使用

git bisect terms

您可以使用 git bisect terms --term-oldgit bisect terms --term-good 获取仅旧术语;git bisect terms --term-newgit bisect terms --term-bad 可用于了解如何称呼比所查找更改更新的提交。

如果您希望使用自己的术语,而不是使用“错误”/“良好”或“新”/“旧”,则可以使用任何您喜欢的名称(除了现有的 bisect 子命令,例如 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 来标记提交。

Bisect 可视化/查看

要在二分过程中查看 gitk 中当前剩余的嫌疑人,请发出以下命令(子命令 view 可以用作 visualize 的替代方法)

$ git bisect visualize

Git 通过各种环境变量检测图形环境:DISPLAY,在 Unix 系统上的 X Window 系统环境中设置。SESSIONNAME,在交互式桌面会话下的 Cygwin 中设置。MSYSTEM,在 Msys2 和 Git for Windows 下设置。SECURITYSESSIONID,可能在交互式桌面会话下的 macOS 上设置。

如果未设置这些环境变量,则使用 git log。您还可以提供命令行选项,例如 -p--stat

$ git bisect visualize --stat

Bisect 日志和 bisect 重播

在将修订版本标记为良好或错误之后,发出以下命令以显示到目前为止所执行的操作

$ git bisect log

如果您发现您在指定修订版本的状态时犯了错误,则可以将此命令的输出保存到文件,编辑它以删除错误条目,然后发出以下命令以返回到修正状态

$ git bisect reset
$ git bisect replay that-file

避免测试提交

如果在 bisect 会话的中间,您知道建议的修订版本不是一个很好的测试对象(例如,它无法构建,并且您知道该错误与您正在追查的错误无关),则可以手动选择一个附近的提交并测试它。

例如

$ 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`(包括 `v2.6`)的所有提交都不应该被测试。

请注意,如果您还想跳过范围内的第一个提交,则应发出以下命令

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

这告诉二分查找过程,应跳过 `v2.5` 和 `v2.6`(包括)之间的提交。

通过向 bisect start 提供更多参数来减少二分查找

如果您知道树的哪一部分与您要追踪的问题有关,则可以通过在发出 `bisect start` 命令时指定 pathspec 参数来进一步减少试验次数

$ 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。

任何其他退出代码都会中止二分查找过程。需要注意的是,通过 `exit(-1)` 终止的程序会使 $? = 255(参见 exit(3) 手册页),因为值会与 `& 0377` 进行切片。

当无法测试当前源代码时,应使用特殊的退出代码 125。如果脚本使用此代码退出,则当前版本将被跳过(参见上面的 `git bisect skip`)。125 被选为用于此目的的最高合理值,因为 126 和 127 被 POSIX shell 用于指示特定的错误状态(127 用于命令未找到,126 用于命令找到但不可执行,这些细节并不重要,因为就 `bisect run` 而言,它们是脚本中的正常错误)。

您经常会发现,在二分查找会话期间,您希望对正在测试的版本进行临时修改(例如,在头文件中使用 `s/#define DEBUG 0/#define DEBUG 1/`,或者“没有此提交的版本需要应用此补丁来解决另一个与该二分查找无关的问题”)。

为了应对这种情况,在内部 `git bisect` 找到下一个要测试的版本之后,脚本可以在编译之前应用补丁,运行真正的测试,然后确定该版本(可能需要补丁)是否通过了测试,然后将树恢复到原始状态。最后,脚本应该使用真正的测试状态退出,以使 `git bisect run` 命令循环确定二分查找会话的最终结果。

选项

--no-checkout

在二分查找过程的每次迭代中,不要检出新的工作树。而是只更新名为 `BISECT_HEAD` 的引用,使其指向要测试的提交。

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

如果存储库是裸存储库,则会假定使用 `--no-checkout`。

--first-parent

在遇到合并提交时,只跟踪第一个父提交。

在检测通过合并分支引入的回归时,合并提交将被识别为引入错误,其祖先将被忽略。

当合并的分支包含损坏的或不可构建的提交,但合并本身是正常的时,此选项在避免误报方面特别有用。

示例

  • 自动在 v1.2 和 HEAD 之间对构建失败进行二分查找

    $ git bisect start HEAD v1.2 --      # HEAD is bad, v1.2 is good
    $ git bisect run make                # "make" builds the app
    $ git bisect reset                   # quit the bisect session
  • 自动在 origin 和 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
  • 自动对测试用例失败进行二分查找

    $ 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 session

    在这里,我们使用一个 `test.sh` 自定义脚本。在此脚本中,如果 `make` 失败,我们将跳过当前提交。`check_test_case.sh` 应该在测试用例通过时 `exit 0`,在测试用例失败时 `exit 1`。

    如果 `test.sh` 和 `check_test_case.sh` 都在存储库之外,则更安全,以防止二分查找、make 和测试进程以及脚本之间的交互。

  • 使用临时修改(热修复)自动进行二分查找

    $ 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 --no-ff 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 --   # culprit is among the last 10
    $ git bisect run sh -c "make || exit 125; ~/check_test_case.sh"
    $ git bisect reset                   # quit the bisect session

    这表明,如果您将测试写成单行代码,则无需使用运行脚本。

  • 在损坏的存储库中查找对象图中的良好区域

    $ 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

    或者

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

获取帮助

使用 `git bisect` 获取简短的使用说明,使用 `git bisect help` 或 `git bisect -h` 获取详细的使用说明。

GIT

git[1] 套件的一部分

scroll-to-top