Git Blame
整體記憶路線
blame先看目前行最後誰碰,但不要急著定罪。log --follow看檔案是不是搬過。log -S method找功能何時出現或搬動。show commit^:path看可疑 commit 前一版,排除只是搬程式。show --name-status用 R100 確認是不是完整搬檔。log -S 壞掉特徵搜危險字串,找真正改壞的 commit。show --patch
看 diff,確認問題就是這次改出來的。
1. 先看目前某幾行最後被誰碰過
1
git blame -L 1276,1328 -- src/main/java/TsLib/dao/TsRecordDao.java
背法:blame 只回答「目前這些行最後誰碰過」,不是一定回答「誰把邏輯寫壞」。
這次案例:這次 blame 指到 UserA,但那只是目前版本附近最後被整理過。後來證明真正把 SQL 改成字串拼接的不是這一步。
-L 1276,1328只看 1276 到 1328 行。查局部問題時一定要縮小範圍。--後面是檔案路徑,不是參數。
2. 看檔案歷史,判斷是不是被搬過
1
git log --oneline --decorate --follow -- src/main/java/TsLib/dao/TsRecordDao.java
背法:log 看歷史,–follow 讓 Git 沿著 rename / 搬檔繼續追。
這次案例:這次看到 d49c17b「調整成 maven 目錄結構」,代表 src/main/java/… 可能不是原始路徑。這個 log 變成追舊路徑的路標。
--oneline每個 commit 一行,快速掃。--decorate顯示 branch / tag 等標籤。--follow檔案搬家也繼續追。
3. 用 method 名稱找這段功能何時搬動
1
git log --all --date=iso --stat -S 'getReportDataForNotice' -- src/main/java/TsLib/dao/TsRecordDao.java src/main/java/TsLib/report/GenerateRptUtil.java
背法:-S 是 pickaxe:找某段文字的出現次數在哪些 commit 有變化。 這次案例:這次用 method 名稱找到 0ccae34。它看起來是把報表內容查詢從 GenerateRptUtil 整理到 TsRecordDao,但還不能直接認定它是兇手。
--all不只找目前 branch,也找其他 refs。--date=iso日期清楚,方便寫追查紀錄。--stat先看改了哪些檔案,不急著看完整 diff。-S 'getReportDataForNotice'找 method 名稱的增減。
4. 看可疑 commit 前一版,確認是不是只是搬程式
1
git show 0ccae34^:src/main/java/TsLib/report/GenerateRptUtil.java | rg -n -C 8 'getReportDataForNotice|P\.NAME LIKE|U\.UNITN2 LIKE|UYEAR='
背法:commit^ 是該 commit 的前一版。冒號後面接那個版本裡的檔案路徑。 這次案例:這次看 0ccae34 的前一版,發現 GenerateRptUtil 只是呼叫 tsRecDao.getReportDataForNotice(…),真正 SQL 早就在 TsRecordDao。這一步排除了錯誤方向。
0ccae34^0ccae34的父 commit,也就是改之前。:src/...看父 commit 裡的這個檔案。rg -n -C 8用 ripgrep 搜關鍵字,顯示行號,前後各 8 行。
5. 確認 Maven 目錄整理是不是完整搬檔
1
git show --name-status --date=iso --format=fuller d49c17b
背法:–name-status 看檔案狀態;R100 代表 100% rename。
這次案例:這次看到 R100 src/TsLib/dao/TsRecordDao.java → src/main/java/TsLib/dao/TsRecordDao.java,所以 d49c17b 只是搬檔,不是重寫邏輯。
--name-status看 A/M/D/R 等檔案狀態。R100100% rename,通常是完整搬移。--format=fuller顯示完整作者、提交者、時間。
6. 沿新舊路徑找 method 真正出現時間
1
git log --all --date=iso --stat -S 'public ArrayList<TsNotice> getReportDataForNotice' -- src/TsLib/dao/TsRecordDao.java src/main/java/TsLib/dao/TsRecordDao.java
背法:搬過檔時,-S 後面同時放舊路徑與新路徑,避免只查到搬家後的歷史。
這次案例:這次找到 2017-04-28 的 1da3e67。原始版本其實還是用 ?,雖然 LIKE 參數多包單引號不漂亮,但還不是直接 SQL 拼接。
-S 'public ...'比只搜 method 名稱更精準。src/TsLib/...舊路徑。src/main/java/...新路徑。
7. 找是誰把安全寫法改成危險拼接
1
git log --all --date=iso --stat -S "AND UYEAR= '" -- src/TsLib/dao/TsRecordDao.java src/main/java/TsLib/dao/TsRecordDao.java
背法:不要只搜 method,也要搜「壞掉後才會出現的特徵字串」。
這次案例:這次真正關鍵是搜 AND UYEAR= ‘,因為這種字串代表 SQL 開始直接拼使用者輸入。最後找到 aa8f806b。
-S "AND UYEAR= '"找直接拼接年份條件的出現時間。為什麼不用只搜 UYEARUYEAR 太普通,安全版和危險版都有。
8. 看真正造成行為變化的 patch
1
git show --date=iso --format=fuller --patch aa8f806b -- src/TsLib/dao/TsRecordDao.java
背法:–patch 才是看這個 commit 實際改了什麼。 這次案例:這次在 patch 裡看到 ? 被改成 ‘+YEAR+’、LIKE 也改成 ‘+NAME+’。這一步才真正能說:問題是在這個 commit 產生的。
--patch顯示 diff。抓兇手最後一定要看 patch。aa8f806b這次查到的可疑 commit。-- src/...只看指定檔案,不被其他檔案干擾。