# git-bisect
> 原文: [https://git-scm.com/docs/git-bisect](https://git-scm.com/docs/git-bisect)
## 名稱
git-bisect - 使用二進制搜索來查找引入錯誤的提交
## 概要
```
git bisect <subcommand> <options>
```
## 描述
該命令采用各種子命令,并根據子命令使用不同的選項:
```
git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>]
[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]
git bisect (bad|new|<term-new>) [<rev>]
git bisect (good|old|<term-old>) [<rev>...]
git bisect terms [--term-good | --term-bad]
git bisect skip [(<rev>|<range>)...]
git bisect reset [<commit>]
git bisect (visualize|view)
git bisect replay <logfile>
git bisect log
git bisect run <cmd>...
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 good`或`git bisect bad`來請求下一個需要測試的提交。
最終將不再需要檢查修改,命令將打印出第一個錯誤提交的描述。引用`refs/bisect/bad`將指向該提交。
### Bisect重置
在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-old`或`git bisect terms --term-good`獲得舊的(分別為新的)術語。
如果您想使用自己的術語而不是“壞”/“好”或“新”/“舊”,您可以選擇任何您喜歡的名稱(現有的bisect子命令除外,如`reset`,`start`,... )通過使用開始二分
```
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 good`和`git bisect bad`來標記提交。
### Bisect可視化/查看
要查看 _gitk_ 中當前剩余的嫌疑人,請在二分過程中發出以下命令(子命令`view`可用作`visualize`的替代):
```
$ git bisect visualize
```
如果未設置`DISPLAY`環境變量,則使用 _git log_ 。您還可以提供命令行選項,如`-p`和`--stat`。
```
$ git bisect visualize --stat
```
### Bisect日志和平分重播
將標記的修訂標記為好或壞后,發出以下命令以顯示到目前為止已完成的操作:
```
$ git bisect log
```
如果發現在指定修訂的狀態時出錯,可以將此命令的輸出保存到文件,編輯它以刪除不正確的條目,然后發出以下命令以返回到已更正的狀態:
```
$ git bisect reset
$ git bisect replay that-file
```
### 避免測試提交
如果在bisect會話的中間,你知道建議的修訂版不是一個好的測試版(例如它無法構建,你知道失敗與你正在追逐的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
```
然后編譯并測試所選的修訂版,然后以通常的方式將修訂版標記為好或壞。
### Bisect跳過
您可以通過發出命令讓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.5`和`v2.6`(包括)之間的提交。
### 通過提供更多參數來平分開始來減少二分
通過在發出`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
```
### 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作為用于此目的的最高敏感值,因為POSIX shell使用126和127來指示特定的錯誤狀態(127表示未找到命令,126表示找到命令但不可執行 - 這些詳細信息不問題,因為它們是腳本中的正常錯誤,就`bisect run`而言。
您可能經常發現在二等分會話期間您希望進行臨時修改(例如,s / #define DEBUG 0 / #define DEBUG 1 /在頭文件中,或者“沒有此提交的修訂版需要將此修補程序應用于解決方法另一個問題是這個二分法對于應用于被測試的修訂版不感興趣。
為了應對這種情況,在內部 _git bisect_ 找到要測試的下一個修訂版之后,腳本可以在編譯之前應用補丁,運行真實測試,然后決定是否修改(可能需要修改) patch)通過了測試,然后將樹倒回到原始狀態。最后,腳本應該以實際測試的狀態退出,讓`git bisect run`命令循環確定bisect會話的最終結果。
## OPTIONS
```
--no-checkout
```
不要在二分過程的每次迭代中簽出新的工作樹。相反,只需更新名為`BISECT_HEAD`的特殊引用,使其指向應測試的提交。
當您在每個步驟中執行的測試不需要簽出樹時,此選項可能很有用。
如果存儲庫是裸的,則假定為`--no-checkout`。
## 例子
* 在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
```
* 自動平分原點和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`都在存儲庫之外,以防止bisect,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 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對象_所需的意義上是完全可遍歷的。
* 在代碼中尋找修復而不是回歸
```
$ 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 bisect](git-bisect-lk2009.html) , [git-blame [1]](https://git-scm.com/docs/git-blame) 對抗回歸。
## GIT
部分 [git [1]](https://git-scm.com/docs/git) 套件
- git
- git-config
- git-help
- git-init
- git-clone
- git-add
- git-status
- git-diff
- git-commit
- git-reset
- git-rm
- git-mv
- git-branch
- git-checkout
- git-merge
- git-mergetool
- git-log
- git-stash
- git-tag
- git-worktree
- git-fetch
- git-pull
- git-push
- git-remote
- git-submodule
- git-show
- git-log
- git-shortlog
- git-describe
- git-apply
- git-cherry-pick
- git-rebase
- git-revert
- git-bisect
- git-blame
- git-grep
- gitattributes
- giteveryday
- gitglossary
- githooks
- gitignore
- gitmodules
- gitrevisions
- gittutorial
- gitworkflows
- git-am
- git-format-patch
- git-send-email
- git-request-pull
- git-svn
- git-fast-import
- git-clean
- git-gc
- git-fsck
- git-reflog
- git-filter-branch
- git-instaweb
- git-archive
- git-bundle
- git-daemon
- git-update-server-info
- git-cat-file
- git-check-ignore
- git-checkout-index
- git-commit-tree
- git-count-objects
- git-diff-index
- git-for-each-ref
- git-hash-object
- git-ls-files
- git-merge-base
- git-read-tree
- git-rev-list
- git-rev-parse
- git-show-ref
- git-symbolic-ref
- git-update-index
- git-update-ref
- git-verify-pack
- git-write-tree