[PATCH] extdiff: single file diffs from the wc without making a copy

Brad Schick brad at schicks.net
Sun Aug 5 23:43:43 CDT 2007


# HG changeset patch
# User Brad Schick <schickb at gmail.com>
# Date 1186375074 25200
# Node ID 54e960401474f64445e35351be1590782bbd7c6e
# Parent  8d9bdcbb2b1852d2507cbb7e5d922f549f73f19d
extdiff: single file diffs from the wc without making a copy

Extdiff was always making a temporary directory and copying files even
when not required. This change makes extdiff avoid the copy when diffing
a single file that lives in the wc. This lets external diff tools edit
the working copy file directly. It also lets other extensions resuse the
functions in extdiff and get in-place diffs.

Also exposed nest functions, made extdiff less chatty in non-verbose
mode, and updated tests.

diff -r 8d9bdcbb2b18 -r 54e960401474 hgext/extdiff.py
--- a/hgext/extdiff.py    Thu Aug 02 12:29:10 2007 -0500
+++ b/hgext/extdiff.py    Sun Aug 05 21:37:54 2007 -0700
@@ -53,57 +53,57 @@ from mercurial import cmdutil, util
 from mercurial import cmdutil, util
 import os, shutil, tempfile
 
+
+def snapshot_node(ui, repo, files, node, tmproot):
+    '''snapshot files as of some revision'''
+    mf = repo.changectx(node).manifest()
+    dirname = os.path.basename(repo.root)
+    if dirname == "":
+        dirname = "root"
+    dirname = '%s.%s' % (dirname, short(node))
+    base = os.path.join(tmproot, dirname)
+    os.mkdir(base)
+    ui.note(_('making snapshot of %d files from rev %s\n') %
+                 (len(files), short(node)))
+    for fn in files:
+        if not fn in mf:
+            # skipping new file after a merge ?
+            continue
+        wfn = util.pconvert(fn)
+        ui.note('  %s\n' % wfn)
+        dest = os.path.join(base, wfn)
+        destdir = os.path.dirname(dest)
+        if not os.path.isdir(destdir):
+            os.makedirs(destdir)
+        data = repo.wwritedata(wfn, repo.file(wfn).read(mf[wfn]))
+        open(dest, 'wb').write(data)
+    return dirname
+
+
+def snapshot_wdir(ui, repo, files, tmproot):
+    '''snapshot files from working directory.
+    if not using snapshot, -I/-X does not work and recursive diff
+    in tools like kdiff3 and meld displays too many files.'''
+    dirname = os.path.basename(repo.root)
+    if dirname == "":
+        dirname = "root"
+    base = os.path.join(tmproot, dirname)
+    os.mkdir(base)
+    ui.note(_('making snapshot of %d files from working dir\n') %
+                 (len(files)))
+    for fn in files:
+        wfn = util.pconvert(fn)
+        ui.note('  %s\n' % wfn)
+        dest = os.path.join(base, wfn)
+        destdir = os.path.dirname(dest)
+        if not os.path.isdir(destdir):
+            os.makedirs(destdir)
+        fp = open(dest, 'wb')
+        for chunk in util.filechunkiter(repo.wopener(wfn)):
+            fp.write(chunk)
+    return dirname
+
 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
-    def snapshot_node(files, node):
-        '''snapshot files as of some revision'''
-        mf = repo.changectx(node).manifest()
-        dirname = os.path.basename(repo.root)
-        if dirname == "":
-            dirname = "root"
-        dirname = '%s.%s' % (dirname, short(node))
-        base = os.path.join(tmproot, dirname)
-        os.mkdir(base)
-        if not ui.quiet:
-            ui.write_err(_('making snapshot of %d files from rev %s\n') %
-                         (len(files), short(node)))
-        for fn in files:
-            if not fn in mf:
-                # skipping new file after a merge ?
-                continue
-            wfn = util.pconvert(fn)
-            ui.note('  %s\n' % wfn)
-            dest = os.path.join(base, wfn)
-            destdir = os.path.dirname(dest)
-            if not os.path.isdir(destdir):
-                os.makedirs(destdir)
-            data = repo.wwritedata(wfn, repo.file(wfn).read(mf[wfn]))
-            open(dest, 'wb').write(data)
-        return dirname
-
-    def snapshot_wdir(files):
-        '''snapshot files from working directory.
-        if not using snapshot, -I/-X does not work and recursive diff
-        in tools like kdiff3 and meld displays too many files.'''
-        dirname = os.path.basename(repo.root)
-        if dirname == "":
-            dirname = "root"
-        base = os.path.join(tmproot, dirname)
-        os.mkdir(base)
-        if not ui.quiet:
-            ui.write_err(_('making snapshot of %d files from working
dir\n') %
-                         (len(files)))
-        for fn in files:
-            wfn = util.pconvert(fn)
-            ui.note('  %s\n' % wfn)
-            dest = os.path.join(base, wfn)
-            destdir = os.path.dirname(dest)
-            if not os.path.isdir(destdir):
-                os.makedirs(destdir)
-            fp = open(dest, 'wb')
-            for chunk in util.filechunkiter(repo.wopener(wfn)):
-                fp.write(chunk)
-        return dirname
-
     node1, node2 = cmdutil.revpair(repo, opts['rev'])
     files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
     modified, added, removed, deleted, unknown = repo.status(
@@ -112,12 +112,34 @@ def dodiff(ui, repo, diffcmd, diffopts,
         return 0
 
     tmproot = tempfile.mkdtemp(prefix='extdiff.')
+    dir2root = ''
     try:
-        dir1 = snapshot_node(modified + removed, node1)
+        # Always make a copy of node1
+        dir1 = snapshot_node(ui, repo, modified + removed, node1, tmproot)
+        changes = len(modified) + len(removed) + len(added)
+
+        # If node2 in not the wc or there is >1 change, copy it
         if node2:
-            dir2 = snapshot_node(modified + added, node2)
+            dir2 = snapshot_node(ui, repo, modified + added, node2,
tmproot)
+        elif changes > 1:
+            dir2 = snapshot_wdir(ui, repo, modified + added, tmproot)
         else:
-            dir2 = snapshot_wdir(modified + added)
+            # This lets the diff tool open the changed file directly
+            dir2 = ''
+            dir2root = repo.root
+
+        # If only one change, diff the files instead of the directories
+        if changes == 1 :
+            if len(modified):
+                dir1 = os.path.join(dir1, util.localpath(modified[0]))
+                dir2 = os.path.join(dir2root, dir2,
util.localpath(modified[0])) 
+            elif len(removed) :
+                dir1 = os.path.join(dir1, util.localpath(removed[0]))
+                dir2 = os.devnull
+            else:
+                dir1 = os.devnull
+                dir2 = os.path.join(dir2root, dir2,
util.localpath(added[0])) 
+   
         cmdline = ('%s %s %s %s' %
                    (util.shellquote(diffcmd), ' '.join(diffopts),
                     util.shellquote(dir1), util.shellquote(dir2)))
diff -r 8d9bdcbb2b18 -r 54e960401474 tests/test-extdiff
--- a/tests/test-extdiff    Thu Aug 02 12:29:10 2007 -0500
+++ b/tests/test-extdiff    Sun Aug 05 21:37:54 2007 -0700
@@ -6,7 +6,9 @@ hg init a
 hg init a
 cd a
 echo a > a
+echo b > b
 hg add
+# should diff cloned directories
 hg extdiff -o -r $opt
 
 echo "[extdiff]" >> $HGRCPATH
@@ -22,13 +24,17 @@ echo b >> a
 echo b >> a
 hg ci -d '1 0' -mtest2
 
+# should diff cloned files directly
 hg falabala -r 0:1
 
 # test diff during merge
 hg update 0
-echo b >> b
-hg add b
+echo c >> c
+hg add c
 hg ci -m "new branch" -d '1 0'
 hg update -C 1
 hg merge tip
-hg falabala || echo "diff-like tools yield a non-zero exit code"
+# should diff cloned file against wc file
+hg falabala > out || echo "diff-like tools yield a non-zero exit code"
+# cleanup the output since the wc is a tmp directory
+sed  's:\(.* \).*\(\/test-extdiff\):\1[tmp]\2:' out
\ No newline at end of file
diff -r 8d9bdcbb2b18 -r 54e960401474 tests/test-extdiff.out
--- a/tests/test-extdiff.out    Thu Aug 02 12:29:10 2007 -0500
+++ b/tests/test-extdiff.out    Sun Aug 05 21:37:54 2007 -0700
@@ -1,9 +1,7 @@ adding a
 adding a
-making snapshot of 0 files from rev 000000000000
-making snapshot of 1 files from working dir
+adding b
 Only in a: a
-making snapshot of 0 files from rev 000000000000
-making snapshot of 1 files from working dir
+Only in a: b
 diffing a.000000000000 a
 hg falabala [OPTION]... [FILE]...
 
@@ -26,14 +24,10 @@ options:
  -X --exclude  exclude names matching the given patterns
 
 use "hg -v help falabala" to show global options
-making snapshot of 1 files from rev e27a2475d60a
-making snapshot of 1 files from rev 5e49ec8d3f05
-diffing a.e27a2475d60a a.5e49ec8d3f05
+diffing a.8a5febb7f867/a a.34eed99112ab/a
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
-making snapshot of 1 files from rev 5e49ec8d3f05
-making snapshot of 1 files from working dir
-diffing a.5e49ec8d3f05 a
 diff-like tools yield a non-zero exit code
+diffing a.34eed99112ab/c [tmp]/test-extdiff/a/c



More information about the Mercurial-devel mailing list