7.10 使用 Git 調(diào)試

2018-02-24 15:22 更新

使用 Git 調(diào)試

Git 也提供了兩個(gè)工具來輔助你調(diào)試項(xiàng)目中的問題。 由于 Git 被設(shè)計(jì)成適用于幾乎所有類型的項(xiàng)目,這些工具是比較通用的,但它們可以在出現(xiàn)問題的時(shí)候幫助你找到 bug 或者錯(cuò)誤。

文件標(biāo)注

如果你在追蹤代碼中的一個(gè) bug,并且想知道是什么時(shí)候以及為何會(huì)引入,文件標(biāo)注通常是最好用的工具。 它展示了文件中每一行最后一次修改的提交。 所以,如果你在代碼中看到一個(gè)有問題的方法,你可以使用?git blame?標(biāo)注這個(gè)文件,查看這個(gè)方法每一行的最后修改時(shí)間以及是被誰修改的。 這個(gè)例子使用?-L?選項(xiàng)來限制輸出范圍在第12至22行:

$ git blame -L 12,22 simplegit.rb
^4832fe2 (Scott Chacon  2008-03-15 10:31:28 -0700 12)  def show(tree = 'master')
^4832fe2 (Scott Chacon  2008-03-15 10:31:28 -0700 13)   command("git show #{tree}")
^4832fe2 (Scott Chacon  2008-03-15 10:31:28 -0700 14)  end
^4832fe2 (Scott Chacon  2008-03-15 10:31:28 -0700 15)
9f6560e4 (Scott Chacon  2008-03-17 21:52:20 -0700 16)  def log(tree = 'master')
79eaf55d (Scott Chacon  2008-04-06 10:15:08 -0700 17)   command("git log #{tree}")
9f6560e4 (Scott Chacon  2008-03-17 21:52:20 -0700 18)  end
9f6560e4 (Scott Chacon  2008-03-17 21:52:20 -0700 19)
42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 20)  def blame(path)
42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 21)   command("git blame #{path}")
42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 22)  end

請(qǐng)注意,第一個(gè)字段是最后一次修改該行的提交的部分 SHA-1 值。 接下來兩個(gè)字段的值是從提交中提取出來的——作者的名字以及提交的時(shí)間——所以你就可以很輕易地找到是誰在什么時(shí)候修改了那一行。 接下來就是行號(hào)和文件內(nèi)容。 注意一下?^4832fe2?這個(gè)提交的那些行,這些指的是這個(gè)文件第一次提交的那些行。 這個(gè)提交是這個(gè)文件第一次加入到這個(gè)項(xiàng)目時(shí)的提交,并且這些行從未被修改過。 這會(huì)帶來小小的困惑,因?yàn)槟阋呀?jīng)至少看到三種 Git 使用?^?來修飾一個(gè)提交的 SHA-1 值的不同含義,但這里確實(shí)就是這個(gè)意思。

另一件比較酷的事情是 Git 不會(huì)顯式地記錄文件的重命名。 它會(huì)記錄快照,然后在事后嘗試計(jì)算出重命名的動(dòng)作。 這其中有一個(gè)很有意思的特性就是你可以讓 Git 找出所有的代碼移動(dòng)。 如果你在git blame?后面加上一個(gè)?-C,Git 會(huì)分析你正在標(biāo)注的文件,并且嘗試找出文件中從別的地方復(fù)制過來的代碼片段的原始出處。 比如,你將?GITServerHandler.m?這個(gè)文件拆分為數(shù)個(gè)文件,其中一個(gè)文件是?GITPackUpload.m。 對(duì)?GITPackUpload.m?執(zhí)行帶?-C?參數(shù)的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)

這個(gè)功能很有用。 通常來說,你會(huì)認(rèn)為復(fù)制代碼過來的那個(gè)提交是最原始的提交,因?yàn)槟鞘悄愕谝淮卧谶@個(gè)文件中修改了這幾行。 但 Git 會(huì)告訴你,你第一次寫這幾行代碼的那個(gè)提交才是原始提交,即使這是在另外一個(gè)文件里寫的。

二分查找

當(dāng)你知道問題是在哪里引入的情況下文件標(biāo)注可以幫助你查找問題。 如果你不知道哪里出了問題,并且自從上次可以正常運(yùn)行到現(xiàn)在已經(jīng)有數(shù)十個(gè)或者上百個(gè)提交,這個(gè)時(shí)候你可以使用?git bisect來幫助查找。?bisect?命令會(huì)對(duì)你的提交歷史進(jìn)行二分查找來幫助你盡快找到是哪一個(gè)提交引入了問題。

假設(shè)你剛剛在線上環(huán)境部署了你的代碼,接著收到一些 bug 反饋,但這些 bug 在你之前的開發(fā)環(huán)境里沒有出現(xiàn)過,這讓你百思不得其解。 你重新查看了你的代碼,發(fā)現(xiàn)這個(gè)問題是可以被重現(xiàn)的,但是你不知道哪里出了問題。 你可以用二分法來找到這個(gè)問題。 首先執(zhí)行?git bisect start?來啟動(dòng),接著執(zhí)行?git bisect bad?來告訴系統(tǒng)當(dāng)前你所在的提交是有問題的。 然后你必須告訴 bisect 已知的最后一次正常狀態(tài)是哪次提交,使用?git bisect good [good_commit]

$ 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 發(fā)現(xiàn)在你標(biāo)記為正常的提交(v1.0)和當(dāng)前的錯(cuò)誤版本之間有大約12次提交,于是 Git 檢出中間的那個(gè)提交。 現(xiàn)在你可以執(zhí)行測(cè)試,看看在這個(gè)提交下問題是不是還是存在。 如果還存在,說明問題是在這個(gè)提交之前引入的;如果問題不存在,說明問題是在這個(gè)提交之后引入的。 假設(shè)測(cè)試結(jié)果是沒有問題的,你可以通過?git bisect good?來告訴 Git,然后繼續(xù)尋找。

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

現(xiàn)在你在另一個(gè)提交上了,這個(gè)提交是剛剛那個(gè)測(cè)試通過的提交和有問題的提交的中點(diǎn)。 你再一次執(zhí)行測(cè)試,發(fā)現(xiàn)這個(gè)提交下是有問題的,因此你可以通過?git bisect bad?告訴 Git:

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

這個(gè)提交是正常的,現(xiàn)在 Git 擁有的信息已經(jīng)可以確定引入問題的位置在哪里。 它會(huì)告訴你第一個(gè)錯(cuò)誤提交的 SHA-1 值并顯示一些提交說明,以及哪些文件在那次提交里修改過,這樣你可以找出引入 bug 的根源:

$ git bisect good
b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit
commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04
Author: PJ Hyett <pjhyett@example.com>
Date:   Tue Jan 27 14:48:32 2009 -0800

    secure this thing

:040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730
f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M  config

當(dāng)你完成這些操作之后,你應(yīng)該執(zhí)行?git bisect reset?重置你的 HEAD 指針到最開始的位置,否則你會(huì)停留在一個(gè)很奇怪的狀態(tài):

$ git bisect reset

這是一個(gè)可以幫助你在幾分鐘內(nèi)從數(shù)百個(gè)提交中找到 bug 的強(qiáng)大工具。 事實(shí)上,如果你有一個(gè)腳本在項(xiàng)目是正常的情況下返回 0,在不正常的情況下返回非 0,你可以使?git bisect?自動(dòng)化這些操作。 首先,你設(shè)定好項(xiàng)目正常以及不正常所在提交的二分查找范圍。 你可以通過?bisect start命令的參數(shù)來設(shè)定這兩個(gè)提交,第一個(gè)參數(shù)是項(xiàng)目不正常的提交,第二個(gè)參數(shù)是項(xiàng)目正常的提交:

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

Git 會(huì)自動(dòng)在每個(gè)被檢出的提交里執(zhí)行?test-error.sh?直到找到第一個(gè)項(xiàng)目不正常的提交。 你也可以執(zhí)行?make?或者?make tests?或者其他東西來進(jìn)行自動(dòng)化測(cè)試。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)