Vim Tips Wiki
Register
Advertisement
Tip 416 Printable Monobook Previous Next

created 2003 · complexity basic · version 6.0


This tip is deprecated for the following reasons:

At some point, the MyDiff function provided by the Vim without Cream installer has been modified. As of Vim 7.3.138 it is still broken, but this tip is no longer completely accurate in describing deficiencies of the MyDiff function

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[]

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:

:set diffexpr=

When 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 gvim.exe, vim.exe and 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[]

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:

set diffopt+=iwhite

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[]

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 README_lang.txt and _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).

The _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 &

After editing _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[]

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[]

 TO DO 

  • 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 use Windows 2000 with bash.exe and UnixUtils.

I had to add set shell=bash.exe to my _vimrc to get it working. Of course bash.exe must be in the PATH.


Advertisement