Vim is able to "diff" files (show the differences between similar files) using an external diff program (Vim assumes you have a working diff program that is not part of the Vim source). :help diff
On Unix-based systems, Vim should work without problem because there should be a "standard" diff program available. However, on other systems, some tricks may be required in order to have Vim find your diff program. Furthermore, the version of diff that you use may be different from that expected by Vim, for example, it may require different command line options. A common way of handling that situation is explained at :help diff-diffexpr where an example shows a
MyDiff() function that can pass any required command line to your diff program.
On Windows, many people use the "Windows Vim installers without Cream" setup package to install Vim. That package (see below for details) has a bug in the way that diff is installed. In addition, many other Windows users try to apply the
MyDiff() advice, and can encounter similar issues, even if using a Vim from some other source.
This tip explains how Windows users can overcome problems running diff from within Vim. In addition, some of the options available when invoking diff are explained.
Recommended solution[edit | edit source]
The best solution for Windows users is to download the GnuWin32 diff package and install the "Complete package, except sources Setup". You also need to adjust your PATH environment variable to include the installed bin directory. Alternatively, you can copy the *.exe and *.dll files from that bin directory to another directory that is already in your PATH. The current version of GnuWin32 diff is 2.8.7.
Then make sure that you do not configure Vim to use the '
diffexpr' option. For example, in your vimrc, you could delete lines that set
diffexpr, or that define a
MyDiff() function. Or, you could manually enter the following to clear the option:
diffexpr is not set, Vim will assume you have a standard diff program in your PATH, and all diffs will work correctly (if the first diff program found in the current directory or the directories listed in your PATH is in fact GnuWin32 diff).
If you have installed from the "Windows Vim installers without Cream" download, you probably have a
diff.exe (GnuWin32 diff 2.7) in the vim72 directory (assuming Vim 7.2). In that case you do not need to download any extra packages. Instead, you simply need to clear
diffexpr so that the broken
MyDiff() function is not used. After restarting Vim, use command
:echo $PATH to check your PATH. You should see that the directory holding
diff.exe has been added to the PATH. Therefore (assuming you have no other diff programs), Vim should correctly invoke the
diff.exe provided with the "Vim without Cream" install, and all diffs should work correctly.
If errors prevent diff from running, use the verbose option to see how diff is called. For example:
gvim -V -od file1 file2
Vim may write temporary files when performing a diff. Therefore, you need valid TEMP or TMP environment variables that point to an existing directory where you are able to write files. For more information, see the tip on handling temp environment errors.
Diff options[edit | edit source]
Some people vertically align variables and comments by adding or removing whitespace. However, those trivial changes may clutter the output from diff when you later compare the old and new files to check your changes. To ignore whitespace in the diff, enter the command:
You can issue this command while viewing a diff (you may also need
:diffupdate to update the display), or you can add the command to your vimrc so it is always in effect. :help 'diffopt'
Vim distribution from Cream[edit | edit source]
Where to download Vim mentions that Cream's "standard" Vim is a good place to download a fully patched installation of Vim and gvim for Windows (using the "Windows Vim installers without Cream" choice). However, versions upto 7.2.2 (and possibly later) have a bug in the way that diff is installed.
By default, the install creates directory
C:\Program Files\vim which contains only files
_vimrc, and folder
vim72 which contains the Vim files, including
gvim.exe, as well as GNU Win32
diff.exe (of course, the directory is
vim72 for version 7.2, or something different for other versions).
_vimrc file has contents:
set nocompatible source $VIMRUNTIME/vimrc_example.vim source $VIMRUNTIME/mswin.vim behave mswin set diffexpr=MyDiff() function MyDiff() let opt = '' if &diffopt =~ 'icase' | let opt = opt . '-i ' | endif if &diffopt =~ 'iwhite' | let opt = opt . '-b ' | endif silent execute '\"!C:\Program Files\vim\diff\" -a ' . opt . v:fname_in . ' ' . v:fname_new . ' > ' . v:fname_out endfunction
The bug appears when an attempt to use diff is made (in gvim for example, with the File menu, "Split Diff with"). The following error occurs:
Error detected while processing function MyDiff: line 4: E10: \ should be followed by /, ? or &
_vimrc to fix this error, a second error occurs:
E97: Cannot create diffs
As described above, the fix is simply to clear
diffexpr so the
MyDiff() function is not used. However, if you need to use the function, you can edit
_vimrc to change line 4 of
MyDiff by removing the backslash before the double quotes, and by moving the bang (
!) so it is before the quoted path, and by changing the path to the location where
diff.exe occurs. The final correct line 4 is:
silent execute '!"C:\Program Files\vim\vim72\diff" -a ' . opt . v:fname_in . ' ' . v:fname_new . ' > ' . v:fname_out
Or even better, to remain compatible when you upgrade Vim, change the line to:
silent execute '!"'.$VIMRUNTIME.'\diff" -a ' . opt . v:fname_in . ' ' . v:fname_new . ' > ' . v:fname_out
You may find that changing the line as follows works (the first diff found in the current directory or directories of the PATH will be used):
silent execute '!diff -a ' . opt . v:fname_in . ' ' . v:fname_new . ' > ' . v:fname_out
After this change (and restarting Vim), diff works correctly.
Other solutions[edit | edit source]
If you need to use the
MyDiff() function, another proposal that worked with Vim 6.4 is shown below (it should probably use $VIMRUNTIME as above).
if has('win32') set diffexpr=MyDiff() function! MyDiff() let opt = '-a --binary ' if &diffopt =~ 'icase' | let opt = opt . '-i ' | endif if &diffopt =~ 'iwhite' | let opt = opt . '-b ' | endif let arg1 = v:fname_in if arg1 =~ ' ' | let arg1 = '"' . arg1 . '"' | endif let arg2 = v:fname_new if arg2 =~ ' ' | let arg2 = '"' . arg2 . '"' | endif let arg3 = v:fname_out if arg3 =~ ' ' | let arg3 = '"' . arg3 . '"' | endif if &sh =~ '\<cmd' silent execute '!""' . $VIM. '\vim64\diff.exe" ' . opt . arg1 . ' ' . arg2 . ' > ' . arg3 . '"' else silent execute '!'.$VIM.'\vim64\diff.exe" ' . opt . arg1 . ' ' . arg2 . ' > ' . arg3 endif endfunction endif
Comments[edit | edit source]
- Need ideas for a better name for this tip.
- Should check the "Recommended solution" which is my understanding of the confusion behind all the misguided comments on the problem.
- Perhaps should give a little more prominence to the para on problems with TEMP.
- Should expand the section on diff options, and add a short section showing some usage examples (windo diffthis, diffoff! and a little more).
- Decide whether the "Other solutions" section is useful.
Under cygwin, I found that the backslashes in the path for the "execute" command caused trouble. You should be able to fix this by changing each backslash to a forward slash, or by changing each backslash with two backslashes.
I saw that I needed the fix after removing "silent" from the command so that I could see exactly what was executed.
There's no general solution to fix this problem out-of-the-box. It depends on the idiosyncrasies of the command processor (shell) that is used to "silent execute" the line. For instance, on my Windows XP I usually start gvim from a 4nt.exe command line, which in turn sets COMSPEC to be 4nt.exe instead of the default cmd.exe. So, in my case, the simplest way to fix MyDiff is to change it this way:
silent execute '!C:\Progra~1\vim\vim63\diff -a ' . opt . v:fname_in . ' ' . v:fname_new . ' > ' . v:fname_out
I had to add
set shell=bash.exe to my _vimrc to get it working. Of course
bash.exe must be in the PATH.