diff: recurse into subrepositories with --subrepos/-S flag
authorMartin Geisler <mg@lazybytes.net>
Fri, 03 Sep 2010 12:58:51 +0200
changeset 12167 d2c5b0927c28
parent 12166 441a74b8def1
child 12168 ff4597f521a4
diff: recurse into subrepositories with --subrepos/-S flag
hgext/keyword.py
mercurial/cmdutil.py
mercurial/commands.py
mercurial/patch.py
mercurial/subrepo.py
tests/test-debugcomplete.t
tests/test-help.t
tests/test-subrepo-recursion.t
--- a/hgext/keyword.py	Fri Sep 03 12:58:51 2010 +0200
+++ b/hgext/keyword.py	Fri Sep 03 12:58:51 2010 +0200
@@ -511,14 +511,14 @@
         self.lines = kwt.shrinklines(self.fname, self.lines)
 
     def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
-                opts=None):
+                opts=None, prefix=''):
         '''Monkeypatch patch.diff to avoid expansion except when
         comparing against working dir.'''
         if node2 is not None:
             kwt.match = util.never
         elif node1 is not None and node1 != repo['.'].node():
             kwt.restrict = True
-        return orig(repo, node1, node2, match, changes, opts)
+        return orig(repo, node1, node2, match, changes, opts, prefix)
 
     def kwweb_skip(orig, web, req, tmpl):
         '''Wraps webcommands.x turning off keyword expansion.'''
--- a/mercurial/cmdutil.py	Fri Sep 03 12:58:51 2010 +0200
+++ b/mercurial/cmdutil.py	Fri Sep 03 12:58:51 2010 +0200
@@ -5,7 +5,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from node import hex, nullid, nullrev, short
+from node import hex, bin, nullid, nullrev, short
 from i18n import _
 import os, sys, errno, re, glob, tempfile
 import util, templater, patch, error, encoding, templatekw
@@ -655,7 +655,8 @@
         single(rev, seqno + 1, fp)
 
 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
-                   changes=None, stat=False, fp=None):
+                   changes=None, stat=False, fp=None, prefix='',
+                   listsubrepos=False):
     '''show diff or diffstat.'''
     if fp is None:
         write = ui.write
@@ -668,16 +669,27 @@
         width = 80
         if not ui.plain():
             width = util.termwidth()
-        chunks = patch.diff(repo, node1, node2, match, changes, diffopts)
+        chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
+                            prefix=prefix)
         for chunk, label in patch.diffstatui(util.iterlines(chunks),
                                              width=width,
                                              git=diffopts.git):
             write(chunk, label=label)
     else:
         for chunk, label in patch.diffui(repo, node1, node2, match,
-                                         changes, diffopts):
+                                         changes, diffopts, prefix=prefix):
             write(chunk, label=label)
 
+    if listsubrepos:
+        ctx1 = repo[node1]
+        for subpath in ctx1.substate:
+            sub = ctx1.sub(subpath)
+            if node2 is not None:
+                node2 = bin(repo[node2].substate[subpath][1])
+            submatch = matchmod.narrowmatcher(subpath, match)
+            sub.diff(diffopts, node2, submatch, changes=changes,
+                     stat=stat, fp=fp, prefix=prefix)
+
 class changeset_printer(object):
     '''show changeset information when templating not requested.'''
 
--- a/mercurial/commands.py	Fri Sep 03 12:58:51 2010 +0200
+++ b/mercurial/commands.py	Fri Sep 03 12:58:51 2010 +0200
@@ -1471,7 +1471,8 @@
 
     diffopts = patch.diffopts(ui, opts)
     m = cmdutil.match(repo, pats, opts)
-    cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat)
+    cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
+                           listsubrepos=opts.get('subrepos'))
 
 def export(ui, repo, *changesets, **opts):
     """dump the header and diffs for one or more changesets
@@ -4183,7 +4184,7 @@
            _('revision'), _('REV')),
           ('c', 'change', '',
            _('change made by revision'), _('REV'))
-         ] + diffopts + diffopts2 + walkopts,
+         ] + diffopts + diffopts2 + walkopts + subrepoopts,
          _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
     "^export":
         (export,
--- a/mercurial/patch.py	Fri Sep 03 12:58:51 2010 +0200
+++ b/mercurial/patch.py	Fri Sep 03 12:58:51 2010 +0200
@@ -1404,7 +1404,7 @@
         context=get('unified', getter=ui.config))
 
 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
-         losedatafn=None):
+         losedatafn=None, prefix=''):
     '''yields diff of changes to files between two nodes, or node and
     working directory.
 
@@ -1418,6 +1418,9 @@
     called with the name of current file being diffed as 'fn'. If set
     to None, patches will always be upgraded to git format when
     necessary.
+
+    prefix is a filename prefix that is prepended to all filenames on
+    display (used for subrepos).
     '''
 
     if opts is None:
@@ -1462,7 +1465,7 @@
         copy = copies.copies(repo, ctx1, ctx2, repo[nullid])[0]
 
     difffn = lambda opts, losedata: trydiff(repo, revs, ctx1, ctx2,
-                 modified, added, removed, copy, getfilectx, opts, losedata)
+                 modified, added, removed, copy, getfilectx, opts, losedata, prefix)
     if opts.upgrade and not opts.git:
         try:
             def losedata(fn):
@@ -1518,7 +1521,10 @@
         header.append('new mode %s\n' % nmode)
 
 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
-            copy, getfilectx, opts, losedatafn):
+            copy, getfilectx, opts, losedatafn, prefix):
+
+    def join(f):
+        return os.path.join(prefix, f)
 
     date1 = util.datestr(ctx1.date())
     man1 = ctx1.manifest()
@@ -1557,8 +1563,8 @@
                             gone.add(a)
                         else:
                             op = 'copy'
-                        header.append('%s from %s\n' % (op, a))
-                        header.append('%s to %s\n' % (op, f))
+                        header.append('%s from %s\n' % (op, join(a)))
+                        header.append('%s to %s\n' % (op, join(f)))
                         to = getfilectx(a, ctx1).data()
                     else:
                         losedatafn(f)
@@ -1600,7 +1606,7 @@
                 elif binary or nflag != oflag:
                     losedatafn(f)
             if opts.git:
-                header.insert(0, mdiff.diffline(revs, a, b, opts))
+                header.insert(0, mdiff.diffline(revs, join(a), join(b), opts))
 
         if dodiff:
             if dodiff == 'binary':
@@ -1609,7 +1615,7 @@
                 text = mdiff.unidiff(to, date1,
                                     # ctx2 date may be dynamic
                                     tn, util.datestr(ctx2.date()),
-                                    a, b, revs, opts=opts)
+                                    join(a), join(b), revs, opts=opts)
             if header and (text or len(header) > 1):
                 yield ''.join(header)
             if text:
--- a/mercurial/subrepo.py	Fri Sep 03 12:58:51 2010 +0200
+++ b/mercurial/subrepo.py	Fri Sep 03 12:58:51 2010 +0200
@@ -7,7 +7,7 @@
 
 import errno, os, re, xml.dom.minidom, shutil, urlparse, posixpath
 from i18n import _
-import config, util, node, error
+import config, util, node, error, cmdutil
 hg = None
 
 nullstate = ('', '', 'empty')
@@ -249,6 +249,9 @@
     def status(self, rev2, **opts):
         return [], [], [], [], [], [], []
 
+    def diff(self, diffopts, node2, match, prefix, **opts):
+        pass
+
 class hgsubrepo(abstractsubrepo):
     def __init__(self, ctx, path, state):
         self._path = path
@@ -289,6 +292,17 @@
                                % (inst, relpath(self)))
             return [], [], [], [], [], [], []
 
+    def diff(self, diffopts, node2, match, prefix, **opts):
+        try:
+            node1 = node.bin(self._state[1])
+            cmdutil.diffordiffstat(self._repo.ui, self._repo, diffopts,
+                                   node1, node2, match,
+                                   prefix=os.path.join(prefix, self._path),
+                                   listsubrepos=True, **opts)
+        except error.RepoLookupError, inst:
+            self._repo.ui.warn(_("warning: %s in %s\n")
+                               % (inst, relpath(self)))
+
     def dirty(self):
         r = self._state[1]
         if r == '':
--- a/tests/test-debugcomplete.t	Fri Sep 03 12:58:51 2010 +0200
+++ b/tests/test-debugcomplete.t	Fri Sep 03 12:58:51 2010 +0200
@@ -180,7 +180,7 @@
   annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, include, exclude
   clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd
   commit: addremove, close-branch, include, exclude, message, logfile, date, user
-  diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude
+  diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude, subrepos
   export: output, switch-parent, rev, text, git, nodates
   forget: include, exclude
   init: ssh, remotecmd
--- a/tests/test-help.t	Fri Sep 03 12:58:51 2010 +0200
+++ b/tests/test-help.t	Fri Sep 03 12:58:51 2010 +0200
@@ -471,6 +471,7 @@
       --stat                 output diffstat-style summary of changes
    -I --include PATTERN [+]  include names matching the given patterns
    -X --exclude PATTERN [+]  exclude names matching the given patterns
+   -S --subrepos             recurse into subrepositories
   
   [+] marked option can be specified multiple times
   
--- a/tests/test-subrepo-recursion.t	Fri Sep 03 12:58:51 2010 +0200
+++ b/tests/test-subrepo-recursion.t	Fri Sep 03 12:58:51 2010 +0200
@@ -2,6 +2,7 @@
 
   $ echo '[defaults]' >> $HGRCPATH
   $ echo 'status = -S' >> $HGRCPATH
+  $ echo 'diff = --nodates -S' >> $HGRCPATH
 
 Create test repository:
 
@@ -54,6 +55,21 @@
   $ hg status
   M foo/bar/z.txt
   M foo/y.txt
+  $ hg diff
+  diff -r d254738c5f5e foo/y.txt
+  --- a/foo/y.txt
+  +++ b/foo/y.txt
+  @@ -1,2 +1,3 @@
+   y1
+   y2
+  +y3
+  diff -r 9647f22de499 foo/bar/z.txt
+  --- a/foo/bar/z.txt
+  +++ b/foo/bar/z.txt
+  @@ -1,2 +1,3 @@
+   z1
+   z2
+  +z3
 
 Status call crossing repository boundaries:
 
@@ -64,6 +80,21 @@
   $ hg status -I '**/?.txt'
   M foo/bar/z.txt
   M foo/y.txt
+  $ hg diff -I '**/?.txt'
+  diff -r d254738c5f5e foo/y.txt
+  --- a/foo/y.txt
+  +++ b/foo/y.txt
+  @@ -1,2 +1,3 @@
+   y1
+   y2
+  +y3
+  diff -r 9647f22de499 foo/bar/z.txt
+  --- a/foo/bar/z.txt
+  +++ b/foo/bar/z.txt
+  @@ -1,2 +1,3 @@
+   z1
+   z2
+  +z3
 
 Status from within a subdirectory:
 
@@ -74,6 +105,21 @@
   M foo/bar/z.txt
   M foo/y.txt
   ? dir/a.txt
+  $ hg diff
+  diff -r d254738c5f5e foo/y.txt
+  --- a/foo/y.txt
+  +++ b/foo/y.txt
+  @@ -1,2 +1,3 @@
+   y1
+   y2
+  +y3
+  diff -r 9647f22de499 foo/bar/z.txt
+  --- a/foo/bar/z.txt
+  +++ b/foo/bar/z.txt
+  @@ -1,2 +1,3 @@
+   z1
+   z2
+  +z3
 
 Status with relative path:
 
@@ -81,17 +127,66 @@
   M ../foo/bar/z.txt
   M ../foo/y.txt
   ? a.txt
+  $ hg diff ..
+  diff -r d254738c5f5e foo/y.txt
+  --- a/foo/y.txt
+  +++ b/foo/y.txt
+  @@ -1,2 +1,3 @@
+   y1
+   y2
+  +y3
+  diff -r 9647f22de499 foo/bar/z.txt
+  --- a/foo/bar/z.txt
+  +++ b/foo/bar/z.txt
+  @@ -1,2 +1,3 @@
+   z1
+   z2
+  +z3
   $ cd ..
 
+Cleanup and final commit:
+
+  $ rm -r dir
+  $ hg commit -m 2-3-2
+  committing subrepository foo
+  committing subrepository foo/bar
+
+Log with the relationships between repo and its subrepo:
+
+  $ hg log --template '{rev}:{node|short} {desc}\n'
+  2:1326fa26d0c0 2-3-2
+  1:4b3c9ff4f66b 1-2-1
+  0:23376cbba0d8 0-0-0
+
+  $ hg -R foo log --template '{rev}:{node|short} {desc}\n'
+  3:65903cebad86 2-3-2
+  2:d254738c5f5e 0-2-1
+  1:8629ce7dcc39 0-1-0
+  0:af048e97ade2 0-0-0
+
+  $ hg -R foo/bar log --template '{rev}:{node|short} {desc}\n'
+  2:31ecbdafd357 2-3-2
+  1:9647f22de499 0-1-1
+  0:4904098473f9 0-0-0
+
 Status between revisions:
 
-  $ rm -r dir
-  $ hg commit -m 2-2-1
-  committing subrepository foo
-  committing subrepository foo/bar
   $ hg status
   $ hg status --rev 0:1
   M .hgsubstate
   M foo/.hgsubstate
   M foo/bar/z.txt
   M foo/y.txt
+  $ hg diff -I '**/?.txt' --rev 0:1
+  diff -r af048e97ade2 -r d254738c5f5e foo/y.txt
+  --- a/foo/y.txt
+  +++ b/foo/y.txt
+  @@ -1,1 +1,2 @@
+   y1
+  +y2
+  diff -r 4904098473f9 -r 9647f22de499 foo/bar/z.txt
+  --- a/foo/bar/z.txt
+  +++ b/foo/bar/z.txt
+  @@ -1,1 +1,2 @@
+   z1
+  +z2