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

名稱

git-bisect - 使用二分查詢找到引入錯誤的提交

概要

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 next
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 reset

在 bisect 會話之後,要清理 bisection 狀態並返回到原始 HEAD,請發出以下命令:

$ git bisect reset

預設情況下,這將把您的程式碼樹恢復到 git bisect start 之前檢出的提交。(一個新的 git bisect start 也會這樣做,因為它會清理舊的 bisection 狀態)。

使用可選引數,您可以返回到不同的提交:

$ git bisect reset <commit>

例如,git bisect reset bisect/bad 將檢出第一個壞修訂版本,而 git bisect reset HEAD 將讓您停留在當前的 bisection 提交上,並且完全避免切換提交。

替代術語

有時您尋找的不是引入了錯誤的提交,而是導致了某個“舊”狀態與“新”狀態之間發生變化的提交。例如,您可能正在尋找引入特定修復的提交。或者您可能正在尋找原始檔名最終都轉換為公司命名標準的第一個提交。或者其他任何情況。

在這些情況下,使用“好”和“壞”來指代“變化之前”和“變化之後”的狀態可能會非常令人困惑。因此,您可以分別使用“舊”和“新”這兩個術語來代替“好”和“壞”。(但請注意,您不能在一個會話中混合使用“好”和“壞”與“舊”和“新”)。

在這種更通用的用法中,您向 git bisect 提供一個具有某種屬性的“新”提交和一個不具有該屬性的“舊”提交。每次 git bisect 檢出一個提交時,您都會測試該提交是否具有該屬性。如果具有,則將該提交標記為“新”;否則,將其標記為“舊”。當 bisection 完成後,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 等) ,方法是使用以下命令啟動 bisection:

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 visualize/view

要在 bisection 過程中使用 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

Bisect log and bisect replay

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

$ 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

然後編譯並測試選定的修訂版本,之後以通常的方式將其標記為好或壞。

Bisect skip

您可以要求 Git 代替您手動選擇一個附近的提交,方法是發出命令:

$ git bisect skip                 # Current version cannot be tested

但是,如果您跳過了一個緊鄰您要查詢的提交的提交,Git 將無法準確地判斷哪個提交是第一個壞提交。

您還可以使用範圍表示法跳過一系列提交,而不是隻跳過一個提交。例如:

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

這告訴 bisect 過程,在 v2.5 之後,直到包含 v2.6 的所有提交,都不應該被測試。

請注意,如果您也想跳過範圍內的第一個提交,可以發出命令:

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

這告訴 bisect 過程,v2.5v2.6 之間的提交(包括這兩個提交)將被跳過。

Bisect next

通常,在標記一個修訂版本為好或壞之後,Git 會自動計算並檢出下一個要測試的修訂版本。但是,如果您需要顯式請求下一個 bisection 步驟,可以使用:

$ git bisect next

您可以使用此命令來恢復被中斷的 bisection 過程,中斷方式是檢出另一個修訂版本。

透過向 bisect start 提供更多引數來縮減 bisection 的範圍

如果您知道要跟蹤的問題涉及程式碼樹的哪個部分,您可以透過在發出 bisect start 命令時指定路徑名引數來進一步減少試錯次數:

$ git bisect start -- arch/i386 include/asm-i386

如果您預先知道多個好提交,可以在發出 bisect start 命令時,在壞提交之後立即指定所有好提交,從而縮小 bisect 的空間:

$ 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

Bisect run

如果您有一個可以判斷當前原始碼是好是壞的指令碼,您可以透過發出命令來執行 bisect:

$ git bisect run my_script arguments

請注意,指令碼(上面示例中的 my_script)在當前原始碼為好/舊時應退出碼 0,在當前原始碼為壞/新時應退出碼 1 到 127(包括 125)之間的值。

任何其他退出碼都將中止 bisect 過程。應注意的是,透過 exit(-1) 終止的程式將退出碼 $? = 255,(參見 exit(3) 手冊頁),因為該值會按 & 0377 進行截斷。

應使用特殊退出碼 125 來表示當前原始碼無法測試。如果指令碼以該退出碼退出,則當前修訂版本將被跳過(參見上面的 git bisect skip)。125 被選為用於此目的的最高合理值,因為 126 和 127 由 POSIX shell 用於指示特定的錯誤狀態(127 表示命令未找到,126 表示命令已找到但不可執行—​這些細節不重要,因為對於 bisect run 而言,它們只是指令碼中的正常錯誤)。

您經常會發現,在 bisect 會話期間,您可能需要對正在測試的修訂版本應用臨時修改(例如,將 s/#define DEBUG 0/#define DEBUG 1/ 寫入標頭檔案,或者“不包含此提交的修訂版本需要應用此補丁來規避另一個 bisect 不關心的其他問題”)。

為了應對這種情況,在內部 git bisect 找到下一個要測試的修訂版本後,指令碼可以在編譯前應用補丁,執行實際測試,然後決定該修訂版本(可能帶有所需的補丁)是否通過了測試,最後再將程式碼樹恢復到原始狀態。最後,指令碼應以實際測試的狀態退出,以便 git bisect run 命令迴圈確定 bisect 會話的最終結果。

選項

--no-checkout

在 bisection 過程的每次迭代中,不檢出新的工作樹。而是僅更新名為 BISECT_HEAD 的引用,使其指向應被測試的提交。

當您在每一步執行的測試不需要檢出的程式碼樹時,此選項可能很有用。

如果倉庫是裸倉庫,則假定使用 --no-checkout

--first-parent

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

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

此選項在合併的分支包含損壞或無法構建的提交,但合併本身是正常的情況下,避免了誤報,特別有用。

示例

  • 在 v1.2 和 HEAD 之間自動 bisect 一個損壞的構建

    $ 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 之間自動 bisect 一個測試失敗

    $ 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
  • 自動 bisect 一個損壞的測試用例

    $ 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.shcheck_test_case.sh 放在倉庫之外,以防止 bisect、make 和 test 程序與指令碼之間發生互動。

  • 自動 bisect 並應用臨時修改(熱修復)

    $ 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

    這會在每次測試執行前應用來自熱修復分支的修改,例如,在您的構建或測試環境發生變化的情況下,舊版本可能需要一個新版本已經擁有的修復。(確保熱修復分支基於一個包含在您正在 bisect 的所有修訂版本中的提交,這樣合併就不會引入太多內容,或者使用 git cherry-pick 而不是 git merge)。

  • 自動 bisect 一個損壞的測試用例

    $ 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

    這表明,如果您將測試寫在一行上,也可以不使用 run 指令碼。

  • 在損壞的倉庫中定位物件圖的一個良好區域

    $ 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] 套件的一部分