internal/diff/ — Git diff 載入與解析

git.go(三種模式 Provider)+ parser.go(unified diff 解析)
套件:internal/diff/(git.go + parser.go + hunk.go + resolver.go + relocation.go)

大方向

internal/diff 是 OCR「讀取變更」的層。兩個重點:git.goProvider 用 shell 呼叫 git 產生三種模式的 diff;parser.goParseDiffText 把 unified diff 文字解析成結構化的 []model.Diff(含 hunk、插入/刪除計數、重新命名偵測)。


1. Provider 與三種模式

Mode 列舉 · NewProvider() · NewCommitProvider() · NewWorkspaceProvider() git.go三種 diff 模式的建構入口。

Mode 是 int 列舉(Workspace/Commit/Range)。NewProvider(repoDir, from, to, runner) 建 range 模式;NewCommitProvider(repoDir, commit, runner) 建 commit 模式;NewWorkspaceProvider 建工作區模式。三個都共用 gitcmd.Runner——它把 git 子程序執行透過 concurrency limiter 共享,避免同時開太多 git process。

GetDiff() · ResolveInput() · computeMergeBase() git.go依模式跑對應 git 指令,組出 []model.Diff。

ResolveInput 判定模式並解析 ref(merge-base、commit 的 parents)。GetDiff 依模式跑:workspace 用 git diff HEAD + git ls-files --others(untracked 從磁碟讀當整檔新增);commit 用 git show;range 用 merge-base(a,b)..b。雜訊目錄(vendor/node_modules…)在 filterDiffs 剔除。

2. Unified diff 解析

ParseDiffText() parser.go把 unified diff 文字解析成 per-file Diff 結構。

核心是狀態機:用正則 diffHeaderRe 偵測 diff --git a/.. b/.. 開新檔區段,用 inHunk 旗標區分「hunk 內容」(有 +/-/空格前綴)與「extended header」。有趣的細節:+++i 這種「內容本身以 + 開頭」的 added line,靠 inHunk 狀態正確計數。index 行被略過(物件 ID 對評審無用)。每個檔區段結束呼叫 finalizeDiff

教學重點inHunk 旗標是正確解析 unified diff 的關鍵——沒有它,「--- a/x」「+++ b/y」這類 header 會被誤算成插入/刪除。
finalizeDiff() parser.go讀新檔內容(git show ref 或工作樹)。

range/commit 模式用 git show ref:path 讀新檔內容;workspace 從磁碟讀。內容存進 Diff.NewFileContent——供 file_read 工具與行解析使用。刪除檔設 /dev/null

3. 行解析與重新定位

hunk.go · resolver.go · relocation.go internal/diffhunk 解析、existing_code → 行號解析、重新定位。

hunk.goParseHunks@@ -x,y +m,n @@ 區段拆成 HunkLine。動態滑動視窗行解析(ResolveLineNumbers)把 existing_code 文字比對到新/舊側行號;失敗時 relocation.goRE_LOCATION_TASK prompt 請模型重新錨定。

看完這頁你應該能說出:三種模式各跑哪些 git 指令、ParseDiffText 的狀態機與 inHunk 旗標為何關鍵、finalizeDiff 如何補新檔內容、以及行解析的「滑動視窗 → 重新定位」兩階段。

下一步:internal/config/rules 或回程式碼對照