mercurial/fileset.py
changeset 38689 ff5b6fca1082
parent 38687 1500cbe22d53
child 38690 5d9749c598f0
equal deleted inserted replaced
38688:2570dca0f21c 38689:ff5b6fca1082
   138     l = getlist(x)
   138     l = getlist(x)
   139     if len(l) < min or len(l) > max:
   139     if len(l) < min or len(l) > max:
   140         raise error.ParseError(err)
   140         raise error.ParseError(err)
   141     return l
   141     return l
   142 
   142 
   143 def getset(mctx, x):
   143 def getmatch(mctx, x):
   144     if not x:
   144     if not x:
   145         raise error.ParseError(_("missing argument"))
   145         raise error.ParseError(_("missing argument"))
   146     return methods[x[0]](mctx, *x[1:])
   146     return methods[x[0]](mctx, *x[1:])
   147 
   147 
   148 def stringset(mctx, x):
   148 def stringmatch(mctx, x):
   149     m = mctx.matcher([x])
   149     return mctx.matcher([x])
   150     return [f for f in mctx.subset if m(f)]
   150 
   151 
   151 def kindpatmatch(mctx, x, y):
   152 def kindpatset(mctx, x, y):
   152     return stringmatch(mctx, _getkindpat(x, y, matchmod.allpatternkinds,
   153     return stringset(mctx, _getkindpat(x, y, matchmod.allpatternkinds,
   153                                          _("pattern must be a string")))
   154                                        _("pattern must be a string")))
   154 
   155 
   155 def andmatch(mctx, x, y):
   156 def andset(mctx, x, y):
   156     xm = getmatch(mctx, x)
   157     xl = set(getset(mctx, x))
   157     ym = getmatch(mctx, y)
   158     yl = getset(mctx, y)
   158     return matchmod.intersectmatchers(xm, ym)
   159     return [f for f in yl if f in xl]
   159 
   160 
   160 def ormatch(mctx, x, y):
   161 def orset(mctx, x, y):
   161     xm = getmatch(mctx, x)
   162     # needs optimizing
   162     ym = getmatch(mctx, y)
   163     xl = getset(mctx, x)
   163     return matchmod.unionmatcher([xm, ym])
   164     yl = getset(mctx, y)
   164 
   165     return xl + [f for f in yl if f not in xl]
   165 def notmatch(mctx, x):
   166 
   166     m = getmatch(mctx, x)
   167 def notset(mctx, x):
   167     return mctx.predicate(lambda f: not m(f), predrepr=('<not %r>', m))
   168     s = set(getset(mctx, x))
   168 
   169     return [r for r in mctx.subset if r not in s]
   169 def minusmatch(mctx, x, y):
   170 
   170     xm = getmatch(mctx, x)
   171 def minusset(mctx, x, y):
   171     ym = getmatch(mctx, y)
   172     xl = getset(mctx, x)
   172     return matchmod.differencematcher(xm, ym)
   173     yl = set(getset(mctx, y))
   173 
   174     return [f for f in xl if f not in yl]
   174 def negatematch(mctx, x):
   175 
       
   176 def negateset(mctx, x):
       
   177     raise error.ParseError(_("can't use negate operator in this context"))
   175     raise error.ParseError(_("can't use negate operator in this context"))
   178 
   176 
   179 def listset(mctx, a, b):
   177 def listmatch(mctx, x, y):
   180     raise error.ParseError(_("can't use a list in this context"),
   178     raise error.ParseError(_("can't use a list in this context"),
   181                            hint=_('see hg help "filesets.x or y"'))
   179                            hint=_('see hg help "filesets.x or y"'))
   182 
   180 
   183 def func(mctx, a, b):
   181 def func(mctx, a, b):
   184     funcname = getsymbol(a)
   182     funcname = getsymbol(a)
   215     """File that is modified according to :hg:`status`.
   213     """File that is modified according to :hg:`status`.
   216     """
   214     """
   217     # i18n: "modified" is a keyword
   215     # i18n: "modified" is a keyword
   218     getargs(x, 0, 0, _("modified takes no arguments"))
   216     getargs(x, 0, 0, _("modified takes no arguments"))
   219     s = set(mctx.status().modified)
   217     s = set(mctx.status().modified)
   220     return [f for f in mctx.subset if f in s]
   218     return mctx.predicate(s.__contains__, predrepr='modified')
   221 
   219 
   222 @predicate('added()', callstatus=True)
   220 @predicate('added()', callstatus=True)
   223 def added(mctx, x):
   221 def added(mctx, x):
   224     """File that is added according to :hg:`status`.
   222     """File that is added according to :hg:`status`.
   225     """
   223     """
   226     # i18n: "added" is a keyword
   224     # i18n: "added" is a keyword
   227     getargs(x, 0, 0, _("added takes no arguments"))
   225     getargs(x, 0, 0, _("added takes no arguments"))
   228     s = set(mctx.status().added)
   226     s = set(mctx.status().added)
   229     return [f for f in mctx.subset if f in s]
   227     return mctx.predicate(s.__contains__, predrepr='added')
   230 
   228 
   231 @predicate('removed()', callstatus=True)
   229 @predicate('removed()', callstatus=True)
   232 def removed(mctx, x):
   230 def removed(mctx, x):
   233     """File that is removed according to :hg:`status`.
   231     """File that is removed according to :hg:`status`.
   234     """
   232     """
   235     # i18n: "removed" is a keyword
   233     # i18n: "removed" is a keyword
   236     getargs(x, 0, 0, _("removed takes no arguments"))
   234     getargs(x, 0, 0, _("removed takes no arguments"))
   237     s = set(mctx.status().removed)
   235     s = set(mctx.status().removed)
   238     return [f for f in mctx.subset if f in s]
   236     return mctx.predicate(s.__contains__, predrepr='removed')
   239 
   237 
   240 @predicate('deleted()', callstatus=True)
   238 @predicate('deleted()', callstatus=True)
   241 def deleted(mctx, x):
   239 def deleted(mctx, x):
   242     """Alias for ``missing()``.
   240     """Alias for ``missing()``.
   243     """
   241     """
   244     # i18n: "deleted" is a keyword
   242     # i18n: "deleted" is a keyword
   245     getargs(x, 0, 0, _("deleted takes no arguments"))
   243     getargs(x, 0, 0, _("deleted takes no arguments"))
   246     s = set(mctx.status().deleted)
   244     s = set(mctx.status().deleted)
   247     return [f for f in mctx.subset if f in s]
   245     return mctx.predicate(s.__contains__, predrepr='deleted')
   248 
   246 
   249 @predicate('missing()', callstatus=True)
   247 @predicate('missing()', callstatus=True)
   250 def missing(mctx, x):
   248 def missing(mctx, x):
   251     """File that is missing according to :hg:`status`.
   249     """File that is missing according to :hg:`status`.
   252     """
   250     """
   253     # i18n: "missing" is a keyword
   251     # i18n: "missing" is a keyword
   254     getargs(x, 0, 0, _("missing takes no arguments"))
   252     getargs(x, 0, 0, _("missing takes no arguments"))
   255     s = set(mctx.status().deleted)
   253     s = set(mctx.status().deleted)
   256     return [f for f in mctx.subset if f in s]
   254     return mctx.predicate(s.__contains__, predrepr='deleted')
   257 
   255 
   258 @predicate('unknown()', callstatus=True)
   256 @predicate('unknown()', callstatus=True)
   259 def unknown(mctx, x):
   257 def unknown(mctx, x):
   260     """File that is unknown according to :hg:`status`. These files will only be
   258     """File that is unknown according to :hg:`status`."""
   261     considered if this predicate is used.
       
   262     """
       
   263     # i18n: "unknown" is a keyword
   259     # i18n: "unknown" is a keyword
   264     getargs(x, 0, 0, _("unknown takes no arguments"))
   260     getargs(x, 0, 0, _("unknown takes no arguments"))
   265     s = set(mctx.status().unknown)
   261     s = set(mctx.status().unknown)
   266     return [f for f in mctx.subset if f in s]
   262     return mctx.predicate(s.__contains__, predrepr='unknown')
   267 
   263 
   268 @predicate('ignored()', callstatus=True)
   264 @predicate('ignored()', callstatus=True)
   269 def ignored(mctx, x):
   265 def ignored(mctx, x):
   270     """File that is ignored according to :hg:`status`. These files will only be
   266     """File that is ignored according to :hg:`status`."""
   271     considered if this predicate is used.
       
   272     """
       
   273     # i18n: "ignored" is a keyword
   267     # i18n: "ignored" is a keyword
   274     getargs(x, 0, 0, _("ignored takes no arguments"))
   268     getargs(x, 0, 0, _("ignored takes no arguments"))
   275     s = set(mctx.status().ignored)
   269     s = set(mctx.status().ignored)
   276     return [f for f in mctx.subset if f in s]
   270     return mctx.predicate(s.__contains__, predrepr='ignored')
   277 
   271 
   278 @predicate('clean()', callstatus=True)
   272 @predicate('clean()', callstatus=True)
   279 def clean(mctx, x):
   273 def clean(mctx, x):
   280     """File that is clean according to :hg:`status`.
   274     """File that is clean according to :hg:`status`.
   281     """
   275     """
   282     # i18n: "clean" is a keyword
   276     # i18n: "clean" is a keyword
   283     getargs(x, 0, 0, _("clean takes no arguments"))
   277     getargs(x, 0, 0, _("clean takes no arguments"))
   284     s = set(mctx.status().clean)
   278     s = set(mctx.status().clean)
   285     return [f for f in mctx.subset if f in s]
   279     return mctx.predicate(s.__contains__, predrepr='clean')
   286 
   280 
   287 @predicate('tracked()')
   281 @predicate('tracked()')
   288 def tracked(mctx, x):
   282 def tracked(mctx, x):
   289     """File that is under Mercurial control."""
   283     """File that is under Mercurial control."""
   290     # i18n: "tracked" is a keyword
   284     # i18n: "tracked" is a keyword
   291     getargs(x, 0, 0, _("tracked takes no arguments"))
   285     getargs(x, 0, 0, _("tracked takes no arguments"))
   292     return [f for f in mctx.subset if f in mctx.ctx]
   286     return mctx.predicate(mctx.ctx.__contains__, predrepr='tracked')
   293 
   287 
   294 @predicate('binary()', callexisting=True)
   288 @predicate('binary()', callexisting=True)
   295 def binary(mctx, x):
   289 def binary(mctx, x):
   296     """File that appears to be binary (contains NUL bytes).
   290     """File that appears to be binary (contains NUL bytes).
   297     """
   291     """
   298     # i18n: "binary" is a keyword
   292     # i18n: "binary" is a keyword
   299     getargs(x, 0, 0, _("binary takes no arguments"))
   293     getargs(x, 0, 0, _("binary takes no arguments"))
   300     return [f for f in mctx.existing() if mctx.ctx[f].isbinary()]
   294     return mctx.fpredicate(lambda fctx: fctx.isbinary(),
       
   295                            predrepr='binary', cache=True)
   301 
   296 
   302 @predicate('exec()', callexisting=True)
   297 @predicate('exec()', callexisting=True)
   303 def exec_(mctx, x):
   298 def exec_(mctx, x):
   304     """File that is marked as executable.
   299     """File that is marked as executable.
   305     """
   300     """
   306     # i18n: "exec" is a keyword
   301     # i18n: "exec" is a keyword
   307     getargs(x, 0, 0, _("exec takes no arguments"))
   302     getargs(x, 0, 0, _("exec takes no arguments"))
   308     return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x']
   303     ctx = mctx.ctx
       
   304     return mctx.predicate(lambda f: ctx.flags(f) == 'x', predrepr='exec')
   309 
   305 
   310 @predicate('symlink()', callexisting=True)
   306 @predicate('symlink()', callexisting=True)
   311 def symlink(mctx, x):
   307 def symlink(mctx, x):
   312     """File that is marked as a symlink.
   308     """File that is marked as a symlink.
   313     """
   309     """
   314     # i18n: "symlink" is a keyword
   310     # i18n: "symlink" is a keyword
   315     getargs(x, 0, 0, _("symlink takes no arguments"))
   311     getargs(x, 0, 0, _("symlink takes no arguments"))
   316     return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l']
   312     ctx = mctx.ctx
       
   313     return mctx.predicate(lambda f: ctx.flags(f) == 'l', predrepr='symlink')
   317 
   314 
   318 @predicate('resolved()')
   315 @predicate('resolved()')
   319 def resolved(mctx, x):
   316 def resolved(mctx, x):
   320     """File that is marked resolved according to :hg:`resolve -l`.
   317     """File that is marked resolved according to :hg:`resolve -l`.
   321     """
   318     """
   322     # i18n: "resolved" is a keyword
   319     # i18n: "resolved" is a keyword
   323     getargs(x, 0, 0, _("resolved takes no arguments"))
   320     getargs(x, 0, 0, _("resolved takes no arguments"))
   324     if mctx.ctx.rev() is not None:
   321     if mctx.ctx.rev() is not None:
   325         return []
   322         return mctx.never()
   326     ms = merge.mergestate.read(mctx.ctx.repo())
   323     ms = merge.mergestate.read(mctx.ctx.repo())
   327     return [f for f in mctx.subset if f in ms and ms[f] == 'r']
   324     return mctx.predicate(lambda f: f in ms and ms[f] == 'r',
       
   325                           predrepr='resolved')
   328 
   326 
   329 @predicate('unresolved()')
   327 @predicate('unresolved()')
   330 def unresolved(mctx, x):
   328 def unresolved(mctx, x):
   331     """File that is marked unresolved according to :hg:`resolve -l`.
   329     """File that is marked unresolved according to :hg:`resolve -l`.
   332     """
   330     """
   333     # i18n: "unresolved" is a keyword
   331     # i18n: "unresolved" is a keyword
   334     getargs(x, 0, 0, _("unresolved takes no arguments"))
   332     getargs(x, 0, 0, _("unresolved takes no arguments"))
   335     if mctx.ctx.rev() is not None:
   333     if mctx.ctx.rev() is not None:
   336         return []
   334         return mctx.never()
   337     ms = merge.mergestate.read(mctx.ctx.repo())
   335     ms = merge.mergestate.read(mctx.ctx.repo())
   338     return [f for f in mctx.subset if f in ms and ms[f] == 'u']
   336     return mctx.predicate(lambda f: f in ms and ms[f] == 'u',
       
   337                           predrepr='unresolved')
   339 
   338 
   340 @predicate('hgignore()')
   339 @predicate('hgignore()')
   341 def hgignore(mctx, x):
   340 def hgignore(mctx, x):
   342     """File that matches the active .hgignore pattern.
   341     """File that matches the active .hgignore pattern.
   343     """
   342     """
   344     # i18n: "hgignore" is a keyword
   343     # i18n: "hgignore" is a keyword
   345     getargs(x, 0, 0, _("hgignore takes no arguments"))
   344     getargs(x, 0, 0, _("hgignore takes no arguments"))
   346     ignore = mctx.ctx.repo().dirstate._ignore
   345     return mctx.ctx.repo().dirstate._ignore
   347     return [f for f in mctx.subset if ignore(f)]
       
   348 
   346 
   349 @predicate('portable()')
   347 @predicate('portable()')
   350 def portable(mctx, x):
   348 def portable(mctx, x):
   351     """File that has a portable name. (This doesn't include filenames with case
   349     """File that has a portable name. (This doesn't include filenames with case
   352     collisions.)
   350     collisions.)
   353     """
   351     """
   354     # i18n: "portable" is a keyword
   352     # i18n: "portable" is a keyword
   355     getargs(x, 0, 0, _("portable takes no arguments"))
   353     getargs(x, 0, 0, _("portable takes no arguments"))
   356     checkwinfilename = util.checkwinfilename
   354     return mctx.predicate(lambda f: util.checkwinfilename(f) is None,
   357     return [f for f in mctx.subset if checkwinfilename(f) is None]
   355                           predrepr='portable')
   358 
   356 
   359 @predicate('grep(regex)', callexisting=True)
   357 @predicate('grep(regex)', callexisting=True)
   360 def grep(mctx, x):
   358 def grep(mctx, x):
   361     """File contains the given regular expression.
   359     """File contains the given regular expression.
   362     """
   360     """
   364         # i18n: "grep" is a keyword
   362         # i18n: "grep" is a keyword
   365         r = re.compile(getstring(x, _("grep requires a pattern")))
   363         r = re.compile(getstring(x, _("grep requires a pattern")))
   366     except re.error as e:
   364     except re.error as e:
   367         raise error.ParseError(_('invalid match pattern: %s') %
   365         raise error.ParseError(_('invalid match pattern: %s') %
   368                                stringutil.forcebytestr(e))
   366                                stringutil.forcebytestr(e))
   369     return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())]
   367     return mctx.fpredicate(lambda fctx: r.search(fctx.data()),
       
   368                            predrepr=('grep(%r)', r.pattern), cache=True)
   370 
   369 
   371 def _sizetomax(s):
   370 def _sizetomax(s):
   372     try:
   371     try:
   373         s = s.strip().lower()
   372         s = s.strip().lower()
   374         for k, v in util._sizeunits:
   373         for k, v in util._sizeunits:
   419     - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
   418     - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
   420     """
   419     """
   421     # i18n: "size" is a keyword
   420     # i18n: "size" is a keyword
   422     expr = getstring(x, _("size requires an expression"))
   421     expr = getstring(x, _("size requires an expression"))
   423     m = sizematcher(expr)
   422     m = sizematcher(expr)
   424     return [f for f in mctx.existing() if m(mctx.ctx[f].size())]
   423     return mctx.fpredicate(lambda fctx: m(fctx.size()),
       
   424                            predrepr=('size(%r)', expr), cache=True)
   425 
   425 
   426 @predicate('encoding(name)', callexisting=True)
   426 @predicate('encoding(name)', callexisting=True)
   427 def encoding(mctx, x):
   427 def encoding(mctx, x):
   428     """File can be successfully decoded with the given character
   428     """File can be successfully decoded with the given character
   429     encoding. May not be useful for encodings other than ASCII and
   429     encoding. May not be useful for encodings other than ASCII and
   431     """
   431     """
   432 
   432 
   433     # i18n: "encoding" is a keyword
   433     # i18n: "encoding" is a keyword
   434     enc = getstring(x, _("encoding requires an encoding name"))
   434     enc = getstring(x, _("encoding requires an encoding name"))
   435 
   435 
   436     s = []
   436     def encp(fctx):
   437     for f in mctx.existing():
   437         d = fctx.data()
   438         d = mctx.ctx[f].data()
       
   439         try:
   438         try:
   440             d.decode(pycompat.sysstr(enc))
   439             d.decode(pycompat.sysstr(enc))
       
   440             return True
   441         except LookupError:
   441         except LookupError:
   442             raise error.Abort(_("unknown encoding '%s'") % enc)
   442             raise error.Abort(_("unknown encoding '%s'") % enc)
   443         except UnicodeDecodeError:
   443         except UnicodeDecodeError:
   444             continue
   444             return False
   445         s.append(f)
   445 
   446 
   446     return mctx.fpredicate(encp, predrepr=('encoding(%r)', enc), cache=True)
   447     return s
       
   448 
   447 
   449 @predicate('eol(style)', callexisting=True)
   448 @predicate('eol(style)', callexisting=True)
   450 def eol(mctx, x):
   449 def eol(mctx, x):
   451     """File contains newlines of the given style (dos, unix, mac). Binary
   450     """File contains newlines of the given style (dos, unix, mac). Binary
   452     files are excluded, files with mixed line endings match multiple
   451     files are excluded, files with mixed line endings match multiple
   454     """
   453     """
   455 
   454 
   456     # i18n: "eol" is a keyword
   455     # i18n: "eol" is a keyword
   457     enc = getstring(x, _("eol requires a style name"))
   456     enc = getstring(x, _("eol requires a style name"))
   458 
   457 
   459     s = []
   458     def eolp(fctx):
   460     for f in mctx.existing():
       
   461         fctx = mctx.ctx[f]
       
   462         if fctx.isbinary():
   459         if fctx.isbinary():
   463             continue
   460             return False
   464         d = fctx.data()
   461         d = fctx.data()
   465         if (enc == 'dos' or enc == 'win') and '\r\n' in d:
   462         if (enc == 'dos' or enc == 'win') and '\r\n' in d:
   466             s.append(f)
   463             return True
   467         elif enc == 'unix' and re.search('(?<!\r)\n', d):
   464         elif enc == 'unix' and re.search('(?<!\r)\n', d):
   468             s.append(f)
   465             return True
   469         elif enc == 'mac' and re.search('\r(?!\n)', d):
   466         elif enc == 'mac' and re.search('\r(?!\n)', d):
   470             s.append(f)
   467             return True
   471     return s
   468         return False
       
   469     return mctx.fpredicate(eolp, predrepr=('eol(%r)', enc), cache=True)
   472 
   470 
   473 @predicate('copied()')
   471 @predicate('copied()')
   474 def copied(mctx, x):
   472 def copied(mctx, x):
   475     """File that is recorded as being copied.
   473     """File that is recorded as being copied.
   476     """
   474     """
   477     # i18n: "copied" is a keyword
   475     # i18n: "copied" is a keyword
   478     getargs(x, 0, 0, _("copied takes no arguments"))
   476     getargs(x, 0, 0, _("copied takes no arguments"))
   479     s = []
   477     def copiedp(fctx):
   480     for f in mctx.subset:
   478         p = fctx.parents()
   481         if f in mctx.ctx:
   479         return p and p[0].path() != fctx.path()
   482             p = mctx.ctx[f].parents()
   480     return mctx.fpredicate(copiedp, predrepr='copied', cache=True)
   483             if p and p[0].path() != f:
       
   484                 s.append(f)
       
   485     return s
       
   486 
   481 
   487 @predicate('revs(revs, pattern)')
   482 @predicate('revs(revs, pattern)')
   488 def revs(mctx, x):
   483 def revs(mctx, x):
   489     """Evaluate set in the specified revisions. If the revset match multiple
   484     """Evaluate set in the specified revisions. If the revset match multiple
   490     revs, this will return file matching pattern in any of the revision.
   485     revs, this will return file matching pattern in any of the revision.
   494     # i18n: "revs" is a keyword
   489     # i18n: "revs" is a keyword
   495     revspec = getstring(r, _("first argument to revs must be a revision"))
   490     revspec = getstring(r, _("first argument to revs must be a revision"))
   496     repo = mctx.ctx.repo()
   491     repo = mctx.ctx.repo()
   497     revs = scmutil.revrange(repo, [revspec])
   492     revs = scmutil.revrange(repo, [revspec])
   498 
   493 
   499     found = set()
   494     matchers = []
   500     result = []
       
   501     for r in revs:
   495     for r in revs:
   502         ctx = repo[r]
   496         ctx = repo[r]
   503         for f in getset(mctx.switch(ctx, _buildstatus(ctx, x)), x):
   497         matchers.append(getmatch(mctx.switch(ctx, _buildstatus(ctx, x)), x))
   504             if f not in found:
   498     if not matchers:
   505                 found.add(f)
   499         return mctx.never()
   506                 result.append(f)
   500     if len(matchers) == 1:
   507     return result
   501         return matchers[0]
       
   502     return matchmod.unionmatcher(matchers)
   508 
   503 
   509 @predicate('status(base, rev, pattern)')
   504 @predicate('status(base, rev, pattern)')
   510 def status(mctx, x):
   505 def status(mctx, x):
   511     """Evaluate predicate using status change between ``base`` and
   506     """Evaluate predicate using status change between ``base`` and
   512     ``rev``. Examples:
   507     ``rev``. Examples:
   524     reverr = _("second argument to status must be a revision")
   519     reverr = _("second argument to status must be a revision")
   525     revspec = getstring(r, reverr)
   520     revspec = getstring(r, reverr)
   526     if not revspec:
   521     if not revspec:
   527         raise error.ParseError(reverr)
   522         raise error.ParseError(reverr)
   528     basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
   523     basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
   529     return getset(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x)
   524     return getmatch(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x)
   530 
   525 
   531 @predicate('subrepo([pattern])')
   526 @predicate('subrepo([pattern])')
   532 def subrepo(mctx, x):
   527 def subrepo(mctx, x):
   533     """Subrepositories whose paths match the given pattern.
   528     """Subrepositories whose paths match the given pattern.
   534     """
   529     """
   535     # i18n: "subrepo" is a keyword
   530     # i18n: "subrepo" is a keyword
   536     getargs(x, 0, 1, _("subrepo takes at most one argument"))
   531     getargs(x, 0, 1, _("subrepo takes at most one argument"))
   537     ctx = mctx.ctx
   532     ctx = mctx.ctx
   538     sstate = sorted(ctx.substate)
   533     sstate = ctx.substate
   539     if x:
   534     if x:
   540         pat = getpattern(x, matchmod.allpatternkinds,
   535         pat = getpattern(x, matchmod.allpatternkinds,
   541                          # i18n: "subrepo" is a keyword
   536                          # i18n: "subrepo" is a keyword
   542                          _("subrepo requires a pattern or no arguments"))
   537                          _("subrepo requires a pattern or no arguments"))
   543         fast = not matchmod.patkind(pat)
   538         fast = not matchmod.patkind(pat)
   544         if fast:
   539         if fast:
   545             def m(s):
   540             def m(s):
   546                 return (s == pat)
   541                 return (s == pat)
   547         else:
   542         else:
   548             m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
   543             m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
   549         return [sub for sub in sstate if m(sub)]
   544         return mctx.predicate(lambda f: f in sstate and m(f),
       
   545                               predrepr=('subrepo(%r)', pat))
   550     else:
   546     else:
   551         return [sub for sub in sstate]
   547         return mctx.predicate(sstate.__contains__, predrepr='subrepo')
   552 
   548 
   553 methods = {
   549 methods = {
   554     'string': stringset,
   550     'string': stringmatch,
   555     'symbol': stringset,
   551     'symbol': stringmatch,
   556     'kindpat': kindpatset,
   552     'kindpat': kindpatmatch,
   557     'and': andset,
   553     'and': andmatch,
   558     'or': orset,
   554     'or': ormatch,
   559     'minus': minusset,
   555     'minus': minusmatch,
   560     'negate': negateset,
   556     'negate': negatematch,
   561     'list': listset,
   557     'list': listmatch,
   562     'group': getset,
   558     'group': getmatch,
   563     'not': notset,
   559     'not': notmatch,
   564     'func': func,
   560     'func': func,
   565 }
   561 }
   566 
   562 
   567 class matchctx(object):
   563 class matchctx(object):
   568     def __init__(self, ctx, subset, status=None, badfn=None):
   564     def __init__(self, ctx, subset, status=None, badfn=None):
   678     else:
   674     else:
   679         return list(ctx.walk(ctx.match([])))
   675         return list(ctx.walk(ctx.match([])))
   680 
   676 
   681 def match(ctx, expr, badfn=None):
   677 def match(ctx, expr, badfn=None):
   682     """Create a matcher for a single fileset expression"""
   678     """Create a matcher for a single fileset expression"""
   683     repo = ctx.repo()
       
   684     tree = parse(expr)
   679     tree = parse(expr)
   685     fset = getset(fullmatchctx(ctx, _buildstatus(ctx, tree), badfn=badfn), tree)
   680     mctx = fullmatchctx(ctx, _buildstatus(ctx, tree), badfn=badfn)
   686     return matchmod.predicatematcher(repo.root, repo.getcwd(),
   681     return getmatch(mctx, tree)
   687                                      fset.__contains__,
       
   688                                      predrepr='fileset', badfn=badfn)
       
   689 
   682 
   690 def _buildstatus(ctx, tree, basectx=None):
   683 def _buildstatus(ctx, tree, basectx=None):
   691     # do we need status info?
   684     # do we need status info?
   692 
   685 
   693     # temporaty boolean to simplify the next conditional
   686     # temporaty boolean to simplify the next conditional