簡體中文 ▾ 主題 ▾ 最新版本 ▾ git-bisect 上次更新於 2.50.0

名稱

git-bisect - 使用二分查詢來找出引入 bug 的提交

概要

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

此命令使用二分查詢演算法來找出專案中哪個提交引入了 bug。使用時,你首先需要指定一個已知包含該 bug 的“壞”提交,以及一個已知在 bug 引入之前的“好”提交。然後 git bisect 會在這兩個端點之間選擇一個提交,並詢問你選定的提交是“好”還是“壞”。它會繼續縮小範圍,直到找到引入該更改的確切提交。

實際上,git bisect 可以用來找出改變了專案任何屬性的提交;例如,修復 bug 的提交,或導致基準測試效能提升的提交。為了支援這種更通用的用法,可以使用“舊”和“新”來代替“好”和“壞”,或者你可以選擇自己的術語。更多資訊請參閱下面的“替代術語”一節。

基本二分查詢命令:start, bad, good

例如,假設你正在嘗試找出哪個提交導致了在專案版本 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 將會報告哪個提交引入了該屬性。

要使用“舊”和“新”來代替“好”和“壞”,你必須在不帶提交引數的情況下執行 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 可用於瞭解如何稱呼比所查詢的更改更新的提交。

如果你想使用自己的術語代替“壞”/“好”或“新”/“舊”,你可以選擇任何你喜歡的名稱(除了像 reset, start 等現有二分查詢子命令)透過如下方式開始二分查詢:

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

例如,如果你正在尋找引入效能退化的提交,你可能會使用:

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

或者如果你正在尋找修復 bug 的提交,你可能會使用:

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

然後,使用 git bisect <舊術語>git bisect <新術語> 來標記提交,而不是 git bisect goodgit bisect bad

二分查詢視覺化/檢視

要在 gitk 中檢視當前剩餘的嫌疑提交,在二分查詢過程中執行以下命令(子命令 view 可以作為 visualize 的替代):

$ git bisect visualize

Git 透過各種環境變數檢測圖形環境:DISPLAY,它在 Unix 系統上的 X Window System 環境中設定。SESSIONNAME,它在 Cygwin 的互動式桌面會話中設定。MSYSTEM,它在 Msys2 和 Git for Windows 中設定。SECURITYSESSIONID,它可能在 macOS 的互動式桌面會話中設定。

如果這些環境變數都沒有設定,則使用 git log。你也可以提供命令列選項,例如 -p--stat

$ git bisect visualize --stat

二分查詢日誌和二分查詢重放

標記修訂版本為好或壞之後,執行以下命令以顯示到目前為止已完成的操作:

$ git bisect log

如果你發現自己在指定修訂版本狀態時犯了錯誤,可以將此命令的輸出儲存到檔案中,編輯它以刪除不正確的條目,然後執行以下命令以恢復到正確狀態:

$ git bisect reset
$ git bisect replay that-file

避免測試某個提交

如果在二分查詢會話中,你發現建議的修訂版本不適合測試(例如,它無法構建,並且你知道此失敗與你正在追查的 bug 無關),你可以手動選擇一個附近的提交併測試它。

例如

$ 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.5v2.6 之間的提交(包括兩者)都應該被跳過。

透過向 bisect start 提供更多引數來縮小二分查詢範圍

如果你知道正在追查的問題涉及程式碼樹的哪個部分,可以透過在執行 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 除外。

任何其他退出程式碼都將中止二分查詢過程。需要注意的是,透過 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

在遇到合併提交時,只跟隨第一個父提交。

在檢測透過分支合併引入的迴歸時,合併提交將被識別為 bug 的引入點,其祖先提交將被忽略。

當合並的分支包含損壞或無法構建的提交,但合併本身沒有問題時,此選項在避免誤報方面特別有用。

示例

  • 自動二分查詢 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

    為了防止二分查詢、make 和測試過程以及指令碼之間的相互作用,test.shcheck_test_case.sh 最好位於倉庫外部。

  • 使用臨時修改(熱修復)自動二分查詢

    $ 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 helpgit bisect -h 獲取詳細用法說明。

GIT

Git[1] 套件的一部分

scroll-to-top