contrib/hgdiff
author Patrick Mezard <pmezard@gmail.com>
Wed, 03 Oct 2007 23:09:36 +0200
changeset 5390 36c46e20976a
parent 5081 ea7b982b6c08
child 8527 f9a80054dd3c
permissions -rwxr-xr-x
merge: provide *_ISLINK environment vars to merge helper Sets HG_MY_ISLINK, HG_OTHER_ISLINK, HG_BASE_ISLINK in environment. Without these variables, it's impossible for the merge application to know whether the 'other' and 'base' files were symlinks in their original contexts. For the purposes of the merge they are always emitted as small text files.

#!/usr/bin/env python

import os, sys, struct, stat
import difflib
import re
from optparse import OptionParser
from mercurial.bdiff import bdiff, blocks
from mercurial.mdiff import bunidiff, diffopts

VERSION="0.3"
usage = "usage: %prog [options] file1 file2"
parser = OptionParser(usage=usage)

parser.add_option("-d", "--difflib", action="store_true", default=False)
parser.add_option('-x', '--count', default=1)
parser.add_option('-c', '--context', type="int", default=3)
parser.add_option('-p', '--show-c-function', action="store_true", default=False)
parser.add_option('-w', '--ignore-all-space', action="store_true",
                  default=False)

(options, args) = parser.parse_args()

if not args:
    parser.print_help()
    sys.exit(1)

# simple utility function to put all the
# files from a directory tree into a dict
def buildlist(names, top):
    tlen = len(top)
    for root, dirs, files in os.walk(top):
        l = root[tlen + 1:]
        for x in files:
            p = os.path.join(root, x)
            st = os.lstat(p)
            if stat.S_ISREG(st.st_mode):
                names[os.path.join(l, x)] = (st.st_dev, st.st_ino)

def diff_files(file1, file2):
    if file1 == None:
        b = file(file2).read().splitlines(1)
        l1 = "--- %s\n" % (file2)
        l2 = "+++ %s\n" % (file2)
        l3 = "@@ -0,0 +1,%d @@\n" % len(b)
        l = [l1, l2, l3] + ["+" + e for e in b]
    elif file2 == None:
        a = file(file1).read().splitlines(1)
        l1 = "--- %s\n" % (file1)
        l2 = "+++ %s\n" % (file1)
        l3 = "@@ -1,%d +0,0 @@\n" % len(a)
        l = [l1, l2, l3] + ["-" + e for e in a]
    else:
        t1 = file(file1).read()
        t2 = file(file2).read()
        l1 = t1.splitlines(1)
        l2 = t2.splitlines(1)
        if options.difflib:
            l = difflib.unified_diff(l1, l2, file1, file2)
        else:
            l = bunidiff(t1, t2, l1, l2, file1, file2,
                         diffopts(context=options.context,
                                  showfunc=options.show_c_function,
                                  ignorews=options.ignore_all_space))
    for x in l:
        if x[-1] != '\n':
            x += "\n\ No newline at end of file\n"
        print x,

file1 = args[0]
file2 = args[1]

if os.path.isfile(file1) and os.path.isfile(file2):
    diff_files(file1, file2)
elif os.path.isdir(file1):
    if not os.path.isdir(file2):
        sys.stderr.write("file types don't match\n")
        sys.exit(1)

    d1 = {}
    d2 = {}

    buildlist(d1, file1)
    buildlist(d2, file2)
    keys = d1.keys()
    keys.sort()
    for x in keys:
        if x not in d2:
            f2 = None
        else:
            f2 = os.path.join(file2, x)
            st1 = d1[x]
            st2 = d2[x]
            del d2[x]
            if st1[0] == st2[0] and st1[1] == st2[1]:
                sys.stderr.write("%s is a hard link\n" % x)
                continue
        x = os.path.join(file1, x)
        diff_files(x, f2)
    keys = d2.keys()
    keys.sort()
    for x in keys:
        f1 = None
        x = os.path.join(file2, x)
        diff_files(f1, x)