前言
上一篇文章中,从源码角度找到了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本次新增了一个函数:
functionupdateUserEmail(db,userId,email){db.query("UPDATE users SET email = '"+email+"' WHERE id = "+userId);returntrue;}并且在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
接着执行:
gitdiff--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
继续执行:
gitdiff--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
继续执行:
gitdiff--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 内容
接下来执行:
gitdiff可以看到具体代码差异:
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 changedocr review --preview输出:
Preview: 1 file(s) changed二者对应:
Git 检测到 1 个文件变化 ↓ OCR Preview 显示 1 file(s) changed2. 文件状态对应
git diff --name-status输出:
M s01:用最小 Demo 跑通 AI 代码审查/src/user.jsocr review --preview输出:
[M] s01/src/user.js二者对应:
Git 中的 M ↓ OCR Preview 中的 [M]含义都是:
Modified,文件被修改。3. 新增删除行数对应
git diff --numstat输出:
7 1 s01:用最小 Demo 跑通 AI 代码审查/src/user.jsocr 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--commitabc123它表示:
审查 abc123 这个提交相对于它父提交的代码差异。这种方式适合在某次提交后单独审查。
3. From-To 模式
还可以审查两个 Git 引用之间的差异:
ocr review--frommain--tofeature-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-demo2. 查看 Git 状态
git status--short3. 查看变更文件状态
gitdiff--name-status4. 查看新增和删除行数
gitdiff--numstat5. 查看 diff 统计
gitdiff--stat6. 查看具体 diff 内容
gitdiff7. 查看 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 按什么规则审查代码