changeset 14551:68d814a3cefd

fileset: basic pattern and boolean support debugfileset can now generate file lists for things like: "* and not hg*"
author Matt Mackall <mpm@selenic.com>
date Wed, 08 Jun 2011 13:44:41 -0500
parents 2425a3536396
children 3417954c42e9
files mercurial/commands.py mercurial/fileset.py
diffstat 2 files changed, 69 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/commands.py	Fri Jun 10 16:50:45 2011 +0200
+++ b/mercurial/commands.py	Wed Jun 08 13:44:41 2011 -0500
@@ -1603,6 +1603,10 @@
     if ui.verbose:
         tree = fileset.parse(expr)[0]
         ui.note(tree, "\n")
+    matcher = lambda x: scmutil.match(repo, x, default='glob')
+
+    for f in fileset.getfileset(repo[None], matcher, expr):
+        ui.write("%s\n" % f)
 
 @command('debugfsinfo', [], _('[PATH]'))
 def debugfsinfo(ui, path = "."):
--- a/mercurial/fileset.py	Fri Jun 10 16:50:45 2011 +0200
+++ b/mercurial/fileset.py	Wed Jun 08 13:44:41 2011 -0500
@@ -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.
 
-import parser, error
+import parser, error, match
 from i18n import _
 
 elements = {
@@ -27,6 +27,8 @@
 
 keywords = set(['and', 'or', 'not'])
 
+globchars = ".*{}[]?/\\"
+
 def tokenize(program):
     pos, l = 0, len(program)
     while pos < l:
@@ -56,13 +58,13 @@
                 pos += 1
             else:
                 raise error.ParseError(_("unterminated string"), s)
-        elif c.isalnum() or c in '.*{}[]?' or ord(c) > 127:
+        elif c.isalnum() or c in globchars or ord(c) > 127:
             # gather up a symbol/keyword
             s = pos
             pos += 1
             while pos < l: # find end of symbol
                 d = program[pos]
-                if not (d.isalnum() or d in ".*{}[]?," or ord(d) > 127):
+                if not (d.isalnum() or d in globchars or ord(d) > 127):
                     break
                 pos += 1
             sym = program[s:pos]
@@ -78,3 +80,63 @@
 
 parse = parser.parser(tokenize, elements).parse
 
+def getstring(x, err):
+    if x and (x[0] == 'string' or x[0] == 'symbol'):
+        return x[1]
+    raise error.ParseError(err)
+
+def getset(mctx, x):
+    if not x:
+        raise error.ParseError(_("missing argument"))
+    return methods[x[0]](mctx, *x[1:])
+
+def stringset(mctx, x):
+    m = mctx.matcher([x])
+    return [f for f in mctx.subset if m(f)]
+
+def andset(mctx, x, y):
+    return getset(mctx.narrow(getset(mctx, x)), y)
+
+def orset(mctx, x, y):
+    # needs optimizing
+    xl = getset(mctx, x)
+    yl = getset(mctx, y)
+    return xl + [f for f in yl if f not in xl]
+
+def notset(mctx, x):
+    s = set(getset(mctx, x))
+    return [r for r in mctx.subset if r not in s]
+
+def listset(mctx, a, b):
+    raise error.ParseError(_("can't use a list in this context"))
+
+methods = {
+    'string': stringset,
+    'symbol': stringset,
+    'and': andset,
+    'or': orset,
+    'list': listset,
+    'group': getset,
+    'not': notset
+}
+
+class matchctx(object):
+    def __init__(self, ctx, matchfn, subset=None):
+        self.ctx = ctx
+        self.matchfn = matchfn
+        self.subset = subset
+        if subset is None:
+            self.subset = ctx.walk(matchfn([])) # optimize this later
+    def matcher(self, pattern):
+        return self.matchfn(pattern)
+    def filter(self, files):
+        return [f for f in files if f in self.subset]
+    def narrow(self, files):
+        return matchctx(self.ctx, self.matchfn,
+                        self.filter(files))
+
+def getfileset(ctx, matchfn, expr):
+    tree, pos = parse(expr)
+    if (pos != len(expr)):
+        raise error.ParseError("invalid token", pos)
+    return getset(matchctx(ctx, matchfn), tree)