从 0 学习 Alibaba Open Code Review(三):Git Diff 解析流程
前言
上一篇文章中,从源码角度找到了 ocr review 的命令入口。
当用户执行:
ocr review
程序会从 cmd/opencodereview/main.go 进入命令分发逻辑,然后进入:
runReview(args[1:])
接着会调用:
parseReviewFlags(args)
解析命令行参数。
也就是说,第二篇主要解决的是:
ocr review 是从哪里启动的?
这一篇继续往下学习一个更基础、也更关键的问题:
ocr review 为什么知道我改了哪个文件?
在第一篇中,执行过:
ocr review --preview
终端输出:
Preview: 1 file(s) changed | +7 -1
Will review (1):
[M] s01/src/user.js
当时只是知道:
OpenCodeReview 识别到了 s01/src/user.js 的变更。
但是还不清楚:
[M] 是什么意思?
+7 -1 是怎么来的?
为什么它知道要审查这个文件?
ocr review --preview 和 git diff 有什么关系?
所以本文就围绕这些问题,学习 Open Code Review 的核心输入:Git Diff。
一、本篇学习目标
本文主要解决下面几个问题:
1. 什么是 Git diff?
2. 为什么 ocr review 依赖 Git diff?
3. git status、git diff 和 ocr review --preview 有什么关系?
4. [M] 表示什么?
5. +7 -1 是怎么统计出来的?
6. 为什么 Git diff 中会出现 -1?
7. ocr review --preview 做了什么?
这一篇关注:
Open Code Review 是如何识别本次代码变更的?
二、为什么先学习 Git Diff
ocr review 和 ocr scan 不一样。
ocr scan 更偏向全量扫描目录或文件,而 ocr review 的核心是审查本次代码变更。
也就是说,ocr review 首先要知道:
这次改了哪些文件?
每个文件新增了多少行?
每个文件删除了多少行?
哪些文件需要审查?
哪些文件应该排除?
这些信息主要来自 Git。
所以可以先简单理解为:
Git diff 是 ocr review 的输入。
如果没有 Git diff,ocr review 就不知道本次要审查哪些代码变更。
这也是为什么 Open Code Review 适合用于:
本地未提交变更审查
commit 审查
分支差异审查
Pull Request 审查
因为这些场景的本质都是:
比较两份代码之间的差异。
三、准备一个代码变更
为了方便观察 Git diff,在练习项目中修改了:
s01/src/user.js
本次新增了一个函数:
function updateUserEmail(db, userId, email) {
db.query("UPDATE users SET email = '" + email + "' WHERE id = " + userId);
return true;
}
并且在 module.exports 中导出了它:
module.exports = {
getUserName,
login,
buildUserQuery,
fetchUser,
deleteUser,
updateUserEmail
};
这个函数写得比较简单,而且包含明显的问题:
1. SQL 字符串拼接;
2. 没有参数化查询;
3. 没有错误处理;
4. 总是 return true。
不过这一篇不分析代码审查结果,只分析 Git diff 和 preview 输出。
四、查看当前 Git 状态
首先进入练习项目目录:
cd D:\agent\open-code-review-main\ocr-practice-demo
然后执行:
git status --short
输出结果中可以看到:
M s01/src/user.js
这里的:
M
表示文件被修改了。
也就是说,Git 已经检测到:
s01/src/user.js 发生了修改。
注意这里的 M 是 Git 原生输出中的文件状态。
后面 ocr review --preview 中看到的:
[M]
也是类似含义,表示:
Modified,文件被修改。
五、查看变更文件状态:git diff --name-status
接着执行:
git diff --name-status
输出类似:
M s01/src/user.js
这条命令只关心:
哪些文件变了?
这些文件是什么状态?
其中:
M = Modified,修改文件
A = Added,新增文件
D = Deleted,删除文件
R = Renamed,重命名文件
所以这次输出:
M s01/src/user.js
表示:
s01/src/user.js 是一个被修改的文件。
这和 ocr review --preview 中的:
[M] s01/src/user.js
是对应的。
六、查看新增和删除行数:git diff --numstat
继续执行:
git diff --numstat
输出结果:
7 1 s01/src/user.js
这三个部分分别表示:
7 新增行数
1 删除行数
文件路径
所以这次变更可以理解为:
src/user.js 新增了 7 行,删除了 1 行。
这个输出可以帮助我们验证 ocr review --preview 中 +7 -1 的含义:
7 insertions → +7
1 deletion → -1
需要注意:这里是用 git diff --numstat 辅助理解 Preview 输出,并不表示 OpenCodeReview 源码中直接调用了 git diff --numstat。
从源码角度看,OpenCodeReview 会先获取 unified diff 文本,再解析 diff 内容,统计每个文件的新增行和删除行。
七、查看 diff 统计:git diff --stat
继续执行:
git diff --stat
输出结果:
.../src/user.js" | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
这条输出更适合人阅读。
它说明:
1 个文件发生变化;
新增了 7 行;
删除了 1 行。
其中:
7 insertions(+)
表示新增 7 行。
1 deletion(-)
表示删除 1 行。
这和前面的 git diff --numstat、ocr review --preview 是一致的。
可以整理成下面的对应关系:
| 命令 | 输出 | 含义 |
|---|---|---|
git diff --name-status |
M s01/src/user.js |
文件被修改 |
git diff --numstat |
7 1 s01/src/user.js |
新增 7 行,删除 1 行 |
git diff --stat |
1 file changed, 7 insertions, 1 deletion |
变更统计 |
ocr review --preview |
[M] src/user.js +7 -1 |
Open Code Review 预览审查范围 |
八、查看具体 diff 内容
接下来执行:
git diff
可以看到具体代码差异:
diff --git a/s01:用最小 Demo 跑通 AI 代码审查/src/user.js b/s01:用最小 Demo 跑通 AI 代码审查/src/user.js
index 1445e1f..f9f2188 100644
--- a/s01/src/user.js
+++ b/s01/src/user.js
@@ -33,10 +33,16 @@ function deleteUser(db, userId) {
return true;
}
+function updateUserEmail(db, userId, email) {
+ db.query("UPDATE users SET email = '" + email + "' WHERE id = " + userId);
+ return true;
+}
+
module.exports = {
getUserName,
login,
buildUserQuery,
fetchUser,
- deleteUser
+ deleteUser,
+ updateUserEmail
};
这段 diff 是理解 +7 -1 的关键。
其中这一行叫做 hunk header:
@@ -33,10 +33,16 @@ function deleteUser(db, userId) {
可以简单理解为:
-33,10 表示旧文件从第 33 行开始,共 10 行;
+33,16 表示新文件从第 33 行开始,共 16 行。
这类行号信息后面会非常重要,因为 Agent 最终生成的 code_comment 需要定位到具体文件和具体代码行。
九、理解 diff 中的 + 和 -
在 Git diff 中:
+ 表示新增行
- 表示删除行
例如下面这几行:
+function updateUserEmail(db, userId, email) {
+ db.query("UPDATE users SET email = '" + email + "' WHERE id = " + userId);
+ return true;
+}
+
这些都是新增行。
所以它们会被统计到:
+7
而这一行:
- deleteUser
表示旧版本中原来是:
deleteUser
后来变成了:
+ deleteUser,
+ updateUserEmail
也就是:
deleteUser,
updateUserEmail
所以 Git 认为:
旧的 deleteUser 这一行被删除;
新的 deleteUser, 这一行被新增;
updateUserEmail 这一行也被新增。
这就是为什么只是加了一个逗号,却会出现:
-1
十、执行 ocr review --preview
接下来执行 Open Code Review 的预览命令:
ocr review --preview
输出结果:
[ocr] A new version (v1.7.0) is available. Run to update:
npm i -g @alibaba-group/open-code-review@1.7.0
Preview: 1 file(s) changed | +7 -1
Will review (1):
[M] s01/src/user.js
真正重要的是:
Preview: 1 file(s) changed | +7 -1
以及:
Will review (1):
[M] s01/src/user.js
这说明 Open Code Review 识别到了:
1 个文件发生变化;
新增 7 行;
删除 1 行;
这个文件会被纳入审查;
文件状态是 Modified。
十一、把 Git 输出和 OCR Preview 对应起来
现在可以把 Git 原生命令和 Open Code Review Preview 输出对应起来。
1. 文件数量对应
git diff --stat 输出:
1 file changed
ocr review --preview 输出:
Preview: 1 file(s) changed
二者对应:
Git 检测到 1 个文件变化
↓
OCR Preview 显示 1 file(s) changed
2. 文件状态对应
git diff --name-status 输出:
M s01:用最小 Demo 跑通 AI 代码审查/src/user.js
ocr review --preview 输出:
[M] s01/src/user.js
二者对应:
Git 中的 M
↓
OCR Preview 中的 [M]
含义都是:
Modified,文件被修改。
3. 新增删除行数对应
git diff --numstat 输出:
7 1 s01:用最小 Demo 跑通 AI 代码审查/src/user.js
ocr review --preview 输出:
+7 -1
二者对应:
7 insertions
↓
+7
1 deletion
↓
-1
所以 ocr review --preview 展示的 +7 -1,本质上来自本次 Git diff 的新增和删除行数统计。
十二、ocr review --preview 的作用
通过前面的对比可以发现,ocr review --preview 的作用是:
在不真正调用 LLM 的情况下,
提前告诉我这次会审查哪些文件。
它可以帮助我在正式执行审查前确认几个问题:
1. 当前是否有 Git 变更;
2. 哪些文件会被审查;
3. 哪些文件被排除;
4. 每个文件的变更规模是多少;
5. 文件状态是新增、修改还是删除。
这一步非常重要。
因为正式执行:
ocr review
会调用 LLM,可能产生 token 消耗和等待时间。
所以在正式 review 前,先执行:
ocr review --preview
可以避免一些问题。
例如:
不小心审查了无关文件;
忘记忽略生成文件;
把 JSON 结果文件也纳入了 review;
当前其实没有代码变更;
审查范围比预期大很多。
因此,使用习惯是:
先执行 ocr review --preview
确认范围没问题
再执行 ocr review
十三、为什么 git diff 和 Preview 有时对不上
本文示例中的修改是未暂存的 tracked 文件,所以 git diff、git diff --numstat 和 ocr review --preview 的结果可以很好地对应起来。
但在真实项目里,有时你会发现:
git diff 看不到某些文件,
ocr review --preview 却能看到。
原因是 OpenCodeReview 的 workspace 模式不只是简单执行裸 git diff。
从源码逻辑看,workspace 模式会处理两类变更:
1. tracked 文件变更:通过 git diff HEAD 获取相对 HEAD 的变更;
2. untracked 文件变更:通过 git ls-files --others --exclude-standard 找到未跟踪文件,并手动构造 diff。
这意味着:
如果文件已经 git add,普通 git diff 可能看不到,因为它默认比较工作区和暂存区;
如果文件是 untracked,普通 git diff 也看不到,因为它还没有进入 Git 跟踪;
但 ocr review --preview 仍然可能把它们纳入审查范围。
所以在对照 OCR Preview 时,更接近的理解方式是:
tracked changes: git diff HEAD
untracked files: git ls-files --others --exclude-standard
而不是只看:
git diff
十四、哪些变更文件不会进入 Will review
ocr review --preview 不只是列出 Git diff 中出现的文件,还会判断这些文件是否真的会进入审查。
Preview 输出里有两个重要区域:
Will review
Excluded from review
不是所有发生变更的文件都会进入 Will review。OpenCodeReview 在拿到 diff 后,还会经过文件过滤逻辑。
常见被排除的原因包括:
1. 二进制文件;
2. 被 .gitignore 或内置目录规则排除;
3. 被 .opencodereview/rule.json 的 exclude 命中;
4. 文件扩展名不在支持范围内;
5. 位于默认排除路径,例如 node_modules、vendor 等;
6. 纯删除文件。
所以 ocr review --preview 的价值不只是告诉我“哪些文件变了”,还会告诉我:
哪些文件最终会被审查;
哪些文件虽然变了,但会被排除。
这也为下一篇分析 rule.json 的 exclude、path 和规则匹配做了铺垫。
十五、从源码角度简单理解 Preview 流程
上一篇已经找到 ocr review 的入口在:
cmd/opencodereview/main.go
并且会进入:
runReview(args[1:])
在 runReview 中,程序会先解析参数。
当用户执行:
ocr review --preview
时,参数解析后可以简单理解为:
opts.preview = true
然后 runReview 会根据这个参数进入 preview 分支。
整体流程可以先简单理解为:
用户执行 ocr review --preview
↓
main.go
↓
dispatch()
↓
runReview(args[1:])
↓
parseReviewFlags(args)
↓
opts.preview = true
↓
runPreview
↓
ag.Preview
↓
loadDiffs
↓
diff.Provider.GetDiff
↓
diff.ParseDiffText
↓
生成 model.Diff
↓
whyExcluded / diffStatus
↓
outputPreviewText
↓
终端显示 Preview 结果
这里最重要的是 model.Diff。
它可以理解为 OpenCodeReview 内部对“单个文件变更”的结构化表示,里面会保存:
OldPath / NewPath
Diff 原始文本
NewFileContent 新文件内容
IsBinary / IsDeleted / IsNew / IsRenamed
Insertions / Deletions
所以 ocr review --preview 展示的文件路径、状态、+7 -1,不是凭空来的,而是来自 model.Diff 中的结构化字段。
目前只需要知道:
Preview 模式不会真正调用 LLM;
它会加载 Git diff,解析成 model.Diff,应用文件过滤规则,然后渲染预览结果。
十六、理解几种常见文件状态
在 Git diff 或 OCR Preview 中,常见文件状态包括:
| 状态 | 含义 |
|---|---|
M / [M] |
Modified,文件被修改 |
A / [A] |
Added,新增文件 |
D / [D] |
Deleted,删除文件 |
R / [R] |
Renamed,文件重命名 |
B / [B] |
Binary,二进制文件 |
本次输出中出现的是:
[M]
所以表示:
s01/src/user.js 是一个被修改的文件。
如果后续新增一个文件,例如:
src/order.js
那么可能看到:
[A] src/order.js
如果删除一个文件,可能看到:
[D] src/old.js
这些状态都来自 Git 对文件变更的判断。
十七、理解三种 review 场景
虽然本文主要使用的是当前工作区变更,但 ocr review 还可以用于其他场景。
1. 默认工作区模式
直接执行:
ocr review
或者:
ocr review --preview
通常审查的是当前工作区中的变更。
也就是还没有提交的修改。
这是我目前练习时最常用的模式。
2. Commit 模式
也可以审查某一个 commit:
ocr review --commit abc123
它表示:
审查 abc123 这个提交相对于它父提交的代码差异。
这种方式适合在某次提交后单独审查。
3. From-To 模式
还可以审查两个 Git 引用之间的差异:
ocr review --from main --to feature-branch
它表示:
审查 main 到 feature-branch 之间的代码变化。
这类模式更接近 Pull Request 或分支对比场景。
十八、为什么 Git Diff 是 Agent Review 的第一步
现在可以重新理解 ocr review 的流程。
一开始以为它大概是:
把代码发给 LLM
↓
LLM 给出评论
但现在通过 Git diff 观察,发现它至少要先完成:
识别变更文件
统计新增删除行数
判断文件状态
过滤不需要审查的文件
确定本次审查范围
这些都属于 LLM 之前的确定性工程处理。
也就是说,ocr review 的第一步不是调用大模型,而是:
先通过 Git diff 确定输入。
然后后续才会进入:
规则匹配
文件读取
上下文检索
Agent 工具调用
LLM 审查
评论输出
所以 Git diff 是整个代码审查流程的起点。
十九、本篇使用的 PowerShell 命令记录
为了方便复盘,本文主要使用了下面这些命令。
1. 进入练习项目
cd D:\agent\open-code-review-main\ocr-practice-demo
2. 查看 Git 状态
git status --short
3. 查看变更文件状态
git diff --name-status
4. 查看新增和删除行数
git diff --numstat
5. 查看 diff 统计
git diff --stat
6. 查看具体 diff 内容
git diff
7. 查看 OCR Preview
ocr review --preview
二十、本篇总结
通过这一篇,对 ocr review 的输入有了更清晰的理解。
本文最核心的结论是:
ocr review 的核心输入是 Git diff。
Git diff 会告诉 Open Code Review:
哪些文件发生了变化;
文件是新增、修改还是删除;
新增了多少行;
删除了多少行;
具体变更内容是什么。
本次示例中,Git 检测到:
1 个文件被修改;
新增 7 行;
删除 1 行。
所以 ocr review --preview 输出:
Preview: 1 file(s) changed | +7 -1
Will review (1):
[M] s01:用最小 Demo 跑通 AI 代码审查/src/user.js
其中:
[M] 表示文件被修改;
+7 表示新增 7 行;
-1 表示删除 1 行。
而 -1 的来源是:
- deleteUser
+ deleteUser,
也就是说,它不是删除了业务逻辑,而是 Git 认为原来的 deleteUser 这一行被替换成了新的 deleteUser,。
通过这一篇,已经能把下面几类输出对应起来:
git status --short
git diff --name-status
git diff --numstat
git diff --stat
ocr review --preview
二十一、下一篇:自定义规则 rule.json 解析
第一篇中,已经配置过:
.opencodereview/rule.json
并且通过:
ocr rules check src/user.js
验证了规则可以命中 JavaScript 文件。
第三篇解决的是:
ocr review 的输入 Git diff 是怎么来的?
下一篇准备继续分析:
从 0 学习 Alibaba Open Code Review:自定义规则 rule.json 解析
下一篇主要解决这些问题:
1. 为什么 .opencodereview/rule.json 能影响审查结果?
2. path: **/*.js 是如何匹配 src/user.js 的?
3. rule 字段如何影响 Agent 的审查重点?
4. merge_system_rule: true 是什么意思?
5. exclude 如何影响审查范围?
6. ocr rules check 的输出应该怎么看?
到这里,学习路线就从:
ocr review 能做什么
进入到:
ocr review 看哪些代码
再继续进入:
ocr review 按什么规则审查代码
更多推荐




所有评论(0)