diff --git a/hgext/lfs/__init__.py b/hgext/lfs/__init__.py --- a/hgext/lfs/__init__.py +++ b/hgext/lfs/__init__.py @@ -362,8 +362,10 @@ def lfsfileset(mctx, x): """File that uses LFS storage.""" # i18n: "lfs" is a keyword fileset.getargs(x, 0, 0, _("lfs takes no arguments")) - return [f for f in mctx.subset - if wrapper.pointerfromctx(mctx.ctx, f, removed=True) is not None] + ctx = mctx.ctx + def lfsfilep(f): + return wrapper.pointerfromctx(ctx, f, removed=True) is not None + return mctx.predicate(lfsfilep, predrepr='') @templatekeyword('lfs_files', requires={'ctx'}) def lfsfiles(context, mapping): diff --git a/mercurial/fileset.py b/mercurial/fileset.py --- a/mercurial/fileset.py +++ b/mercurial/fileset.py @@ -140,43 +140,41 @@ def getargs(x, min, max, err): raise error.ParseError(err) return l -def getset(mctx, x): +def getmatch(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 stringmatch(mctx, x): + return mctx.matcher([x]) -def kindpatset(mctx, x, y): - return stringset(mctx, _getkindpat(x, y, matchmod.allpatternkinds, - _("pattern must be a string"))) +def kindpatmatch(mctx, x, y): + return stringmatch(mctx, _getkindpat(x, y, matchmod.allpatternkinds, + _("pattern must be a string"))) -def andset(mctx, x, y): - xl = set(getset(mctx, x)) - yl = getset(mctx, y) - return [f for f in yl if f in xl] +def andmatch(mctx, x, y): + xm = getmatch(mctx, x) + ym = getmatch(mctx, y) + return matchmod.intersectmatchers(xm, ym) -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 ormatch(mctx, x, y): + xm = getmatch(mctx, x) + ym = getmatch(mctx, y) + return matchmod.unionmatcher([xm, ym]) -def notset(mctx, x): - s = set(getset(mctx, x)) - return [r for r in mctx.subset if r not in s] +def notmatch(mctx, x): + m = getmatch(mctx, x) + return mctx.predicate(lambda f: not m(f), predrepr=('', m)) -def minusset(mctx, x, y): - xl = getset(mctx, x) - yl = set(getset(mctx, y)) - return [f for f in xl if f not in yl] +def minusmatch(mctx, x, y): + xm = getmatch(mctx, x) + ym = getmatch(mctx, y) + return matchmod.differencematcher(xm, ym) -def negateset(mctx, x): +def negatematch(mctx, x): raise error.ParseError(_("can't use negate operator in this context")) -def listset(mctx, a, b): +def listmatch(mctx, x, y): raise error.ParseError(_("can't use a list in this context"), hint=_('see hg help "filesets.x or y"')) @@ -217,7 +215,7 @@ def modified(mctx, x): # i18n: "modified" is a keyword getargs(x, 0, 0, _("modified takes no arguments")) s = set(mctx.status().modified) - return [f for f in mctx.subset if f in s] + return mctx.predicate(s.__contains__, predrepr='modified') @predicate('added()', callstatus=True) def added(mctx, x): @@ -226,7 +224,7 @@ def added(mctx, x): # i18n: "added" is a keyword getargs(x, 0, 0, _("added takes no arguments")) s = set(mctx.status().added) - return [f for f in mctx.subset if f in s] + return mctx.predicate(s.__contains__, predrepr='added') @predicate('removed()', callstatus=True) def removed(mctx, x): @@ -235,7 +233,7 @@ def removed(mctx, x): # i18n: "removed" is a keyword getargs(x, 0, 0, _("removed takes no arguments")) s = set(mctx.status().removed) - return [f for f in mctx.subset if f in s] + return mctx.predicate(s.__contains__, predrepr='removed') @predicate('deleted()', callstatus=True) def deleted(mctx, x): @@ -244,7 +242,7 @@ def deleted(mctx, x): # i18n: "deleted" is a keyword getargs(x, 0, 0, _("deleted takes no arguments")) s = set(mctx.status().deleted) - return [f for f in mctx.subset if f in s] + return mctx.predicate(s.__contains__, predrepr='deleted') @predicate('missing()', callstatus=True) def missing(mctx, x): @@ -253,27 +251,23 @@ def missing(mctx, x): # i18n: "missing" is a keyword getargs(x, 0, 0, _("missing takes no arguments")) s = set(mctx.status().deleted) - return [f for f in mctx.subset if f in s] + return mctx.predicate(s.__contains__, predrepr='deleted') @predicate('unknown()', callstatus=True) def unknown(mctx, x): - """File that is unknown according to :hg:`status`. These files will only be - considered if this predicate is used. - """ + """File that is unknown according to :hg:`status`.""" # i18n: "unknown" is a keyword getargs(x, 0, 0, _("unknown takes no arguments")) s = set(mctx.status().unknown) - return [f for f in mctx.subset if f in s] + return mctx.predicate(s.__contains__, predrepr='unknown') @predicate('ignored()', callstatus=True) def ignored(mctx, x): - """File that is ignored according to :hg:`status`. These files will only be - considered if this predicate is used. - """ + """File that is ignored according to :hg:`status`.""" # i18n: "ignored" is a keyword getargs(x, 0, 0, _("ignored takes no arguments")) s = set(mctx.status().ignored) - return [f for f in mctx.subset if f in s] + return mctx.predicate(s.__contains__, predrepr='ignored') @predicate('clean()', callstatus=True) def clean(mctx, x): @@ -282,14 +276,14 @@ def clean(mctx, x): # i18n: "clean" is a keyword getargs(x, 0, 0, _("clean takes no arguments")) s = set(mctx.status().clean) - return [f for f in mctx.subset if f in s] + return mctx.predicate(s.__contains__, predrepr='clean') @predicate('tracked()') def tracked(mctx, x): """File that is under Mercurial control.""" # i18n: "tracked" is a keyword getargs(x, 0, 0, _("tracked takes no arguments")) - return [f for f in mctx.subset if f in mctx.ctx] + return mctx.predicate(mctx.ctx.__contains__, predrepr='tracked') @predicate('binary()', callexisting=True) def binary(mctx, x): @@ -297,7 +291,8 @@ def binary(mctx, x): """ # i18n: "binary" is a keyword getargs(x, 0, 0, _("binary takes no arguments")) - return [f for f in mctx.existing() if mctx.ctx[f].isbinary()] + return mctx.fpredicate(lambda fctx: fctx.isbinary(), + predrepr='binary', cache=True) @predicate('exec()', callexisting=True) def exec_(mctx, x): @@ -305,7 +300,8 @@ def exec_(mctx, x): """ # i18n: "exec" is a keyword getargs(x, 0, 0, _("exec takes no arguments")) - return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x'] + ctx = mctx.ctx + return mctx.predicate(lambda f: ctx.flags(f) == 'x', predrepr='exec') @predicate('symlink()', callexisting=True) def symlink(mctx, x): @@ -313,7 +309,8 @@ def symlink(mctx, x): """ # i18n: "symlink" is a keyword getargs(x, 0, 0, _("symlink takes no arguments")) - return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l'] + ctx = mctx.ctx + return mctx.predicate(lambda f: ctx.flags(f) == 'l', predrepr='symlink') @predicate('resolved()') def resolved(mctx, x): @@ -322,9 +319,10 @@ def resolved(mctx, x): # i18n: "resolved" is a keyword getargs(x, 0, 0, _("resolved takes no arguments")) if mctx.ctx.rev() is not None: - return [] + return mctx.never() ms = merge.mergestate.read(mctx.ctx.repo()) - return [f for f in mctx.subset if f in ms and ms[f] == 'r'] + return mctx.predicate(lambda f: f in ms and ms[f] == 'r', + predrepr='resolved') @predicate('unresolved()') def unresolved(mctx, x): @@ -333,9 +331,10 @@ def unresolved(mctx, x): # i18n: "unresolved" is a keyword getargs(x, 0, 0, _("unresolved takes no arguments")) if mctx.ctx.rev() is not None: - return [] + return mctx.never() ms = merge.mergestate.read(mctx.ctx.repo()) - return [f for f in mctx.subset if f in ms and ms[f] == 'u'] + return mctx.predicate(lambda f: f in ms and ms[f] == 'u', + predrepr='unresolved') @predicate('hgignore()') def hgignore(mctx, x): @@ -343,8 +342,7 @@ def hgignore(mctx, x): """ # i18n: "hgignore" is a keyword getargs(x, 0, 0, _("hgignore takes no arguments")) - ignore = mctx.ctx.repo().dirstate._ignore - return [f for f in mctx.subset if ignore(f)] + return mctx.ctx.repo().dirstate._ignore @predicate('portable()') def portable(mctx, x): @@ -353,8 +351,8 @@ def portable(mctx, x): """ # i18n: "portable" is a keyword getargs(x, 0, 0, _("portable takes no arguments")) - checkwinfilename = util.checkwinfilename - return [f for f in mctx.subset if checkwinfilename(f) is None] + return mctx.predicate(lambda f: util.checkwinfilename(f) is None, + predrepr='portable') @predicate('grep(regex)', callexisting=True) def grep(mctx, x): @@ -366,7 +364,8 @@ def grep(mctx, x): except re.error as e: raise error.ParseError(_('invalid match pattern: %s') % stringutil.forcebytestr(e)) - return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())] + return mctx.fpredicate(lambda fctx: r.search(fctx.data()), + predrepr=('grep(%r)', r.pattern), cache=True) def _sizetomax(s): try: @@ -421,7 +420,8 @@ def size(mctx, x): # i18n: "size" is a keyword expr = getstring(x, _("size requires an expression")) m = sizematcher(expr) - return [f for f in mctx.existing() if m(mctx.ctx[f].size())] + return mctx.fpredicate(lambda fctx: m(fctx.size()), + predrepr=('size(%r)', expr), cache=True) @predicate('encoding(name)', callexisting=True) def encoding(mctx, x): @@ -433,18 +433,17 @@ def encoding(mctx, x): # i18n: "encoding" is a keyword enc = getstring(x, _("encoding requires an encoding name")) - s = [] - for f in mctx.existing(): - d = mctx.ctx[f].data() + def encp(fctx): + d = fctx.data() try: d.decode(pycompat.sysstr(enc)) + return True except LookupError: raise error.Abort(_("unknown encoding '%s'") % enc) except UnicodeDecodeError: - continue - s.append(f) + return False - return s + return mctx.fpredicate(encp, predrepr=('encoding(%r)', enc), cache=True) @predicate('eol(style)', callexisting=True) def eol(mctx, x): @@ -456,19 +455,18 @@ def eol(mctx, x): # i18n: "eol" is a keyword enc = getstring(x, _("eol requires a style name")) - s = [] - for f in mctx.existing(): - fctx = mctx.ctx[f] + def eolp(fctx): if fctx.isbinary(): - continue + return False d = fctx.data() if (enc == 'dos' or enc == 'win') and '\r\n' in d: - s.append(f) + return True elif enc == 'unix' and re.search('(?>> open('bin', 'wb').write(b'\0a') and None $ fileset 'binary()' + bin $ fileset 'binary() and unknown()' bin $ echo '^bin$' >> .hgignore @@ -192,6 +195,7 @@ Test files properties bin $ fileset 'grep("b{1}")' + .hgignore b1 b2 c1 @@ -354,8 +358,12 @@ Test with a revision $ fileset -r1 'unknown()' $ fileset -r1 'ignored()' $ fileset -r1 'hgignore()' + .hgignore + a2 b2 bin + c2 + sub2 $ fileset -r1 'binary()' bin $ fileset -r1 'size(1k)' @@ -403,30 +411,42 @@ Test with a revision dos mixed $ fileset 'eol(unix)' + .hgignore .hgsub .hgsubstate b1 b2 + b2.orig c1 + c2 + c3 + con.xml mixed + unknown $ fileset 'eol(mac)' mac Test safety of 'encoding' on removed files $ fileset 'encoding("ascii")' + .hgignore .hgsub .hgsubstate 1k 2k b1 b2 + b2.orig b2link (symlink !) bin c1 + c2 + c3 + con.xml dos mac mixed + unknown Test detection of unintentional 'matchctx.existing()' invocation @@ -437,7 +457,8 @@ Test detection of unintentional 'matchct > @filesetpredicate(b'existingcaller()', callexisting=False) > def existingcaller(mctx, x): > # this 'mctx.existing()' invocation is unintentional - > return [f for f in mctx.existing()] + > existing = set(mctx.existing()) + > return mctx.predicate(existing.__contains__, cache=False) > EOF $ cat >> .hg/hgrc <