37

I know that Vim's diff mode (vimdiff) allows us to compare the contents of two (or more) files.

But it is possible to compare content of multiple files across directories in order to merge two directories recursively (like DiffMerge and similar tools)?

  • Is a VCS of any kind in use for your respective folders? That would open up a whole range of answers and plugins that may not be available for flat folders. – Caleb Feb 14 '15 at 9:25
27

There is DirDiff.vim plugin (GitHub) to diff and merge two directories recursively.

It performs a recursive diff on two directories and generate a diff "window". Based on that window you can perform various diff operations such as opening two files in Vim's diff mode, copy the file or directory recursively to the other, or remove the directory tree from the source directory.

Usage:

:DirDiff <dir1> <dir2>

For more information / help: :help dirdiff

See the screenshot:

wlee screenshot

See also:

  • the link to the dedm blog is broken - rather the blog has been removed. – drevicko Mar 29 '18 at 11:08
  • 1
    @drevicko Thanks, I've replaced link with the archived one – kenorb Mar 29 '18 at 20:50
4

I use a wrapper script in python to merge files (see below). This is a simplified version of what I use to merge my ~/.vim dirs and such.

It should work in Python 2 and 3; but probably not in very old versions of Python as shipped with CentOS and some other distros.

Be aware that some checks (like the one for binary files, or if the files are the same) are not very fast (it reads the entire file); you could remove them if you want.

It also doesn't report if a is only present in one of the directories...

#!/usr/bin/env python
from __future__ import print_function
import hashlib, os, subprocess, sys

if len(sys.argv) < 3:
    print('Usage: {} dir1 dir2'.format(sys.argv[0]))
    sys.exit(1)

dir1 = os.path.realpath(sys.argv[1])
dir2 = os.path.realpath(sys.argv[2])

for root, dirs, files in os.walk(dir1):
    for f in files:
        f1 = '{}/{}'.format(root, f)
        f2 = f1.replace(dir1, dir2, 1)

        # Don't diff files over 1MiB
        if os.stat(f1).st_size > 1048576 or os.stat(f2).st_size > 1048576: continue

        # Check if files are the same; in which case a diff is useless
        h1 = hashlib.sha256(open(f1, 'rb').read()).hexdigest()
        h2 = hashlib.sha256(open(f2, 'rb').read()).hexdigest()
        if h1 == h2: continue

        # Don't diff binary files
        if open(f1, 'rb').read().find(b'\000') >= 0: continue

        subprocess.call(['vimdiff', f1, f2])
3

I've wanted the same for a while. The best solution I found was to use vdwrap, which works amazingly well. All it does is wrap git difftool --dir-diff for vimdiff. It doesn't require any vim plugins.

All you need to do is tell git difftool to use vdwrap:

git config --global difftool.vdwrap.cmd '/full/path/vdwrap $LOCAL $REMOTE'
git config --global diff.tool vdwrap

The next time you use git difftool, it will open Vim with separate Vim tabs for each pairs of files.

A caveat is that it's a Zsh script. It should be pretty simple to convert it to a bash script but I haven't given that a go.

2

If you just want to just use vimdiff without installing anything extra, the following command will open all the differing files in succession letting you look at the changes with vimdiff:

    for files in $(diff -rq dir1 dir2|grep 'differ$'|sed "s/^Files //g;s/ differ$//g;s/ and /:/g"); do 
        vimdiff ${files%:*} ${files#*:}; 
    done
1

There's a lightweight solution for that:

  1. Set up vimdiffext plugin.
  2. Do diff dir1 dir2 | vim -R - at shell.

It will add folds and side-by-side comparison for changed files.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.