在线文本对比完全指南:LCS/Myers 算法 + 6 大场景 + diff 选型
在线文本对比(text diff)工具要回答的问题很简单:A 版和 B 版到底改了什么。把两段文本粘贴进去,工具用最长公共子序列(LCS)算法跑一遍,就能在并排视图或统一 diff 视图中看到每一处插入、删除和修改,用时通常不到一毫秒。
本文面向做代码审查的开发者、对比日志片段的 SRE、给合同划红线的律师、审稿的写作者。内容覆盖:算法(LCS、Myers、Patience)、两种标准视图、能解决 95% 「全都变了」抱怨的忽略选项、什么时候该改用 JSON diff、六个可复制粘贴的实战场景,以及那些用算法本身就能解释的坑。
想直接对比两段文本?打开文本对比工具,全部计算在浏览器本地完成,文本不上传。
1. 什么是文本对比(text diff)?
文本对比就是用最少的插入与删除操作,把一段文本变成另一段文本,并在每行上标注「新增」「删除」或「未变」。常用的 diff 还会在词或字符级别做第二轮扫描,这样改动一个字符时只会高亮那个 token,而不是整行。
1.1 为什么字符相等(===)远远不够
在一个 200 行的配置文件最上面插入一行,朴素的字符比对会把插入点之后的每一个字节都报告为不同。文本本身没变,只是位置移了。diff 算法必须能识别出「后面 199 行还是原来那些行,只是整体下移了一行」,并把它报告为一次插入。这就是 LCS 提供的能力,也是 git、GitHub 以及每一个代码审查工具都自带 diff 引擎的原因。
1.2 side-by-side 与 unified diff
side-by-side(并排视图)把两个版本放在两栏中并行排列,用颜色给单元格上色:绿色代表新增,红色代表删除,黄色代表修改。Unified diff(统一 diff 格式)是 GNU diff 留下来的老牌文本格式,单栏排版,行首用 - 和 + 标记,每个 hunk 上下各保留三行上下文。同一份对比结果,两种呈现方式。第 4 节会讲什么时候用哪种。
1.3 文本对比的应用场景
GitHub 和 GitLab 上的代码审查。本地 git diff 输出。粘贴到 Slack 里的 patch。合同划红线。翻译稿审校。CI 中那些用 +/- 报错的快照测试。事故时间线日志排查。比较两份 .env 文件。任何需要逐行匹配两块文本的场景。
打开文本对比工具粘贴两段文本,就能看到上述所有场景实际运转的样子,每一次比对都在浏览器本地完成。
2. 文本对比背后的算法(LCS + Myers + Patience)
2.1 最长公共子序列(LCS)
给定两个行序列 A 和 B,最长公共子序列就是在保持原有顺序、不要求相邻的前提下,同时出现在两者中的最长行列表。一旦有了 LCS,diff 就很直白:在 A 中而不在 LCS 中的行就是删除,在 B 中而不在 LCS 中的行就是新增,在 LCS 中的行就是未变。
经典 LCS 用一张 N × M 的动态规划表来跑。单元格 (i, j) 存的是 A 的前 i 行与 B 的前 j 行的 LCS 长度。从左到右、从上到下填完表,再从右下角往回走一遍,就能还原出编辑脚本。时间和空间复杂度都是 O(N×M),对两个一千行的文件没问题,对十万行日志就慢了。
2.2 Myers(1986)
Eugene Myers 1986 年的论文《An O(ND) Difference Algorithm and Its Variations》把问题重新表述为一张编辑图上的最短路径:节点是两份输入中的位置 (i, j),横向移动代表删除,纵向移动代表插入,对角线移动代表匹配。最短路径就是最小编辑脚本。
Myers 的时间复杂度是 O((N+M)D),其中 D 是编辑脚本的大小。当两段文本相近时,也就是 diff 的常态,D 很小,算法基本是线性的。它是 git diff、GNU diff 和 GitHub PR 渲染器的默认算法。对 99% 的 Web 输入来说,这就是正确答案。
2.3 Patience diff(Bram Cohen,2005)
Patience diff 走了另一条路:找出在每份输入中都只出现一次的行(称为「唯一锚点行」),把它们对齐,再在锚点之间的空隙里递归处理。数学上没有优势,最坏情况依然差,但在代码上读起来效果好得多。
为什么?Myers 最小化的是编辑距离,从数学上看最优,但当最优对齐穿过无关的大括号或空行时,视觉上就糟糕了。Patience 拒绝在常见的样板内容上对齐(每个文件都有 } 行,每个文件都有空行),所以函数边界保持完整。Bram Cohen 当年是为 Bazaar 发明的;Git 里通过 git diff --patience 提供。与它密切相关的 Histogram 算法(git diff --histogram)速度略快,输出质量相近。
设想同一个文件的两个版本,其中一个函数被挪了位置。Myers 可能会把函数 A 的右大括号和函数 B 的右大括号对齐,然后把两个函数体报告为完全不同。Patience 会锚定在唯一的函数名上,把它报告为一次移动。同样的输入,审查体验完全不同。
2.4 算法对比
| 属性 | Myers(默认) | Patience | Histogram |
|---|---|---|---|
| 时间复杂度 | O((N+M)D) | 常见情况 ~O(N log N) | 与 Patience 相近 |
| 编辑距离最优 | 是,脚本最短 | 否,可能更长 | 否,可能更长 |
| 代码上可读性 | 偶尔会错位对齐大括号和空行 | 较好,以唯一行为锚点 | 较好 |
| 使用方 | git 默认、GNU diff、GitHub UI | git diff --patience、Bazaar | git diff --histogram |
| 适用场景 | 大多数输入的速度与正确性 | 代码审查、重构 diff | 同 Patience,速度略快 |
2.5 本工具采用的方案
文本对比工具使用经典动态规划 LCS,配合两项优化:公共前后缀裁剪,以及行内词级 diff 的第二轮 token 级 LCS。两个两千行的配置文件、只改了一行的 diff,裁剪后会塌缩成 1×1 的 DP 表,渲染耗时不到一毫秒。对典型 Web 输入而言,Myers 还是 DP 的选择并不可见,两者都比浏览器绘制结果还快。
3. 行内词级 diff:为什么改一个字符整行都高亮
你在一行里只改了一个标识符,整行却被染成红绿一片。Bug?不,这是设计。
diff 先在行级别跑 LCS:「第 14 行被替换了。」然后对每一对被替换的行再跑一轮 token 级 LCS。token 由 Unicode 词边界切分得来,连续的字母与数字保持在一起,空白与标点各自成 token。第二轮 LCS 给出该行内最小的 token 级编辑脚本。
渲染器会用高亮色把整行涂满,方便视线定位,但只在真正变化的 token 上施加明亮背景色。变化 token 周围未变的部分则用同一色系的弱化版本,存在感弱但不显眼。视线会精准落到那处编辑上。
示例 1:标识符重命名。 function getUser(id) 改成 function getUser(userId)。整行被标为修改。行内,只有 id(红色删除线)和 userId(明亮绿色)带有强高亮,其余部分保持弱化。
示例 2:日志延迟变化。 POST /api/orders 201 88ms 变成 POST /api/orders 201 4200ms。行被标为修改。行内只有 88 和 4200 是明亮的,路径、method、状态码都保持弱化,这正是事故时间线读者需要的视觉效果。
当一行里变化的 token 太多时,词级高亮反而成了噪声。这时工具会回退到「删除 + 新增」配对呈现:原始行整行删除,新行整行新增,不再做行内着色。阈值大致是「超过一半的 token 不同」。
总结:行级 diff 告诉你哪一行变了,词级 diff 告诉你那一行上具体哪些字符承载了变化。在文本对比工具里点 Sample,可以在同一份输入上看到这两种视图。
4. side-by-side 与 unified diff:两种视图,一份 diff
4.1 并排(side-by-side)视图
两栏:左边是原始版本,右边是修改后版本。匹配的行水平对齐。新增的行只出现在右栏,背景为绿色;删除的行只出现在左栏,背景为红色;修改对则并排放置,中间一道黄色 gutter 标识,配合行内词级高亮。
side-by-side 适用于「人来看 diff」的场景:PR 审查、教学、演示、和非技术干系人一起过一份合同变更。这是给眼睛看的视图。
缺点:它不可传输。你没法把一张 side-by-side 渲染粘贴到 Slack 里让别人应用它,也没法把它管道喂给 patch。要分享、要应用,就得用 unified。
4.2 unified diff 格式
unified diff 是 GNU diff 留下来的一种五十年历史的纯文本格式,已被 POSIX 标准化。一个完整示例:
--- original
+++ modified
@@ -1,3 +1,4 @@
1. The service is provided as-is.
2. Either party may terminate with 30 days notice.
+2a. Termination notice must be in writing.
3. Disputes are resolved in California courts.
前两行是源文件名。@@ -L,C +L,C @@ 是 hunk 头:-L,C 表示从原始版本第 L 行开始,涉及 C 行;+L,C 对修改版同理。hunk 内部,以空格开头的行是上下文(未变),- 是删除,+ 是新增。
每个变更上下各保留三行上下文,是 GNU 的默认值。大多数工具允许用 -U n 调整:diff -U0 表示无上下文,diff -U10 表示十行上下文。hunk 头会跟随你的选择更新。
在文本对比工具里点 Unified 标签切换视图,或者点 Copy unified diff 把 patch 复制到剪贴板。
4.3 unified diff 的可移植性
unified diff 的覆盖面很广,是文本变更最常用的交换格式。
| 目的地 | 接受 unified diff? | 用法 |
|---|---|---|
GNU patch | 是 | patch -p1 < diff.patch |
git apply | 是 | git apply diff.patch |
| GitHub PR 评论 | 是(放在 ```diff 代码块里) | 自动着色渲染 |
| GitLab MR 评论 | 是 | 同样的围栏代码块 |
| Bitbucket / Azure DevOps PR | 是 | 同样的围栏代码块 |
| Slack / Discord 粘贴 | 部分 | 在代码块中以纯文本渲染,无颜色 |
| VS Code「Open Patch」 | 是 | 通过 Source Control 应用 patch |
| Jira / Linear issue 正文 | 部分 | 代码块中可看,没有应用按钮 |
同样九行 ---/+++/@@ 文本,可以喂给 patch、喂给 git apply、在三个 PR 平台中正常渲染、还能在 Slack 粘贴中存活。其他 diff 格式很少能覆盖到这种程度。
4.4 何时选哪个
审查用 side-by-side,分享与应用用 unified。如果你自己在读 diff,两栏视图更快。如果下游有人或有工具需要消费它(审查者、工具、patch 命令),就复制 unified 格式。
5. 忽略选项:空白、大小写、空行、行尾符
绝大多数「全都变了」的抱怨其实是噪声。四个开关能解决 95%。
- 忽略大小写 把
A映射成a。等价于git diff -i。适用于环境变量对比、SQL 关键字风格审查,以及任何「大写 vs 小写但语义相同」的场景。 - 忽略所有空白 在比对前把每个空格、tab、换行都折叠掉。等价于
git diff -w。专治 tab ↔ 空格的重新格式化、缩进改写以及「我们切到 Prettier 了」这种把行数搞炸的 diff。这类改动开了忽略空白后,通常会从 87 处修改降到 4 处。 - 忽略行尾空格和 tab 仅剥掉行末的空白。等价于
git diff -b。专治 Windows 与 Unix 间拷贝后留下的 CRLF 噪声,行末的\r被过滤掉,真正的内容就能对齐。 - 忽略空行 比对前丢弃空行。专治散文 diff 中「我加了一处段落分隔,结果第 12 段看起来完全不一样了」。
一个报告「87 处修改」的 200 行配置文件,开启「忽略所有空白」后通常会降到「4 处修改」。一份从 Windows 拷到 Unix、每一行都标红的文件,开启「忽略行尾空格」后会降到零。每个开关相互独立,并在会话间持久化。
CRLF 与 LF。 Windows 的行尾符是 \r\n,Unix 是 \n,经典 Mac 是 \r。在不做归一化的 Unix 编辑器里打开 Windows 文件,行末的 \r 就会留下来。每一行都会被报告为「内容相同但行末多一个 \r」。开启「忽略行尾空格」可以让这种噪声消失,又不会掩盖真正的变化。
注意。 忽略选项是双刃剑。打开「忽略大小写」,把 LOG.error 重构成 log.Error 看起来就一样了。打开「忽略所有空白」,Python 的缩进 bug 就会隐身。按当下要回答的问题挑开关,问完就关掉。
6. 文本 diff、JSON diff、git diff:决策矩阵
文本 diff 是不带结构理解的行级与词级匹配。对散文来说这正是你要的,对 JSON 来说这正是你不要的。
6.1 决策矩阵
| 输入类型 | 文本 diff | JSON diff | git diff |
|---|---|---|---|
| 散文 / Markdown / 合同 | 最佳 | 用错工具 | 部分可用(仅限被 git 跟踪的文件) |
| 单文件代码片段(粘贴) | 最佳 | 用错工具 | 部分可用(需要 repo) |
| repo 中的代码(多文件) | 部分可用 | 用错工具 | 最佳 |
| API JSON 响应 | 用错工具(key 顺序产生假阳性) | 最佳 | 用错工具 |
| YAML / TOML 配置 | 部分可用(key 顺序产生假阳性) | 最佳(先转换) | 部分可用 |
| CSV 逐行比对 | 部分可用 | 用错工具 | 用错工具 |
| 日志 / heredoc | 最佳 | 用错工具 | 用错工具 |
| 二进制文件 | 用错工具 | 用错工具 | git diff --binary |
6.2 文本 diff 派不上用场的时候
三个经典误区。
JSON 里 key 顺序变了。 {"a":1,"b":2} 和 {"b":2,"a":1} 是同一份 JSON 文档。文本 diff 会把每一行都报告为变化,因为它们的确是不同的行。改用 JSON Diff,它理解 JSON 的 key 是无序的。
被重新格式化的 YAML 配置。 改一个值,把文件过一遍格式化器,缩进、key 顺序、引号风格全都会变。文本 diff 会报告整个文件被重写。先把两份文件转成 JSON,再用 JSON Diff 对比。
带重命名的多文件重构。 Git 会跟踪重命名,文本 diff 不会。如果你把两棵目录树各自拼成一大块来对比,跨文件的移动都会显示为「删除 + 新增」。这种场景请用 git diff(或 git diff --find-renames=80%)。
6.3 文本 diff 完全胜任的时候
散文。任何地方粘过来的代码片段。合同红线。日志切片。需要按自然语言句子匹配的翻译审校。.env 文件(顺序重要,因为 shell 从上往下读)。任何「行本身就承载语义」的输入。
想深入研究怎么把 JSON diff 里的噪声(timestamps、request IDs、自动生成的 UUID)过滤掉,请读如何在 JSON Diff 中忽略 Timestamps 和 IDs。
7. 六个真实场景(附可复制粘贴输入)
7.1 代码审查片段:函数重命名
你在审一个 PR。作者把 id 改成了 userId,并加了一个保护性判断。粘贴两个版本:
// Original
function getUser(id) {
const u = db.users.find(x => x.id === id);
return u;
}
// Modified
function getUser(userId) {
if (!userId) return null;
const u = db.users.find(x => x.id === userId);
return u;
}
diff 显示三行被修改、一行新增。行内词级高亮标出每一个 id → userId token;新加的保护语句以绿色背景出现。忽略选项保持关闭。在文本对比工具里试一下,把 unified 输出复制下来当评审评论。
7.2 合同或政策红线:插入一条款
五十段的合同,插了一条条款。把昨天的版本贴在左边,今天的贴在右边:
1. The service is provided as-is.
2. Either party may terminate with 30 days notice.
3. Disputes are resolved in California courts.
1. The service is provided as-is.
2. Either party may terminate with 30 days notice.
2a. Termination notice must be in writing.
3. Disputes are resolved in California courts.
diff 渲染出 49 行未变 + 1 行新增(+2a. Termination notice must be in writing.)。导出 unified diff,作为法务审查的留痕。
7.3 日志时间线排查
你怀疑出现了延迟回归。抓事故前与事故中各一段访问日志:
GET /api/users 200 14ms
POST /api/orders 201 88ms
GET /api/orders/42 200 21ms
GET /api/users 200 14ms
POST /api/orders 201 4200ms
GET /api/orders/42 500 21ms
行内高亮揪出 88 → 4200(50 倍延迟跳变)和 200 → 500(订单详情接口开始报错)。如果你的日志是 JSON 格式,需要更深入的日志处理(抽字段、按接口分组、算分位数),可以把 diff 与 jq 速查手册搭配使用。
7.4 翻译审校:保留占位符
你换了一家翻译机构,想确认新文案在结构上和旧版一致。把旧译文贴在左、新译文贴在右。打开忽略行尾空格 / tab,因为译者经常在字符串末尾留个零散的空格。
diff 会确认每一个 {username}、{count}、%s 占位符都在原位,只有自然语言部分发生变化。漏掉的占位符会在行内 diff 中以删除 token 显现,上线前就能抓出来。如果你需要比对占位符格式本身,Regex 正则表达式速查覆盖了 \{\w+\} 之类的写法。在文本对比工具里试试。
7.5 配置或 .env 审计:生产 vs 预发
对比两份 .env 文件。打开忽略空行,避免按段落分组的写法让段落间错位。diff 会告诉你哪些 key 值不同、哪些 key 只在一边存在、哪里的注释已经不同步。五分钟时间能省掉一次「预发能跑、生产挂了」的排障会。
7.6 散文或草稿修订
编辑把稿子返给你。把原稿贴在左、改过的版本贴在右。行内词级 diff 会明确告诉你哪些句子被重写了、哪些原封不动、哪些段落是新插入的。逐处接受或拒绝改动,不需要 Track Changes 功能,不需要 Word 文档,也不需要任何专有格式。
8. 常见坑及其症状解读
算法行为能解释绝大多数用户痛点。五种常见抱怨,以及它们背后的真实含义。
坑 1:「从 Windows 拷到 Unix 后每一行都标红了。」 症状:内容看起来一模一样,但 diff 里每一行都显示为变化。原因:CRLF 行尾符留下的 \r。解决:打开「忽略行尾空格 / tab」开关,diff 立刻塌缩到真正的变化上。
坑 2:「我粘贴了 JSON,100% 的行都不一样。」 症状:两份应当等价的 JSON 对象显示为完全不同。原因:key 顺序被打乱了。文本 diff 把行顺序当作有意义的信息,JSON 不这么认为。解决:任何 JSON 输入都改用 JSON Diff。
坑 3:「tab ↔ 空格的格式化把 diff 炸了。」 症状:87 处修改,全都是缩进。原因:你的格式化器改掉了每一行的前导空白。解决:「忽略所有空白」会把噪声压平,露出真正的语义变化。
坑 4:「diff 说一致,可是 cmp 不同意。」 症状:diff 报告没差异,但字节级比对说文件确实不同。原因:上一会话留下的某个忽略选项还开着,掩盖了真正的变化。解决:打开「忽略选项」面板,把每一个开关都关掉,再 diff 一次。
坑 5:「一处短小修改显示成了删除 + 新增。」 症状:一个小变化呈现为单独一行删除加单独一行新增,而不是行内高亮。原因:变化 token 的比例超过了行内阈值,渲染器回退到了配对呈现。这是设计,不是 bug。切到 Unified 视图就能看到 patch 工具期望的那种经典 -/+ 配对。
9. 隐私、性能,以及何时该用命令行
文本对比工具的每一次比对都在浏览器内的 JavaScript 中运行。无上传、无临时文件、无服务端日志、不会对你粘贴的文本做任何分析统计。对专有代码、内部合同、私有日志,任何你不会愿意贴进第三方服务器的内容,都是安全的。
实际上限:每边大约 5,000 行或 1 MB。合计超过 200 KB 时实时 diff 会自动关闭,切换到手动 Diff 按钮,避免打字阻塞页面。超过 5,000 行后输入会被截断并提示。这些限制存在,是因为 diff 运行在主线程上(没有走 web worker),而 worker 之间的传递和序列化在小输入上的代价已经超过 diff 本身。
当输入规模超出浏览器能力范围时,请落到命令行:
# Unified diff between two files
diff -u a.txt b.txt
# Same, but using git's diff engine (Patience, Histogram, color)
git diff --no-index a.txt b.txt
git diff --no-index --patience a.txt b.txt
# Streaming diff viewer for huge files (Rust, side-by-side, syntax-aware)
delta a.txt b.txt
当你要处理几 MB 的日志、二进制文件、多文件 repo diff,或者想要 delta 那种带语法高亮的着色,又或者需要把 diff 输出通过管道送给其他工具时,请切到命令行。
10. Unicode、CJK 与 RTL:国际化文本对比说明
tokenizer 按 Unicode 词边界拆分,分三类:词元(\p{L} 字母与 \p{N} 数字)、非词标点、空白。每一类各自产生 token,所以 hello, world! 会被拆成 hello、,、 、world、! 五个 token。
对于 CJK 内容(中文、日文、韩文),每个汉字或假名各自成 token。改动中文句子中的一个字,只有那个字会带行内高亮,整行其余部分保持弱化。段落级结构仍然按行划分,所以一处加了换行的句子重写会被识别为行级编辑,而非 token 级编辑。
对于 RTL 语言(阿拉伯文、希伯来文),diff 使用逻辑方向的 CSS 属性(用 ms-、me- 而不是 ml-、mr-)。在 RTL 区域下,gutter 与行列会自然翻转;每个 diff 单元格内,文本方向跟随内容走,所以阿拉伯字符串从右向左渲染,而 + 和 - 标记始终对齐到 start gutter。
行尾归一化识别三种:\r\n(Windows)、\n(Unix)、单独的 \r(老版 Mac OS 9 及更早)。三种都会被切成独立的行,所以跨平台转换过的文件不会塌缩成一行超长的内容。
11. FAQ
在线文本对比是怎么工作的?
文本对比先把两份输入按行切分,跑一个最长公共子序列算法(通常是 Myers 的 O((N+M)D))找出最小的插入与删除集合,然后把新增(绿)、删除(红)、未变(灰)的行标出来。第二轮 token 级 LCS 会标出每个被修改行内具体哪些词发生了变化。文本对比工具在浏览器本地完成整个比对。
文本 diff 和 JSON diff 有什么区别?
文本 diff 按行比对,适合散文、代码、日志和合同。JSON Diff 懂 JSON 的数据模型:key 顺序无关紧要,类型严格(1 ≠ "1"),数组可以按 key 匹配。把 JSON 粘进文本 diff 里,key 顺序或空白都会冒出来变成变化,而 JSON Diff 会忽略它们。非结构化内容用文本 diff,API 响应和配置用 JSON Diff。
我只改了一个词,为什么 diff 把整行都标成变化了?
它没有。整行被高亮是因为这一行上有东西变了,但高亮内部只有变化的 token 带着明亮背景(新增是绿色,删除是红色带删除线)。这就是行内词级 diff:保持行的上下文可读,同时让视线精准落在变化处。当一行变得太多、词级高亮反而不实用时,diff 会回退到独立的「删除 + 新增」配对,让结构保持清爽。
怎么在 diff 中忽略空白、大小写或空行?
打开「忽略选项」面板。「忽略大小写」让 A 和 a 等价。「忽略所有空白」把每个空格、tab、换行都折叠掉,等价于 git diff -w。「忽略行尾空格和 tab」对应 git diff -b,让 CRLF 噪声消失。「忽略空行」会丢弃空行,避免段落重排打乱 diff 对齐。每个选项相互独立,并在会话间保留。
什么是 unified diff 格式?
unified diff 是 GNU diff 在 1980 年代后期引入的 ---/+++/@@ -L,C +L,C @@ 文本格式,被 git、GitHub、GitLab 以及 Unix patch 命令所采用。每个 hunk 在变化上下各展示三行上下文,- 是删除,+ 是新增。把 unified 输出粘进 PR 评论,喂给 git apply,或者运行 patch -p1 < diff.patch,都能干净地应用。
Myers 与 Patience:代码审查用哪个算法更好?
Myers 是 git diff 和 GNU diff 的默认选项,快速且数学上最小,但偶尔会把不相关的空行或右大括号对齐起来,让 diff「读起来怪怪的」。Patience(Bram Cohen,2005)以每份输入中只出现一次的行为锚点,并在锚点之间递归处理,函数边界因此保持完整。审查重构时使用 git diff --patience(或速度略快、输出相近的 --histogram)。
我粘贴的文本会被发送到任何服务器吗?
不会。文本对比工具的每一次比对都在浏览器内的 JavaScript 中本地运行。你的文本不会被上传、记录、写入磁盘,也不会发给任何第三方。只有你的 UI 偏好(视图模式与忽略选项开关)会写入 localStorage,让页面下次访问时记住设置,文本本身从不保存。可在 DevTools → Network 中验证:点击 Diff 时零网络请求。
两边输入最大能放多少?
实际上限大约是每边 5,000 行或 1 MB。合计超过 200 KB 后实时 diff 自动关闭,切换为手动 Diff 按钮。超过 5,000 行时输入会被截断并给出警告。处理几 MB 的文件,请切到 diff -u a.txt b.txt、git diff --no-index a.txt b.txt 或 delta,它们采用流式处理,能应付到 GB 级别。