Post

Git Blame

整體記憶路線

  1. blame 先看目前行最後誰碰,但不要急著定罪。
  2. log --follow 看檔案是不是搬過。
  3. log -S method 找功能何時出現或搬動。
  4. show commit^:path 看可疑 commit 前一版,排除只是搬程式。
  5. show --name-status 用 R100 確認是不是完整搬檔。
  6. log -S 壞掉特徵 搜危險字串,找真正改壞的 commit。
  7. 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 等檔案狀態。
  • R100 100% 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= '" 找直接拼接年份條件的出現時間。
  • 為什麼不用只搜 UYEAR UYEAR 太普通,安全版和危險版都有。

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/... 只看指定檔案,不被其他檔案干擾。
This post is licensed under CC BY 4.0 by the author.