fileset: restrict getfileset() to not return a computed set (API)
authorYuya Nishihara <yuya@tcha.org>
Sat, 09 Jun 2018 20:53:12 +0900
changeset 46905 760cc5dc01e8078ae617f6eeb927f9d90d07ebd1
parent 46904 0ba4cf3f088fe67e1c2049e869778d6826a0c523
child 46906 5cbcbe51d38d8f518133b63153b4cb0c78cb0b28
push id827
push usergszorc@mozilla.com
push dateTue, 10 Jul 2018 05:37:34 +0000
fileset: restrict getfileset() to not return a computed set (API) And rename the functions accordingly. fileset.match() will be changed to not compute the initial subset. test-glog*.t get back to the state before 9f9ffe5f687c "match: compose 'set:' pattern as matcher."
mercurial/context.py
mercurial/debugcommands.py
mercurial/fileset.py
mercurial/match.py
mercurial/subrepo.py
tests/test-glog-beautifygraph.t
tests/test-glog.t
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -176,18 +176,18 @@ class basectx(object):
         return self._manifestctx
     def repo(self):
         return self._repo
     def phasestr(self):
         return phases.phasenames[self.phase()]
     def mutable(self):
         return self.phase() > phases.public
 
-    def getfileset(self, expr):
-        return fileset.getfileset(self, expr)
+    def matchfileset(self, expr, badfn=None):
+        return fileset.match(self, expr, badfn=badfn)
 
     def obsolete(self):
         """True if the changeset is obsolete"""
         return self.rev() in obsmod.getrevs(self._repo, 'obsolete')
 
     def extinct(self):
         """True if the changeset is extinct"""
         return self.rev() in obsmod.getrevs(self._repo, 'extinct')
--- a/mercurial/debugcommands.py
+++ b/mercurial/debugcommands.py
@@ -898,17 +898,17 @@ def debugfileset(ui, repo, expr, **opts)
         files.update(repo.dirstate.walk(scmutil.matchall(repo),
                                         subrepos=list(wctx.substate),
                                         unknown=True, ignored=True))
         files.update(wctx.substate)
     else:
         files.update(ctx.files())
         files.update(ctx.substate)
 
-    m = scmutil.matchfiles(repo, ctx.getfileset(expr))
+    m = ctx.matchfileset(expr)
     for f in sorted(files):
         if not m(f):
             continue
         ui.write("%s\n" % f)
 
 @command('debugformat',
          [] + cmdutil.formatteropts,
         _(''))
--- a/mercurial/fileset.py
+++ b/mercurial/fileset.py
@@ -615,19 +615,24 @@ def _buildsubset(ctx, status):
     if status:
         subset = []
         for c in status:
             subset.extend(c)
         return subset
     else:
         return list(ctx.walk(ctx.match([])))
 
-def getfileset(ctx, expr):
+def match(ctx, expr, badfn=None):
+    """Create a matcher for a single fileset expression"""
+    repo = ctx.repo()
     tree = parse(expr)
-    return getset(fullmatchctx(ctx, _buildstatus(ctx, tree)), tree)
+    fset = getset(fullmatchctx(ctx, _buildstatus(ctx, tree)), tree)
+    return matchmod.predicatematcher(repo.root, repo.getcwd(),
+                                     fset.__contains__,
+                                     predrepr='fileset', badfn=badfn)
 
 def _buildstatus(ctx, tree, basectx=None):
     # do we need status info?
 
     # temporaty boolean to simplify the next conditional
     purewdir = ctx.rev() is None and basectx is None
 
     if (_intree(_statuscallers, tree) or
--- a/mercurial/match.py
+++ b/mercurial/match.py
@@ -35,37 +35,37 @@ def _rematcher(regex):
     matcher function'''
     m = util.re.compile(regex)
     try:
         # slightly faster, provided by facebook's re2 bindings
         return m.test_match
     except AttributeError:
         return m.match
 
-def _expandsets(kindpats, ctx, listsubrepos):
-    '''Returns the kindpats list with the 'set' patterns expanded.'''
-    fset = set()
+def _expandsets(root, cwd, kindpats, ctx, listsubrepos, badfn):
+    '''Returns the kindpats list with the 'set' patterns expanded to matchers'''
+    matchers = []
     other = []
 
     for kind, pat, source in kindpats:
         if kind == 'set':
             if not ctx:
                 raise error.ProgrammingError("fileset expression with no "
                                              "context")
-            s = ctx.getfileset(pat)
-            fset.update(s)
+            matchers.append(ctx.matchfileset(pat, badfn=badfn))
 
             if listsubrepos:
                 for subpath in ctx.substate:
-                    s = ctx.sub(subpath).getfileset(pat)
-                    fset.update(subpath + '/' + f for f in s)
+                    sm = ctx.sub(subpath).matchfileset(pat, badfn=badfn)
+                    pm = prefixdirmatcher(root, cwd, subpath, sm, badfn=badfn)
+                    matchers.append(pm)
 
             continue
         other.append((kind, pat, source))
-    return fset, other
+    return matchers, other
 
 def _expandsubinclude(kindpats, root):
     '''Returns the list of subinclude matcher args and the kindpats without the
     subincludes in it.'''
     relmatchers = []
     other = []
 
     for kind, pat, source in kindpats:
@@ -92,26 +92,25 @@ def _kindpatsalwaysmatch(kindpats):
     """
     for kind, pat, source in kindpats:
         if pat != '' or kind not in ['relpath', 'glob']:
             return False
     return True
 
 def _buildkindpatsmatcher(matchercls, root, cwd, kindpats, ctx=None,
                           listsubrepos=False, badfn=None):
-    fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
     matchers = []
+    fms, kindpats = _expandsets(root, cwd, kindpats, ctx=ctx,
+                                listsubrepos=listsubrepos, badfn=badfn)
     if kindpats:
         m = matchercls(root, cwd, kindpats, listsubrepos=listsubrepos,
                        badfn=badfn)
         matchers.append(m)
-    if fset:
-        m = predicatematcher(root, cwd, fset.__contains__,
-                             predrepr='fileset', badfn=badfn)
-        matchers.append(m)
+    if fms:
+        matchers.extend(fms)
     if not matchers:
         return nevermatcher(root, cwd, badfn=badfn)
     if len(matchers) == 1:
         return matchers[0]
     return unionmatcher(matchers)
 
 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob',
           exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None,
--- a/mercurial/subrepo.py
+++ b/mercurial/subrepo.py
@@ -313,19 +313,19 @@ class abstractsubrepo(object):
     def filedata(self, name, decode):
         """return file data, optionally passed through repo decoders"""
         raise NotImplementedError
 
     def fileflags(self, name):
         """return file flags"""
         return ''
 
-    def getfileset(self, expr):
+    def matchfileset(self, expr, badfn=None):
         """Resolve the fileset expression for this repo"""
-        return set()
+        return matchmod.nevermatcher(self.wvfs.base, '', badfn=badfn)
 
     def printfiles(self, ui, m, fm, fmt, subrepos):
         """handle the files command for this subrepo"""
         return 1
 
     def archive(self, archiver, prefix, match=None, decode=True):
         if match is not None:
             files = [f for f in self.files() if match(f)]
@@ -787,34 +787,40 @@ class hgsubrepo(abstractsubrepo):
         if self._ctx.rev() is None:
             ctx = self._repo[None]
         else:
             rev = self._state[1]
             ctx = self._repo[rev]
         return cmdutil.files(ui, ctx, m, fm, fmt, subrepos)
 
     @annotatesubrepoerror
-    def getfileset(self, expr):
+    def matchfileset(self, expr, badfn=None):
+        repo = self._repo
         if self._ctx.rev() is None:
-            ctx = self._repo[None]
+            ctx = repo[None]
         else:
             rev = self._state[1]
-            ctx = self._repo[rev]
+            ctx = repo[rev]
 
-        files = ctx.getfileset(expr)
+        matchers = [ctx.matchfileset(expr, badfn=badfn)]
 
         for subpath in ctx.substate:
             sub = ctx.sub(subpath)
 
             try:
-                files.extend(subpath + '/' + f for f in sub.getfileset(expr))
+                sm = sub.matchfileset(expr, badfn=badfn)
+                pm = matchmod.prefixdirmatcher(repo.root, repo.getcwd(),
+                                               subpath, sm, badfn=badfn)
+                matchers.append(pm)
             except error.LookupError:
                 self.ui.status(_("skipping missing subrepository: %s\n")
                                % self.wvfs.reljoin(reporelpath(self), subpath))
-        return files
+        if len(matchers) == 1:
+            return matchers[0]
+        return matchmod.unionmatcher(matchers)
 
     def walk(self, match):
         ctx = self._repo[None]
         return ctx.walk(match)
 
     @annotatesubrepoerror
     def forget(self, match, prefix, dryrun, interactive):
         return cmdutil.forget(self.ui, self._repo, match,
--- a/tests/test-glog-beautifygraph.t
+++ b/tests/test-glog-beautifygraph.t
@@ -2018,24 +2018,35 @@ Test --copies
   \xe2\x97\x8b  0 add a   copies: (esc)
   
 Test "set:..." and parent revision
 
   $ hg up -q 4
   $ testlog "set:copied()"
   []
   (func
-    (symbol 'filelog')
-    (string 'set:copied()'))
+    (symbol '_matchfiles')
+    (list
+      (string 'r:')
+      (string 'd:relpath')
+      (string 'p:set:copied()')))
   <filteredset
-    <spanset- 0:7>, set([])>
+    <spanset- 0:7>,
+    <matchfiles patterns=['set:copied()'], include=[] exclude=[], default='relpath', rev=2147483647>>
   $ testlog --include "set:copied()"
   []
-  []
-  <spanset- 0:7>
+  (func
+    (symbol '_matchfiles')
+    (list
+      (string 'r:')
+      (string 'd:relpath')
+      (string 'i:set:copied()')))
+  <filteredset
+    <spanset- 0:7>,
+    <matchfiles patterns=[], include=['set:copied()'] exclude=[], default='relpath', rev=2147483647>>
   $ testlog -r "sort(file('set:copied()'), -rev)"
   ["sort(file('set:copied()'), -rev)"]
   []
   <filteredset
     <fullreposet- 0:7>,
     <matchfiles patterns=['set:copied()'], include=[] exclude=[], default='glob', rev=None>>
 
 Test --removed
--- a/tests/test-glog.t
+++ b/tests/test-glog.t
@@ -1865,24 +1865,35 @@ Test --copies
   o  0 add a   copies:
   
 Test "set:..." and parent revision
 
   $ hg up -q 4
   $ testlog "set:copied()"
   []
   (func
-    (symbol 'filelog')
-    (string 'set:copied()'))
+    (symbol '_matchfiles')
+    (list
+      (string 'r:')
+      (string 'd:relpath')
+      (string 'p:set:copied()')))
   <filteredset
-    <spanset- 0:7>, set([])>
+    <spanset- 0:7>,
+    <matchfiles patterns=['set:copied()'], include=[] exclude=[], default='relpath', rev=2147483647>>
   $ testlog --include "set:copied()"
   []
-  []
-  <spanset- 0:7>
+  (func
+    (symbol '_matchfiles')
+    (list
+      (string 'r:')
+      (string 'd:relpath')
+      (string 'i:set:copied()')))
+  <filteredset
+    <spanset- 0:7>,
+    <matchfiles patterns=[], include=['set:copied()'] exclude=[], default='relpath', rev=2147483647>>
   $ testlog -r "sort(file('set:copied()'), -rev)"
   ["sort(file('set:copied()'), -rev)"]
   []
   <filteredset
     <fullreposet- 0:7>,
     <matchfiles patterns=['set:copied()'], include=[] exclude=[], default='glob', rev=None>>
 
 Test --removed