不知道 bug 躲在哪個 commit 嗎?來試試 Git bisect 吧!

Larry Lu
Starbugs Weekly 星巴哥技術專欄
6 min readFeb 21, 2021

--

Git 作為近年來最流行的版本控制工具,除了提供基本的 add、commit、push 功能之外,其實還有很多進階的指令可以用~像上個月專欄提到的 Interactive Rebase 跟今天要介紹的 Git bisect 我都覺得非常好用,可以說是必學的 Git 指令!

前情提要

有用過 Git 進行多人協作的話應該都知道:通常在團隊中開發或是參與開源專案時,每個人都會開自己的 feature branch,直到功能完成之後才 merge 回主線

不過團隊成員多了之後,不只開出去的 branch 多了,主線上也會不斷有新完成的 branch 合併進來。導致有一天在主線上發現 bug 時,那個 bug 可能也存在一陣子了(也許幾十個 commit 之前)

但因為每天都有一堆 branch 合併回主線,而且每個人都說「在我那個 branch 沒這問題啊」,所以想找出主線上的 bug 到底是哪個 commit 造成的非常費工

那在這種情況下,究竟要怎麼在茫茫 commit 海中找出 buggy commit 呢?這時就要把 Git Bisect 請出來了

Git bisect

Git bisect 的原理是這樣的:雖然你不知道 buggy commit 是哪一個,但你可以指定一個範圍叫 Git 幫你找 buggy commit(範圍多大都沒問題)

接著 Git 就會在範圍內使用二分搜尋法跳來跳去(所以才叫 bisect),如果你告訴 Git 這個時間點已經有 bug 了他就往舊的 commit 跳,若是 bug 還沒發生則往新的 commit 跳,就這樣直到揪出誰是 buggy commit

以上圖來說,在選定了搜尋範圍之後(一定要好壞各一),Git 第一步就會直接跳到中間的 commit 讓你測試這個 commit 是好是壞

如果是壞的話,代表 buggy commit 一定在這個 commit 的左邊,所以第二步往左跳了兩個 commit 讓你測試。如果這時測出來是 Good,那就會再往右跳一格,就這樣不斷的測試直到找到 Good 跟 Bad 的分界點,真的跟二分搜尋法一樣對吧~

正因為是用二分搜尋法,可能的範圍會不斷切半切半再切半,所以即便是要在 1000 個 commit 裡尋找 buggy commit,也只需要十次的測試就夠了(2¹⁰ = 1024),可說是非常高效率

實際操作

身為工程師當然不能光說不練,所以這邊要來實際操作一下,想跟著玩的話我也建了一個 repo。專案的歷史紀錄長這樣,雖然只有七個 commit 有點鬧XD,不過意思有到就好了

給大家看一下,在最舊的 commit 3e11686 上執行 node index.js 時結果是 Hello World,但到了最新的 commit 0767776 再執行 node index.js 則是出現 Hello Bug,代表這中間一定有某個 commit 把程式碼改壞了

所以我們就啟動 git bisect,並且把最新的 commit 標示為 Bad、最舊的標示為 Good,然後 Git 就會自動跳到中間的 commit 讓我們測試(看 HEAD)

因為測試完之後發現結果還是 Hello Bug,所以就下 git bisect bad 告訴 Git 中間這個 commit 也是壞掉的,那 Git 就會繼續幫你跳到需要測試的地方

就這樣經過幾次測試之後,Git 就會告訴我們是從 Minor bug fix 這個 commit 開始壞掉的,換句話說他就是 buggy commit,然後就可以下 git bisect reset 結束 git bisect,就這麼簡單~

有辦法自動測試嗎?

不知道各位有沒有發現,說穿了 Git bisect 就是自動幫你切換到各個 commit 讓你手動測試而已。但因為測試的步驟基本上都一樣,像這個例子就是跑 node index.js 然後檢查輸出,所以當然也可以寫成腳本讓他自動化

譬如說我要寫個腳本要判斷目前的 commit 是不是有 bug,那就先執行 node index.js,然後用 grep 判斷他的輸出有沒有 World 就好了(正常應該要輸出 Hello World

如果有 World 就代表這個 commit 是 Good,所以就 exit 0 正常結束;沒有 World 的話則是代表這個 commit 是 Bad,用 exit 1 來代表異常,很簡單吧~

有了腳本之後就跟先前一樣先選定要做 git bisect 的範圍(從 3e116860767776

設定好範圍後馬上就下 git bisect run ./test.sh 告訴 Git 要用剛寫好的 test.sh 來判斷 commit 的好壞,接著 Git 就會霹哩啪拉的跳來跳去,然後根據腳本的執行結果去標示 Good 跟 Bad,最後指出有問題的 commit 就是 Minor bug fix,跟之前手動測試的結果相同

因為已經把判斷好壞的步驟都寫到腳本裡頭了,所以放著跑一下就能找到 buggy commit,也不用再手動操作什麼,真的很方便對吧!

總結

關於 git bisect 的介紹就講到這邊~希望大家下次找不到 bug 躲在哪個 commit 時都可以試試看 git bisect,真的會一試成主顧。不過前提是你的 commit 不能太肥,不然就算找到了 buggy commit,結果那個 commit 總共修改了兩千行程式碼,那大概連佛祖都幫不了你XD

如果不知道怎麼把 commit 切小可以參考我寫的「送 PR 前,使用 Git rebase 來整理你的 commit 吧!」,把 commit 整理好了哪天需要回去看的時候真的會感謝自己XD

--

--

Larry Lu
Starbugs Weekly 星巴哥技術專欄

我是 Larry 盧承億,傳說中的 0.1 倍工程師。我熱愛技術、喜歡與人分享,專長是 JS 跟 Go,平常會寫寫技術文章還有參加各種技術活動,歡迎大家來找我聊聊~