歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> 讓 Git Bisect 幫助你

讓 Git Bisect 幫助你

日期:2017/2/28 14:25:12   编辑:Linux教程

Git 提供來很多的工具來幫助我們改進工作流程。 bisect 命令就是其中之一, 雖然由於使用得不多而不廣為人知,但是當你想知道一個本來好的分支從什麼時候開始變壞時,它就能派上用場了。到底是哪一次提交把事情搞砸了呢,讓 bisect 來告訴你吧。

Bisect 基於二分查找算法。給定一個有序的元素序列,它會返回要你要查找的元素的序號(或者告訴你該元素是否在序列中)。它基於如下的不變式:當你對比完一個元素,你就能根據比較的結果拋棄掉它之前或者之後的所有元素(從而大大縮小下次查找的范圍)。

如果你有去過圖書館,那你可能注意到了每個專區內的書籍都是按作者姓名來排序的。比方說你要找的一本書的作者的姓為 Martin,而你剛剛看到某個書架上的最後一本書的作者的姓是 F 開頭的,那麼你就能確定你要找的書一定放在後面的某個書架上。依此方法繼續下次,你就能快速的縮小搜索范圍直到找到你要的書為止。

二分查找法亦是如此。如果你想在有 n 個元素的序列(有序的)中查找元素 x,你挑出第 n/2 個元素並將其與元素 x 比較。如果 x 大,那麼就對從 n/2+1 到 n 的子序列重復上述步驟,反之,就對從 1 到 n/2-1 的子序列重復上述步驟, 這樣一直遞歸下去。這裡就有一個 關於此算法的有趣的演示。

二分查找算法所需的比較次數通常都比你傻傻的去拿序列中的每個元素與 x 比較所需的比較次數少得多。事實證明在有序序列中查找元素,二分查找更快更有用。

Bisect

Bisect 就是利用二分查找發來查找在你的某一分支中到底是哪一次提交引入了特定的變更。

首先,也是最起碼的,你得有辦法檢測出一個給定的提交點是不是有 bug。通常你可以寫個測試代碼,如果這也不可能的話,那至少你得有一套步驟來使 bug 再現出來。

有了這套檢測方法,你就可以開始用 bisect 查找你的提交歷史來以最快的速度發現引入 bug 的時間點了。等等,你還得准備好兩個提交點:一個是你確定 bug 還沒有被引入的提交點,另一個則是確定 bug 已經引入了的提交點(這樣就縮小了初始的查找范圍)。在 bisect 命令上,你可以用哈希值(hash)或者標簽(tag)來引用這兩個提交點。bisect 查找時,查找范圍裡的提交點要麼被標記為好的(通過測試,沒有 bug 的),要麼被標記為壞的(通不過測試,有 bug 的)。

上圖演示了 bisect 的執行步驟(綠點是好的提交點,紅點的就是壞的):bisect 被要求在提交點 1 到 8 這個范圍內查找首次引入 bug 的提交點(看圖一目了然是提交點 6),這裡提交點 1 和 8 就是上段中提及的,我們需要首先准備好給 bisect 命令的兩個(一好一壞)提交點。bisect 首先用調用者提供的檢測方法來測試 1 到 8 中間的提交點 4,如果它是好的(圖上的情況),那麼 bisect 就縮小范圍為它右邊的區域,重復上述步驟,反之,則選擇其左邊的區域為新的搜索范圍。如此遞歸下去,在上圖的例子中,只需要 3 步就確定了罪魁禍首是提交點 6.

我這裡創建了一個 git 倉庫,裡面共有 1024 個提交記錄,每個提交都往一個文本文件後面添加一個遞增的數字,從 1 到 1024,一行一個數字。我們的任務是要找出是哪個提交點向文本文件附加了 1013 這個數字(我們假定它就是一個 bug)。

首先,我們定出一個方法來判斷文件裡面有沒有這個數字。這就是一個測試了。方法很簡單:

$ grep 1013 file.txt

有了這條命令,我們就可以開始 bisect 了:

$ git bisect start

對 master 分支(的頭部提交點)運行這個測試,沒有得到想要的結果(就是沒有任何輸出),所以我們把它標記為壞的。

$ git bisect bad

現在讓我們指定一個好的提交點:假設第一個提交點(7c0dcfa)是沒有 bug 的。我們可以檢出(check out)這個提交點並標記它為好的提交點(git bisect good + 該提交點的哈希值)。

$ git bisect good 7c0dcfa
Bisecting: 511 revisions left to test after this (roughly 9 steps)
[8950f7db7e7cad0b2dc394ff9b75fc3d38c9d72a] added 512

也可以用下面的命令,完成上面的所有步驟:

$ git bisect start master 7c0dcfa

git bisect start 命令可以接受兩個參數,第一個是壞的提交點,第二是好的提交點。

很好,git bisect 自動檢出了正中間的提交點,運行我們的檢測命令,沒有任何的輸出(沒有 bug),因此我們把該提交點標記為好的。

$ grep 1013 file.txt

$ git bisect good
Bisecting: 剩余 255 個修訂待測試 (大概還需要 8 步)
[a01ba83f3500b48da97c5f5c33052623aaa4161a] added 768

$ grep 1013 file.txt

$ git bisect good
Bisecting: 剩余 255 個修訂待測試 (大概還需要 8 步)
[a01ba83f3500b48da97c5f5c33052623aaa4161a] added 768

譯者注:按照二分查找算法,bisect 根據你標記的結果,決定是檢出左半邊的中間提交點(如果你標記為 bad)還是右半邊的中間提交點(如果你標記為 good)

在 bisect 新檢出的提交點上,我們再次運行測試命令。這次依然是個好的提交點。

$ grep 1013 file.txt

$ git bisect good
Bisecting:剩余 127 個修訂待測試 (大概還需要 7 步)
[4a4a668bf3363d09af5fd1906bc4272aacdb4495] added 896

再次檢測,還是好的提交點。

$ grep 1013 file.txt

$ git bisect good
Bisecting: 剩余 63 個修訂待測試 (大概還需要 6 步)
[9059c5b8b898159e8d1d797bff3b1febd1fd6a1c] added 960

讓我們看看這些消息:除了告訴你當前新檢出的提交點以及新的搜索范圍內有多少個待測試的提交點外,它還估計出你最多還需要重復多少次測試命令就能找到你要的提交點。這次又是一個好的提交點。

$ grep 1013 file.txt

$ git bisect good
Bisecting: 剩余 31 個修訂待測試 (大概還需要 5 步)
[0c844d0b33ef297b742206ebc293f4925705b083] added 992

繼續,依然是一個好的提交點。

$ grep 1013 file.txt

$ git bisect good
Bisecting: 剩余 15 個修訂待測試 (大概還需要 4 步)
[0ee17eb17bd96b321a01c73eb13a8929a68b1239] added 1008

還有 4 步,這次依然 good。

$ grep 1013 file.txt

$ git bisect good
Bisecting: 剩余 7 個修訂待測試 (大概還需要 3 步)
[dfb1e71736dcfffa2a30aecd7299f45f757c057e] added 1016

這次測試命令終於有了輸出,bug 現身了!我們把這個提交點標記為壞的。

$ grep 1013 file.txt
1013
$ git bisect bad
Bisecting: 剩余 3 個修訂待測試 (大概還需要 2 步)
[6e6d08c374df5162fed65fed82859b69f86b936e] added 1012

終點近在咫尺,測試通過,我們把它標記為好的。

$ grep 1013 file.txt

$ git bisect bad
Bisecting: 剩余 1 個修訂待測試 (大概還需要 1 步)
254efa859d7fc66f1f58a59f0] added 1014

壞的提交點。

$ grep 1013 file.txt
1013
$ git bisect bad
Bisecting: 剩余 0 個修訂待測試 (大概還需要 0 步)
8e16d98ec7039a7c53855dd9ed6] added 1013

最後一步,這次是壞的。

$ git bisect bad 
458eab0eb8d808e16d98ec7039a7c53855dd9ed6 is the first bad commit
commit 458eab0eb8d808e16d98ec7039a7c53855dd9ed6
Author: Rodrigo Flores <[email protected]>
Date:   Tue Oct 21 22:31:05 2014 -0200

    added 1013

:100644 100644 7bc3db7f48a43ccf1a8cc7c26146912cc88c1009 b393a2138a96c1530f41f70
1ab43cca893226976 M  file.txt

我們終於得到了那個引入 1013 數字的提交點。命令 git bisect log 可以回放整個過程。

$ git bisect start 
# bad: [740cdf012013dc41a39b41d4b09b57a970bfe38f] added 1024
git bisect bad 740cdf012013dc41a39b41d4b09b57a970bfe38f
# good: [7c0dcfa7514379151e0d83ffbf805850d2093538] added 1
git bisect good 7c0dcfa7514379151e0d83ffbf805850d2093538
# good: [8950f7db7e7cad0b2dc394ff9b75fc3d38c9d72a] added 512
git bisect good 8950f7db7e7cad0b2dc394ff9b75fc3d38c9d72a
# good: [a01ba83f3500b48da97c5f5c33052623aaa4161a] added 768
git bisect good a01ba83f3500b48da97c5f5c33052623aaa4161a
# good: [4a4a668bf3363d09af5fd1906bc4272aacdb4495] added 896
git bisect good 4a4a668bf3363d09af5fd1906bc4272aacdb4495
# good: [9059c5b8b898159e8d1d797bff3b1febd1fd6a1c] added 960
git bisect good 9059c5b8b898159e8d1d797bff3b1febd1fd6a1c
# good: [0c844d0b33ef297b742206ebc293f4925705b083] added 992
git bisect good 0c844d0b33ef297b742206ebc293f4925705b083
# good: [0ee17eb17bd96b321a01c73eb13a8929a68b1239] added 1008
git bisect good 0ee17eb17bd96b321a01c73eb13a8929a68b1239
# bad: [dfb1e71736dcfffa2a30aecd7299f45f757c057e] added 1016
git bisect bad dfb1e71736dcfffa2a30aecd7299f45f757c057e
# good: [6e6d08c374df5162fed65fed82859b69f86b936e] added 1012
git bisect good 6e6d08c374df5162fed65fed82859b69f86b936e
# bad: [1d23b7045a8accd254efa859d7fc66f1f58a59f0] added 1014
git bisect bad 1d23b7045a8accd254efa859d7fc66f1f58a59f0
# bad: [458eab0eb8d808e16d98ec7039a7c53855dd9ed6] added 1013
git bisect bad 458eab0eb8d808e16d98ec7039a7c53855dd9ed6
# first bad commit: [458eab0eb8d808e16d98ec7039a7c53855dd9ed6] added 1013

這個例子裡一共有 1024 個提交點,遍歷他們我們只用了 10 步。如果提交點數量再多一倍變成 2048 個,根據二分查找算法,我們僅僅需要多加一步就能找到想要的提交點,因為二分查找算法的時間復雜度為 O(log n)。

盡管已經如此高效,一遍又一遍的運行測試命令還是很枯燥的。因此,讓我們再進一步,將這個過程自動化吧。

自動化

Git bisect 能接受一個腳本文件名作為命令參數,通過它的返回代碼判斷當前提交點的好壞。如果返回 0,就是好的提交點,返回其他值就是壞的提交點。湊巧的是,grep 命令如果找到了匹配行就會返回 0,反之返回 1。你可以使用 echo $? 命令來檢查測試命令的返回值。

grep 的返回值正好和我們的測試標准相反,所以我們需要寫一個腳本來反轉它的值(我不太習慣寫 shell 腳本,如果大家有更好的方法,感謝告知)。

#!/bin/sh

if [[ `grep 1013 file.txt` ]]; then
exit 1
else
exit 0
fi

保存上述代碼為 test.sh,並賦予它執行權限。

接著在給 bisect 命令指定了初始的好/壞提交點之後,你只需要運行:

git bisect run ./test.sh
running ./ola.sh
Bisecting: 255 revisions left to test after this (roughly 8 steps)
[a01ba83f3500b48da97c5f5c33052623aaa4161a] added 768
running ./ola.sh
Bisecting: 127 revisions left to test after this (roughly 7 steps)
[4a4a668bf3363d09af5fd1906bc4272aacdb4495] added 896
running ./ola.sh
Bisecting: 63 revisions left to test after this (roughly 6 steps)
[9059c5b8b898159e8d1d797bff3b1febd1fd6a1c] added 960
running ./ola.sh
Bisecting: 31 revisions left to test after this (roughly 5 steps)
[0c844d0b33ef297b742206ebc293f4925705b083] added 992
running ./ola.sh
Bisecting: 15 revisions left to test after this (roughly 4 steps)
[0ee17eb17bd96b321a01c73eb13a8929a68b1239] added 1008
running ./ola.sh
Bisecting: 7 revisions left to test after this (roughly 3 steps)
[dfb1e71736dcfffa2a30aecd7299f45f757c057e] added 1016
running ./ola.sh
Bisecting: 3 revisions left to test after this (roughly 2 steps)
[6e6d08c374df5162fed65fed82859b69f86b936e] added 1012
running ./ola.sh
Bisecting: 1 revision left to test after this (roughly 1 step)
[1d23b7045a8accd254efa859d7fc66f1f58a59f0] added 1014
running ./ola.sh
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[458eab0eb8d808e16d98ec7039a7c53855dd9ed6] added 1013
running ./ola.sh
458eab0eb8d808e16d98ec7039a7c53855dd9ed6 is the first bad commit
commit 458eab0eb8d808e16d98ec7039a7c53855dd9ed6
Author: Rodrigo Flores <[email protected]>
Date: Tue Oct 21 22:31:05 2014 -0200

added 1013

:100644 100644 7bc3db7f48a43ccf1a8cc7c26146912cc88c1009 b393a2138a96c15
30f41f701ab43cca893226976 M file.txt
bisect run success

不費吹灰之力,你就得到了出問題的提交點。

結論

你很可能不會每天都用到 bisect。也許一周用一次,甚至一個月只用到一次。但是當你想要排查出帶入問題的提交點時,bisect 確實能幫你一把。盡管並非必須,控制好每次提交的改動規模不要太大將對 bisect 排查大有幫助。如果每個提交都包含了大量的改動,那麼就算 bisect 幫你找到這個提交,你也不得不埋頭於大量的改動中苦苦搜尋 bug 的蹤跡。因此,我推薦的提交策略是嫌大不嫌多。

你呢?你經常使用 bisect 嗎?

Linux git命令參數及用法詳解 http://www.linuxidc.com/Linux/2012-01/51205.htm

Fedora通過Http Proxy下載Git http://www.linuxidc.com/Linux/2009-12/23170.htm

在Ubuntu Server上安裝Git http://www.linuxidc.com/Linux/2009-06/20421.htm

服務器端Git倉庫的創建(Ubuntu) http://www.linuxidc.com/Linux/2011-02/32542.htm

Linux下Git簡單使用教程(以Android為例) http://www.linuxidc.com/Linux/2010-11/29883.htm

Git權威指南 PDF高清中文版 http://www.linuxidc.com/Linux/2013-10/91053.htm

Git 2分鐘指南 http://www.linuxidc.com/Linux/2014-11/109201.htm

Git 的詳細介紹:請點這裡
Git 的下載地址:請點這裡

Copyright © Linux教程網 All Rights Reserved