两个文件“内容明明一样”,diff 却报出一大堆差异,很多时候是空白造成的:编辑器删掉了行尾空格、把制表符换成了空格,或者改了缩进宽度。diff 工具提供了忽略这类差异的选项,但每个选项忽略的范围各不相同,需要准确了解后再用。
GNU diff 的空白相关选项
| 选项 | 长选项名 | 忽略的内容 |
|---|---|---|
-Z | --ignore-trailing-space | 行尾的空白 |
-E | --ignore-tab-expansion | 制表符展开为空格造成的差异 |
-b | --ignore-space-change | 空白数量的变化、行尾空白 |
-w | --ignore-all-space | 所有空白 |
-B | --ignore-blank-lines | 只新增或删除空行的变更 |
-i | --ignore-case | 大小写 |
--strip-trailing-cr | 输入行末尾的 CR 字符 |
git diff 的对应选项
git 文档的说明如下:
--ignore-cr-at-eol:比较时忽略行尾的回车符(CR)。--ignore-space-at-eol:忽略行尾空白的变化。-b、--ignore-space-change:忽略空白数量的变化。忽略行尾空白,并把其他一个或多个连续空白字符都视为相同。-w、--ignore-all-space:比较行时忽略空白。即使一行有空白而另一行没有,这种差异也会被忽略。--ignore-blank-lines:忽略所有行都是空行的变更。
-b 与 -w 的关键区别
这两个选项经常被混淆。关键在于是否区分空白“有/没有”。
| 左侧 | 右侧 | -b(忽略空白数量) | -w(忽略所有空白) |
|---|---|---|---|
a b | a b | 相同 | 相同 |
a b | ab | 不同 | 相同 |
x = 1 | x=1 | 不同 | 相同 |
if (a) + 行尾空白 | if (a) | 相同 | 相同 |
| 用 1 个制表符缩进 | 用 4 个空格缩进 | 相同 | 相同 |
-b 是把每段连续空白压缩成一个空格来看,-w 则是把空白全部删掉来看。所以 -w 会认为 x = 1 和 x=1 相同,甚至 int a 和 inta 也相同。
缩进有语法意义时的风险
在 Python、YAML、Makefile 这类缩进属于语法的文件中,忽略空白的选项可能会掩盖真正的修改。
if ready:
start()
notify()
if ready:
start()
notify()
在第二段中,notify() 被移出了 if 语句,行为已经改变;但用 -w 比较时空白被全部删除,差异就消失了。-b 则能发现它,因为行首空白从“有”变成了“没有”。不过,如果缩进从 4 个空格变成 8 个空格、代码被移进了更深一层的代码块,那只是空白的数量变了,-b 也看不出来。
稳妥的做法是:对缩进很重要的文件,关闭空白选项再比较。空白选项用来确认“是否只是整理了格式”,最终检查则养成不加选项的习惯,这样更安全。
忽略空行(-B)的范围
-B 和 git 的 --ignore-blank-lines 会隐藏仅由空行构成的变更。如果新增的空行与其他实际修改位于同一个 hunk 中,这些空行仍可能照常显示。这对调整了段落间空行数量的文档,或代码格式化工具调整了函数间空行的情况很有用。
本站选项对照表
| 本站 | 相近的 GNU diff | 相近的 git diff |
|---|---|---|
| 空白:按原样比较 | (无选项) | (无选项) |
| 空白:忽略行尾空白 | -Z | --ignore-space-at-eol |
| 空白:忽略空白数量 | -b | -b |
| 空白:忽略所有空白 | -w | -w |
| 忽略大小写 | -i | (无对应) |
| 忽略空行 | 类似 -B(空行不参与比较) | 类似 --ignore-blank-lines |
| 统一换行符 | --strip-trailing-cr + 忽略结尾换行符 | 类似 --ignore-cr-at-eol |
本站的“忽略空行”会把空行完全排除在比较之外,只用浅色显示。此外,忽略选项同样作用于行内高亮:开启“忽略空白数量”后,只有空格数量不同的部分不会被高亮。
在本工具中试一试
把上表中的例子放进 文本对比工具,逐个切换空白选项,就能立刻看到区别。行尾的 CR、文件末尾换行符等“看不见的差异”请参阅 换行符与看不见的差异,把结果当作 patch 阅读的方法请参阅 unified diff 阅读指南。