1. 程式人生 > >讓 Git Bisect 幫助你

讓 Git Bisect 幫助你

fff ng- rms 個數字 htm animation 每天 process gre

讓 Git Bisect 幫助你

英文原文:Letting Git Bisect Help You

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)。

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

?
1 $ grep 1013 file.txt

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

?
1 $ git bisect start

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

?
1 $ git bisect bad

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

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

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

?
1 $ git bisect start master 7c0dcfa

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

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

?
1 2 3 4 5 $ grep 1013 file.txt $ git bisect good Bisecting: 剩余 255 個修訂待測試 (大概還需要 8 步) [a01ba83f3500b48da97c5f5c33052623aaa4161a] added 768

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

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

?
1 2 3 4 5 $ grep 1013 file.txt $ git bisect good Bisecting:剩余 127 個修訂待測試 (大概還需要 7 步) [4a4a668bf3363d09af5fd1906bc4272aacdb4495] added 896

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

?
1 2 3 4 5 $ grep 1013 file.txt $ git bisect good Bisecting: 剩余 63 個修訂待測試 (大概還需要 6 步) [9059c5b8b898159e8d1d797bff3b1febd1fd6a1c] added 960

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

?
1 2 3 4 5 $ grep 1013 file.txt $ git bisect good Bisecting: 剩余 31 個修訂待測試 (大概還需要 5 步) [0c844d0b33ef297b742206ebc293f4925705b083] added 992

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

?
1 2 3 4 5 $ grep 1013 file.txt $ git bisect good Bisecting: 剩余 15 個修訂待測試 (大概還需要 4 步) [0ee17eb17bd96b321a01c73eb13a8929a68b1239] added 1008

還有 4 步,這次依然 good。

?
1 2 3 4 5 $ grep 1013 file.txt $ git bisect good Bisecting: 剩余 7 個修訂待測試 (大概還需要 3 步) [dfb1e71736dcfffa2a30aecd7299f45f757c057e] added 1016

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

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

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

?
1 2 3 4 5 $ grep 1013 file.txt $ git bisect bad Bisecting: 剩余 1 個修訂待測試 (大概還需要 1 步) 254efa859d7fc66f1f58a59f0] added 1014

壞的提交點。

?
1 2 3 4 5 $ 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 腳本,如果大家有更好的方法,感謝告知)。

?
1 2 3 4 5 6 7 #!/bin/sh if [[ `grep 1013 file.txt` ]]; then exit 1 else exit 0 fi

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

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

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 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 嗎?


來源: <讓 Git Bisect 幫助你 - 技術翻譯 - 開源中國社區>

讓 Git Bisect 幫助你