5月27日 14:01

Vim 怎么比较两个文件的差异?vimdiff 怎么用?

两个文件摆在一起,差异一目了然

日常开发中,比较两个文件的差异是高频操作。虽然 diff 命令能输出结果,但纯文本的比对输出读起来很费劲。Vim 自带的 diff 模式把两个文件并排展示,差异行高亮、相同行折叠,一眼就能看清哪里不一样。更关键的是,你可以在比对的同时直接编辑——这比任何只读的 diff 工具都高效。

启动 diff 模式的几种方式

最直接的启动方式是在终端输入:

bash
vimdiff file1 file2

它和 vim -d file1 file2 完全等价。默认是左右竖屏分割,如果你想上下横屏显示,加个 -o 参数:

bash
vimdiff -o file1 file2

也支持同时比较三个甚至四个文件,不过实际使用中两个文件的场景占绝大多数。

如果你已经在 Vim 里编辑了一个文件,临时想和另一个文件比对,不需要退出重来。在 Vim 的命令模式下输入:

vim
:vertical diffsplit another_file

这会在右侧打开一个新窗口,自动进入 diff 模式。如果是上下分割,用 :diffsplit 不加 vertical 即可。

还有一种情况:你已经在用 Vim 编辑了,但当前窗口不是 diff 模式,可以手动开启:

vim
:diffthis

在两个窗口分别执行一次,diff 高亮和折叠就会生效。

看懂 diff 界面的颜色编码

进入 diff 模式后,Vim 用不同的背景色来标记差异:

  • 蓝色/青色背景:只在这个文件中存在的行
  • 绿色背景:另一侧文件有这行,但当前文件对应位置是空的
  • 紫色/洋红背景:两侧都有这行,但内容不同
  • 红色背景:差异行中具体不同的文字(在紫色行的内部进一步高亮)

同时,两侧连续相同的行会被折叠成一行显示,默认展开差异处上下各 6 行上下文。如果你想调整上下文行数:

vim
:set diffopt=context:3

展开折叠用 zo,重新折叠用 zc

在差异之间跳转

进入 diff 模式后最常用的操作就是在差异点之间快速跳转:

  • ]c — 跳到下一个差异
  • [c — 跳到上一个差异

这两个快捷键省去了手动翻找的麻烦。配合窗口切换 Ctrl-w w(在左右窗口间轮换),可以快速检视所有差异。

用 dp 和 do 合并差异

这是 Vim diff 最实用的部分。dpdo 两个快捷键让你不用复制粘贴就能把差异从一侧搬到另一侧:

  • dp(diff put):把当前光标处的差异推送到另一侧文件
  • do(diff obtain):从另一侧文件拉取差异到当前文件

举个例子:左侧文件的某行写了 version: "2.0",右侧对应行是 version: "1.0"。你把光标放在左侧那行,按 dp,右侧就会变成 version: "2.0"。反过来,如果光标在右侧,按 do,右侧同样会变成 version: "2.0"

如果需要更精确的控制,可以用命令形式:

vim
:diffput " 和 dp 一样 :diffget " 和 do 一样

命令形式还支持指定范围,比如只获取第 10 到 20 行的差异:

vim
:10,20diffget

操作失误了?按 u 撤销就行,但要注意光标必须在被修改的那个窗口里。

滚动同步与 :diffupdate

默认情况下,两侧窗口的滚动是同步的(scrollbind 选项),你滚动一侧另一侧跟着动,方便逐行对照。如果你需要独立滚动某一边来查看上下文,可以临时关闭:

vim
:set noscrollbind

看完再打开:

vim
:set scrollbind

手动编辑文件后,diff 高亮可能不会立刻更新。这时候执行:

vim
:diffupdate

Vim 会重新扫描两个文件,刷新差异标记。养成改完内容就 :diffupdate 的习惯,可以避免看着过时的高亮做出错误判断。

与 Git 集成:把 Vim 设为 difftool

Git 默认用命令行 diff 输出比对结果,但你可以配置成自动调用 vimdiff:

bash
git config --global diff.tool vimdiff git config --global difftool.prompt false

之后用 git difftool 代替 git diff,每次比较都会在 Vim 的 diff 模式中打开。difftool.prompt false 省去了每次确认的步骤。

处理合并冲突时更有用。配置 mergetool:

bash
git config --global merge.tool vimdiff

执行 git mergetool 后,Vim 会打开一个四窗口布局:左侧是本地版本(LOCAL),中间是公共祖先(BASE),右侧是远程版本(REMOTE),底部是合并结果(MERGED)。在 MERGED 窗口中,你可以用 :diffget 选择接受哪一方的改动:

vim
:diffget LOCAL " 采用本地版本 :diffget REMOTE " 采用远程版本 :diffget BASE " 采用祖先版本

更简短的写法是 :diffg //2(LOCAL)和 :diffg //3(REMOTE),这是 Git 内部的缓冲区编号。处理完所有冲突后 :wqa 保存退出,再 git commit 就完成了合并。

如果用的是 Neovim,配置方式相同,只是把工具名换成 nvimdiff

bash
git config --global diff.tool nvimdiff git config --global merge.tool nvimdiff

几个实用技巧

比较目录时,可以先用 vimdiff dir1/file dir2/file 打开单个文件的比对。如果需要批量处理,git difftool 天然支持逐文件比对,每处理完一个文件 :qa 就会自动打开下一个。

临时想关闭 diff 高亮但保留分屏,执行 :diffoff。想重新开启,对两个窗口分别 :diffthis

退出时用 :qa 退出所有窗口,:wqa 保存并退出所有窗口,:qa! 强制不保存退出。这些批量操作比逐个窗口 :q 方便得多。


Vim 的 diff 功能没有花哨的界面,但 dp/do 一键合并差异、]c/[c 快速跳转、与 Git 的无缝衔接,这些组合起来形成了非常高效的差异处理流程。熟练之后,你会发现很多场景下它比图形化的 diff 工具还顺手——毕竟,手指不用离开键盘,才是 Vim 的核心优势。

标签:Vim