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 reset

在 bisect 會期之後,若要清除二分狀態並返回原始 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 來了解如何稱呼比尋求的變更更新的提交。

如果您想使用自己的詞,而不是「壞」/「好」或「新」/「舊」,您可以選擇任何您喜歡的名稱(除了現有的 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 visualize/view

若要在二分過程中於 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 和 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

這會告知二分程序,不應該測試 v2.5 之後(直到並包括 v2.6)的任何提交。

請注意,如果您也想跳過該範圍的第一個提交,您會執行以下命令

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

這會告知二分程序,應該跳過 v2.5v2.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

Bisect 執行

如果您有一個腳本可以判斷目前的原始碼是好還是壞,您可以透過發出以下指令來進行二分搜尋:

$ 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.shcheck_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 helpgit bisect -h 來取得詳細的使用說明。

GIT

git[1] 套件的一部分

scroll-to-top