在 Linux 环境下直接使用 svn diff 查看代码修改?坦白讲,这种体验确实不够友好。满屏的 + 和 - 挤在一起,很容易让人眼花缭乱。于是,很多人转向了一个成熟的做法——将 vimdiff 设置为 svn diff 的对比工具。对于那些习惯使用 Vim 的开发者来说,这简直是提升代码审查效率的利器。
举个例子,当你执行下面这条命令时:
$ svn diff -r4420 ngx_http_limit_req_module.c
实际上,Subversion 会在后台向默认的 diff 程序发送类似下面的命令:
-u -L ngx_http_limit_req_module.c (revision 4420) -L ngx_http_limit_req_module.c (working copy) .svn/tmp/tempfile.tmp ngx_http_limit_req_module.c
官方 FAQ 对 svn diff 外部调用机制有过详细说明(中英文版本均有)。简单来讲,当你指定一个外部 diff 命令时,Subversion 会生成一个相当复杂的命令行参数。第一个参数是你的 --diff-cmd,接着是 --extensions(如果未指定或为空,默认会附加一个 -u)。第三、第四个参数是一对 -L 和第一个文件的标签;第五、第六个则是另一对 -L 和第二个文件的标签。最后的第七、第八个参数才是真正需要对比的两个文件路径——也就是临时文件和原文件。
理解了内部调用的参数结构,用 vimdiff 替代默认 diff 的思路就清晰了。vimdiff 比较两个文件只需知道它们的路径和文件名即可——也就是上面长命令中的第 7 和第 8 个参数。但是,如果直接使用 --diff-cmd vimdiff 调用是行不通的,因为多余的参数会让 vimdiff 无法处理。例如:
$ svn diff --diff-cmd vimdiff -r4420 ngx_http_limit_req_module.c
因此,需要自己编写一个包装脚本,让它先接收 diff 的全部参数,然后只把最后两个文件路径传递给 vimdiff。脚本内容非常简单:
#!/bin/sh # 去掉前5个参数 shift 5 # 使用vimdiff比较 vimdiff "$@"
另外,svn 的配置文件中可以指定默认的 diff 程序,这样之后每次执行 svn diff 都无需手动添加 --diff-cmd 参数。找到 ~/.subversion/config,定位到这一行:
# diff-cmd = diff_program (diff, gdiff, etc.)
把上面脚本的完整路径填入,例如:
diff-cmd = /usr/local/bin/diffwrap.sh
至此,配置完成。以后执行 svn diff,系统会自动弹出 vimdiff 界面,左右两个文件清晰对比。效果图如下:

