Open Code Review 项目解析知识图谱 / 分组目录 / 审查引擎 / Git Diff 获取与解析

Git Diff 获取与解析

审查引擎基础方法机制G-B · 审查引擎

What · 是什么

三种 diff 来源(workspace/range/commit)+ unified diff 解析器(ParseDiffText/ParseHunks)
diff 获取支持三种 Provider,按用户场景选择:range(区间对比)、commit(单提交审查)、workspace(工作区未提交改动)
range 模式执行 git diff -U3 --find-renames merge-base..to:对比 merge base 到目标提交的完整差异,-U3 表示 3 行上下文,--find-renames 识别重命名文件
commit 模式执行 git show --diff-merges=first-parent:审查单提交时用 first-parent 避免 merge 提交的组合 diff(combined diff)
workspace 模式覆盖 tracked(已跟踪)与 untracked(未跟踪)文件:合并两者得到工作区全部改动
ParseDiffText 状态机按 diff --git 头切分多个文件条目,ParseHunks 解析 @@ 行(hunk 头)拆分变更块
finalizeDiff 读取新文件全文以补充上下文;DiffContextLines=3 为默认上下文行数,与 -U3 呼应

Why · 为什么

原理 · Principle

三种 Provider 覆盖三种真实审查入口:审查历史区间(range)、审查单个提交(commit)、审查未提交改动(workspace),单一模式无法适配全部场景

--find-renames 保证重命名文件被识别为『移动+修改』而非『删除+新增』,避免审查对重命名文件产生误报

--diff-merges=first-parent 规避 merge 组合 diff 的爆炸性体积与语义混乱:合并提交的 combined diff 会把多个父分支的变更混在一起,first-parent 只与主父分支对比

-U3 与 DiffContextLines=3 统一上下文行数,既给模型足够上下文判断变更影响面,又不让 diff 体积失控

ParseDiffText 状态机与 ParseHunks 是确定性解析:diff 文本结构稳定(--git 头、@@ 行),用代码解析比交给模型理解更可靠、更省 token

How · 怎么做

range 模式:执行 git diff -U3 --find-renames merge-base..to,其中 merge-base 取两端的合并基点,to 为目标提交,输出为统一 diff 格式文本
commit 模式:执行 git show --diff-merges=first-parent <commit>,带 --diff-merges=first-parent 参数避免 merge 组合 diff
workspace 模式:分别获取 tracked(git diff 无参数默认覆盖工作区已跟踪文件)与 untracked(新增未跟踪文件)两路改动后合并
解析流程:ParseDiffText 以 diff --git 为分隔标记按文件切分 diff 文本 → 每个文件条目内 ParseHunks 按 @@ -l,s +l,s @@ 头解析变更块 → 块内含 -U3 提供的上下文行与 +/- 变更行
finalizeDiff 在 hunk 解析后读取新文件全文,用于后续 file_read/评论定位需要完整文件内容的场景
输出供下游:解析后的 diff 结构(文件、hunk、行号、变更类型)注入文件子任务上下文,供 Plan/MainTask 使用

案例 · Cases

代表性案例
审查一个 merge 提交:若用默认 git show 会得到 combined diff(多个父分支变更交织),改用 --diff-merges=first-parent 后 diff 体积与语义回归可控
PR 中包含重命名+微改的文件:--find-renames 使其以 rename 形式呈现,过滤与评论定位按新路径处理,避免误判为删旧增新

反模式 · Anti-patterns

正反实例论证 · Evidence

正例 · Pro
审 merge 提交用 --diff-merges=first-parent
git show --diff-merges=first-parent <merge_commit># 只与主父分支对比,diff 体积与语义可控# 反例(默认行为): git show <merge_commit> 产生 combined diff,多父分支变更交织
first-parent 规避 merge 组合 diff 的体积爆炸与语义混乱:模型上下文不被无关父分支的变更污染
反例 · Con
审 merge 提交用默认 git show 拿到 combined diff
git show <merge_commit> # 默认输出 combined diff# 多个父分支的变更被压缩交织展示(diff --cc),行号语义混乱# 模型上下文被无关父分支变更污染,评论定位失真
combined diff 体积膨胀且语义混乱,模型难以区分哪个父分支引入了哪行变更,评论误报率上升
正例 · Pro
range 模式带 --find-renames 识别重命名文件
git diff -U3 --find-renames merge-base..to# 重命名+微改的文件以 rename 形式呈现(similarity index)# 过滤链与评论定位按新路径处理,不会误判为删旧增新
--find-renames 使重命名文件被识别为『移动+修改』而非『删除+新增』,避免两条记录与路径错位导致的误报
反例 · Con
缺少 --find-renames 时重命名被拆成删除+新增
git diff -U3 merge-base..to # 未开 --find-renames# 旧路径显示为 deleted、新路径显示为 new file# 过滤链对旧路径执行 deleted 处理,评论 path 错位,误报删旧增新
rename 被拆成两条记录后,过滤与评论定位(path)都会产生误报;且删除文件不进审查,重命名中的真实改动可能被漏审

实践资产 · Assets

源码节选
三种 diff Provider 节选:range / commit / workspace
internal/diff/git.go · 2005 B
源码节选
git 子进程并发限流(默认 16)
internal/gitcmd/runner.go · 1271 B

相关边