
早上接到一句话:「你帮我看看这两个版本哪里不一样。」发过来的是两个压缩包,解压后各 1.2 GB。这时候选错工具,代价不是十分钟,是一整天。
比对差异的工具看起来都差不多:两边一放,中间出红线绿线。但它们各自解决的问题根本不是同一类——有的比「同一份文件的两次修改」,有的比「两个不同的东西」。搞混这两件事,就会出现拿 git diff 去比两个文件夹的场面。
1. 先分清三种「不一样」
第一种是同一份文件的两个版本,中间有明确的先后关系,你知道哪边是新的。第二种是两个平行的副本,谁新谁旧说不清,你要找的是「差在哪」。第三种是两次变更来自两条线,你要把两边都想要的改动合到一起。前两种只需要读,第三种需要写,这是能力上的分水岭。
工具也是按这三层分的。只会读的工具做不了合并,做合并的工具在只读场景下往往太重。把工具按「读 / 写」分类,比按功能列表分类有用得多。
2. 四个工具的边界
下面四个覆盖了绝大多数日常。不排优劣,只标边界。每一个都在某个场景下是错的答案。
| 工具 | 前提 | 强项 | 明显短板 |
|---|---|---|---|
| git diff(配 delta 分页) | 文件在 Git 仓库里,有提交历史 | 按提交、按分支、按行追溯,输出可管道处理 | 仓库外的东西一概不管 |
| VS Code 内置 Diff | 两个文件路径或两段文本 | 零配置、有语法高亮、能就地改 | 不能比目录,不能三路合并 |
| Beyond Compare 5 | 桌面环境,需安装 | 目录树、二进制、Office 文档、FTP 两端 | 商业授权,启动慢,脚本化弱 |
| meld 3.22 | Linux 桌面,apt 一条命令 | 三路合并、目录对比、免费 | 大文件卡,Windows 上体验一般 |
git diff 的边界最容易被误判。它比的是「仓库里的两个状态」,不是「磁盘上的两个文件」。git diff --no-index a.txt b.txt 能绕出来做文件对比,但那一刻你已经不需要 Git 了。用 git diff 比仓库外的文件,是在为一个不存在的历史买单。
delta 是 git diff 的显示层,装完配两行 config 就生效:行号、语法高亮、并排视图。它不改变比对逻辑,只改变你读结果的成本。一天要看二十次 diff 的人,值得花十分钟配它。
3. VS Code 内置的那一个,被严重低估
在资源管理器里选中一个文件,按住 Ctrl 再选另一个,右键「比较所选项目」,就出来了。也可以在命令面板里直接粘两段文本比。它解决的是「临时比一下」,这个需求占了日常的七成。
它的短板很明确:没有目录对比。要比两个文件夹的结构差异,它给不了你任何东西。一旦需求从「两个文件」变成「两棵树」,这个工具立刻出局。
还有一个隐藏用法:Git 面板里点某个已修改文件,打开的 diff 是只读的双栏,右边可以直接改,改完保存即暂存区内容。它是少数能在看差异的同时完成修改的工具,这一点 git diff 永远做不到。
4. Beyond Compare 的位置:目录与二进制
它的核心不是文本比对,是「两棵目录树的比对」。左边一个文件夹,右边一个,它按规则列出只在左边、只在右边、两边都有但内容不同、两边相同四类。整站迁移、版本升级前后的配置对照,这类活只有它能一次性看完。
它还支持把 Office 文档当结构化内容比,而不是字节比。两份 docx 改了三处措辞,用文本工具打开是一堆压缩乱码,它能直接标出段落差异。这一条在真实办公场景里的价值,远高于它的文本高亮做得多漂亮。
代价是授权与重量。它不便宜,启动要几秒,而且很难塞进脚本。它适合「人要坐下来仔细看」的场合,不适合放进流水线。
5. meld 的位置:三路合并
三路合并的意思是:给你「共同祖先」「你的版本」「他的版本」三份,工具算出各自相对祖先改了什么,再把两组改动叠到一份结果上。两路对比只能告诉你「不一样」,三路才能告诉你「谁改的」。
git 遇到冲突时,把 merge.tool 设成 meld,冲突文件会用三栏打开:左中右,中间是结果,你逐块选「取左」或「取右」。这是唯一能让人真正看懂冲突的界面形态,命令行里那些尖括号标记做不到。
它的短板是大文件。几千行的配置还能撑,十万行的日志会卡到无法操作。meld 是给人看的工具,不是给机器跑的。
6. 三套组合,按场景配
组合一:仓库内日常改动 = git diff + delta + VS Code。终端里快速扫,需要改的时候转到编辑器。这套覆盖写代码的 90%。
组合二:两份文档/两个目录的对照 = Beyond Compare。不装第二个工具,一个够用。若在 Linux 且不想买授权,换 meld,功能覆盖八成。
组合三:多人协作的冲突处理 = git + meld(或 VS Code 的三路视图)。前提是把 merge.conflictStyle 设成 zdiff3,它会多显示一段共同祖先的内容,冲突判断会清楚一大截。
7. 三种「你选错了」的信号
- 你在给 diff 输出加 grep 找关键行。说明你要的不是差异,是过滤。先把两边都过一遍 jq 或 sort 归一化,再比,差异会从两千行掉到二十行。
- 你在用目录对比工具逐个打开文件看内容。说明你需要的是「哪些文件变了」,不是「变了什么」。用
diff -rq或rsync -n --itemize-changes一遍就够。 - 你把 Beyond Compare 当 Git 客户端在用。说明你在用图形界面补偿一个没配好的命令行环境。先把 git 的 aliases 和 delta 配起来,这类需求会消失。
这三种信号有一个共同点:工具没选错,是问题的层次没分清。先问「我要的是行、是文件,还是树」,答案会自己浮出来。
8. 比之前,先做三件归一化
最浪费时间的比对,是把两个本来一致的东西比出三千行差异。行尾符是最常见的元凶:Windows 的 CRLF 和 Linux 的 LF,在多数比对工具里就是「整文件都变了」。先把行尾统一,再谈差异。
第二件是格式化。同一份代码被两个编辑器按不同缩进保存过,逻辑没变,视觉上处处是差异。用 gofmt、prettier、black 这类工具把两边各跑一遍,再比。归一化之后剩下的差异,才是真正需要人判断的部分。
第三件是排序。JSON 的键顺序、CSV 的行顺序,在语义上往往无关,在字节上却全部不同。用 jq -S 或 sort 处理一遍,差异行数常常从四位数掉到两位数。顺序不是信息的时候,删掉顺序就是在删除噪音。
写在最后
比对工具的本质是把「变化」可视化。而人对变化的容忍度,取决于变化是不是自己造成的。自己改的十行,一眼就过;别人改的一行,要盯半分钟。
所以真正决定效率的不是工具的高亮配色,是你知不知道自己在找什么。带着问题去比,两千行差异里只有三行与你有关;没有问题时,两千行全是噪音。
你上一次为了看清两个版本差在哪,最后是用什么办法解决的?是工具,还是干脆重写了一遍?

评论 (0)