match: resolve filesets in subrepos for commands given the '-S' argument
authorMatt Harbison <matt_harbison@yahoo.com>
Sat, 16 May 2015 00:36:35 -0400
changeset 25122 755d23a49170
parent 25121 df63d4843581
child 25123 64f403b70a01
match: resolve filesets in subrepos for commands given the '-S' argument This will work for any command that creates its matcher via scmutil.match(), but only the files command is tested here (both workingctx and basectx based tests). The previous behavior was to completely ignore the files in the subrepo, even though -S was given. My first attempt was to teach context.walk() to optionally recurse, but once that was in place and the complete file list was built up, the predicate test would fail with 'path in nested repo' when a file in a subrepo was accessed through the parent context. There are two slightly surprising behaviors with this functionality. First, any path provided inside the fileset isn't narrowed when it is passed to the subrepo. I dont see any clean way to do that in the matcher. Fortunately, the 'subrepo()' fileset is the only one to take a path. The second surprise is that status predicates are resolved against the subrepo, not the parent like 'hg status -S' is. I don't see any way to fix that either, given the path auditor error mentioned above.
mercurial/context.py
mercurial/match.py
mercurial/scmutil.py
tests/test-subrepo-deep-nested-change.t
--- a/mercurial/context.py	Fri May 15 23:13:05 2015 -0400
+++ b/mercurial/context.py	Sat May 16 00:36:35 2015 -0400
@@ -251,11 +251,13 @@
     def sub(self, path):
         return subrepo.subrepo(self, path)
 
-    def match(self, pats=[], include=None, exclude=None, default='glob'):
+    def match(self, pats=[], include=None, exclude=None, default='glob',
+              listsubrepos=False):
         r = self._repo
         return matchmod.match(r.root, r.getcwd(), pats,
                               include, exclude, default,
-                              auditor=r.auditor, ctx=self)
+                              auditor=r.auditor, ctx=self,
+                              listsubrepos=listsubrepos)
 
     def diff(self, ctx2=None, match=None, **opts):
         """Returns a diff generator for the given contexts and matcher"""
@@ -1437,17 +1439,20 @@
             finally:
                 wlock.release()
 
-    def match(self, pats=[], include=None, exclude=None, default='glob'):
+    def match(self, pats=[], include=None, exclude=None, default='glob',
+              listsubrepos=False):
         r = self._repo
 
         # Only a case insensitive filesystem needs magic to translate user input
         # to actual case in the filesystem.
         if not util.checkcase(r.root):
             return matchmod.icasefsmatcher(r.root, r.getcwd(), pats, include,
-                                           exclude, default, r.auditor, self)
+                                           exclude, default, r.auditor, self,
+                                           listsubrepos=listsubrepos)
         return matchmod.match(r.root, r.getcwd(), pats,
                               include, exclude, default,
-                              auditor=r.auditor, ctx=self)
+                              auditor=r.auditor, ctx=self,
+                              listsubrepos=listsubrepos)
 
     def _filtersuspectsymlink(self, files):
         if not files or self._repo.dirstate._checklink:
--- a/mercurial/match.py	Fri May 15 23:13:05 2015 -0400
+++ b/mercurial/match.py	Sat May 16 00:36:35 2015 -0400
@@ -21,7 +21,7 @@
     except AttributeError:
         return m.match
 
-def _expandsets(kindpats, ctx):
+def _expandsets(kindpats, ctx, listsubrepos):
     '''Returns the kindpats list with the 'set' patterns expanded.'''
     fset = set()
     other = []
@@ -32,6 +32,12 @@
                 raise util.Abort("fileset expression with no context")
             s = ctx.getfileset(pat)
             fset.update(s)
+
+            if listsubrepos:
+                for subpath in ctx.substate:
+                    s = ctx.sub(subpath).getfileset(pat)
+                    fset.update(subpath + '/' + f for f in s)
+
             continue
         other.append((kind, pat))
     return fset, other
@@ -47,7 +53,8 @@
 
 class match(object):
     def __init__(self, root, cwd, patterns, include=[], exclude=[],
-                 default='glob', exact=False, auditor=None, ctx=None):
+                 default='glob', exact=False, auditor=None, ctx=None,
+                 listsubrepos=False):
         """build an object to match a set of file patterns
 
         arguments:
@@ -80,11 +87,13 @@
         matchfns = []
         if include:
             kindpats = self._normalize(include, 'glob', root, cwd, auditor)
-            self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)')
+            self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)',
+                                              listsubrepos)
             matchfns.append(im)
         if exclude:
             kindpats = self._normalize(exclude, 'glob', root, cwd, auditor)
-            self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)')
+            self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)',
+                                              listsubrepos)
             matchfns.append(lambda f: not em(f))
         if exact:
             if isinstance(patterns, list):
@@ -97,7 +106,8 @@
             if not _kindpatsalwaysmatch(kindpats):
                 self._files = _roots(kindpats)
                 self._anypats = self._anypats or _anypats(kindpats)
-                self.patternspat, pm = _buildmatch(ctx, kindpats, '$')
+                self.patternspat, pm = _buildmatch(ctx, kindpats, '$',
+                                                   listsubrepos)
                 matchfns.append(pm)
 
         if not matchfns:
@@ -287,12 +297,12 @@
     """
 
     def __init__(self, root, cwd, patterns, include, exclude, default, auditor,
-                 ctx):
+                 ctx, listsubrepos=False):
         init = super(icasefsmatcher, self).__init__
         self._dsnormalize = ctx.repo().dirstate.normalize
 
         init(root, cwd, patterns, include, exclude, default, auditor=auditor,
-             ctx=ctx)
+             ctx=ctx, listsubrepos=listsubrepos)
 
         # m.exact(file) must be based off of the actual user input, otherwise
         # inexact case matches are treated as exact, and not noted without -v.
@@ -420,10 +430,10 @@
         return '.*' + pat
     return _globre(pat) + globsuffix
 
-def _buildmatch(ctx, kindpats, globsuffix):
+def _buildmatch(ctx, kindpats, globsuffix, listsubrepos):
     '''Return regexp string and a matcher function for kindpats.
     globsuffix is appended to the regexp of globs.'''
-    fset, kindpats = _expandsets(kindpats, ctx)
+    fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
     if not kindpats:
         return "", fset.__contains__
 
--- a/mercurial/scmutil.py	Fri May 15 23:13:05 2015 -0400
+++ b/mercurial/scmutil.py	Sat May 16 00:36:35 2015 -0400
@@ -803,7 +803,7 @@
         pats = expandpats(pats or [])
 
     m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
-                         default)
+                         default, listsubrepos=opts.get('subrepos'))
     def badfn(f, msg):
         ctx.repo().ui.warn("%s: %s\n" % (m.rel(f), msg))
     m.bad = badfn
--- a/tests/test-subrepo-deep-nested-change.t	Fri May 15 23:13:05 2015 -0400
+++ b/tests/test-subrepo-deep-nested-change.t	Sat May 16 00:36:35 2015 -0400
@@ -209,6 +209,29 @@
   sub1/sub2/folder/bar (glob)
   sub1/sub2/x.txt (glob)
 
+  $ hg files -S "set:eol('dos') or eol('unix') or size('<= 0')"
+  .hgsub
+  .hgsubstate
+  foo/bar/abc (glob)
+  main
+  sub1/.hgsub (glob)
+  sub1/.hgsubstate (glob)
+  sub1/foo (glob)
+  sub1/sub1 (glob)
+  sub1/sub2/folder/bar (glob)
+  sub1/sub2/x.txt (glob)
+
+  $ hg files -r '.^' -S "set:eol('dos') or eol('unix')"
+  .hgsub
+  .hgsubstate
+  main
+  sub1/.hgsub (glob)
+  sub1/.hgsubstate (glob)
+  sub1/sub1 (glob)
+  sub1/sub2/folder/test.txt (glob)
+  sub1/sub2/sub2 (glob)
+  sub1/sub2/test.txt (glob)
+
   $ hg rollback -q
   $ hg up -Cq