Git筆記(34) 調試

Git筆記(34) 調試


1. 文件標註

如果在追蹤代碼中的一個 bug,並且想知道是什麼時候以及爲何會引入
文件標註通常是最好用的工具
它能 顯示任何文件中每行最後一次修改的提交記錄

所以,如果在代碼中看到一個有 bug 的方法
可以使用 git blame 標註這個文件,查看哪一次提交引入了這行

以下示例用 git blame 確定了 Linux 內核源碼頂層的 Makefile 中每一行分別來自哪個提交和提交者
此外用 -L 選項還可以將標註的輸出限制爲該文件中的第 69 行到第 82 行

$ git blame -L 69,82 Makefile
b8b0618cf6fab (Cheng Renquan  2009-05-26 16:03:07 +0800 69) ifeq ("$(origin V)", "command line")
b8b0618cf6fab (Cheng Renquan  2009-05-26 16:03:07 +0800 70)   KBUILD_VERBOSE = $(V)
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 71) endif
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 72) ifndef KBUILD_VERBOSE
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 73)   KBUILD_VERBOSE = 0
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 74) endif
^1da177e4c3f4 (Linus Torvalds 2005-04-16 15:20:36 -0700 75)
066b7ed955808 (Michal Marek   2014-07-04 14:29:30 +0200 76) ifeq ($(KBUILD_VERBOSE),1)
066b7ed955808 (Michal Marek   2014-07-04 14:29:30 +0200 77)   quiet =
066b7ed955808 (Michal Marek   2014-07-04 14:29:30 +0200 78)   Q =
066b7ed955808 (Michal Marek   2014-07-04 14:29:30 +0200 79) else
066b7ed955808 (Michal Marek   2014-07-04 14:29:30 +0200 80)   quiet=quiet_
066b7ed955808 (Michal Marek   2014-07-04 14:29:30 +0200 81)   Q = @
066b7ed955808 (Michal Marek   2014-07-04 14:29:30 +0200 82) endif

請注意,第一個字段是最後一次修改該行的提交的部分 SHA-1
接下來兩個字段的值是從提交中提取出來的——作者的名字以及提交的時間
所以就可以很輕易地知道是誰在什麼時候修改了那一行

接下來就是行號和文件內容
注意一下 ^1da177e4c3f4 這個提交的幾行
其中的前綴 ^ 指出了該文件自第一次提交後從未修改的那些行

會帶來小小的困惑
因爲目前至少已經看到三種 Git 使用 ^ 來修飾一個提交的 SHA-1 值的不同含義

另一件事情是 Git 不會顯式地記錄文件的重命名
它會記錄快照,然後在事後嘗試計算出重命名的動作
這其中有一個特性就是可以讓 Git 找出所有的代碼移動

如果你在 git blame 後面加上一個 -C
Git 會分析正在標註的文件, 並且嘗試找出文件中從別的地方複製過來的代碼片段的原始出處

比如,將 GITServerHandler.m 這個文件拆分爲數個文件,其中一個文件是 GITPackUpload.m
GITPackUpload.m 執行帶 -C 參數的 blame 命令,就可以看到代碼塊的原始出處:

$ git blame -C -L 141,153 GITPackUpload.m
f344f58d GITServerHandler.m (Scott 2009-01-04 141)
f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (void) gatherObjectShasFromC
f344f58d GITServerHandler.m (Scott 2009-01-04 143) {
70befddd GITServerHandler.m (Scott 2009-03-22 144)         //NSLog(@"GATHER COMMI
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 145)
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 146)         NSString *parentSha;
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 147)         GITCommit *commit = [g
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 148)
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 149)         //NSLog(@"GATHER COMMI
ad11ac80 GITPackUpload.m    (Scott 2009-03-24 150)
56ef2caf GITServerHandler.m (Scott 2009-01-05 151)         if(commit) {
56ef2caf GITServerHandler.m (Scott 2009-01-05 152)                 [refDict setOb
56ef2caf GITServerHandler.m (Scott 2009-01-05 153)

這個功能很有用
通常來說,會認爲複製代碼過來的那個提交是最原始的提交
因爲那是第一次在這個文件中修改了這幾行
但 Git 會告訴第一次寫這幾行代碼的那個提交纔是原始提交,即使這是在另外一個文件裏寫的


2. 二分查找

當知道問題是在哪裏引入的情況下文件標註可以幫助你查找問題
如果不知道哪裏出了問題,並且自從上次可以正常運行到現在已經有數十個或者上百個提交
這個時候可以使用 git bisect 來幫助查找
它對你的提交歷史進行二分查找,儘快找到是哪一個提交引入了問題

假設剛剛在線上環境部署了你的代碼,接着收到一些 bug 反饋
但這些 bug 在你之前的開發環境裏沒有出現過
重新查看了你的代碼,發現這個問題是可以被重現的
但是不知道哪裏出了問題

首先執行 git bisect start 來啓動
接着執行 git bisect bad 來告訴系統當前你所在的提交是有問題的
然後必須使用 git bisect good <good_commit>,告訴 bisect 已知的最後一次正常狀態是哪次提交:

$ git bisect start
$ git bisect bad
$ git bisect good v1.0
Bisecting: 6 revisions left to test after this
[ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo

Git 發現在你標記爲正常的提交(v1.0)和當前的錯誤版本之間有大約12次提交
於是 Git 檢出中間的那個提交

現在可以執行測試,看看在這個提交下問題是不是還是存在
如果還存在,說明問題是在這個提交之前引入的
如果問題不存在,說明問題是在這個提交之後引入的

假設測試結果是沒有問題的,你可以通過 git bisect good 來告訴 Git,然後繼續尋找

$ git bisect good
Bisecting: 3 revisions left to test after this
[b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing

現在在另一個提交上了,這個提交是剛剛那個測試通過的提交和有問題的提交的中點
再一次執行測試,發現這個提交下是有問題的,因此你可以通過 git bisect bad 告訴 Git:

$ git bisect bad
Bisecting: 1 revisions left to test after this
[f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table

這個提交是正常的,現在 Git 擁有的信息已經可以確定引入問題的位置在哪裏
它會告訴你第一個錯誤提交的 SHA-1 值並顯示一些提交說明,以及哪些文件在那次提交裏被修改過
這樣就可以找出引入 bug 的根源:

$ git bisect good
b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit
commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04
Author: PJ Hyett <[email protected]>
Date:   Tue Jan 27 14:48:32 2009 -0800

    secure this thing

:040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730
f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M  config

當你完成這些操作之後
應該執行 git bisect reset 重置你的 HEAD 指針到最開始的位置
否則會停留在一個奇怪的狀態:

$ git bisect reset

事實上,如果有一個腳本在項目是正常的情況下返回 0,在不正常的情況下返回非 0
可以使 git bisect 自動化這些操作
首先,設定好項目正常以及不正常所在提交的二分查找範圍
可以通過 bisect start 命令的參數來設定這兩個提交
第一個參數是項目不正常的提交,第二個參數是項目正常的提交:

$ git bisect start HEAD v1.0
$ git bisect run test-error.sh

Git 會自動在每個被檢出的提交裏執行 test-error.sh,直到找到項目第一個不正常的提交
也可以執行 make 或者 make tests 或者其他東西來進行自動化測試


參考: git
以上內容,均根據git官網介紹刪減、添加和修改組成


相關推薦:

Git筆記(33) Rerere
Git筆記(32) 高級合併
Git筆記(31) 重置揭密
Git筆記(30) 重寫歷史
Git筆記(29) 搜索


謝謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章