fileset.py
572 lines
| 18.7 KiB
| text/x-python
|
PythonLexer
/ mercurial / fileset.py
|
r14511 | # fileset.py - file set queries for mercurial | ||
# | ||||
# Copyright 2010 Matt Mackall <mpm@selenic.com> | ||||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
|
r25938 | from __future__ import absolute_import | ||
|
r38706 | import errno | ||
|
r20034 | import re | ||
|
r25938 | |||
from .i18n import _ | ||||
from . import ( | ||||
error, | ||||
|
r38841 | filesetlang, | ||
|
r35757 | match as matchmod, | ||
|
r25938 | merge, | ||
|
r32523 | pycompat, | ||
|
r28448 | registrar, | ||
|
r31193 | scmutil, | ||
|
r25938 | util, | ||
) | ||||
|
r37102 | from .utils import ( | ||
stringutil, | ||||
) | ||||
|
r14511 | |||
|
r38899 | # common weight constants | ||
_WEIGHT_CHECK_FILENAME = filesetlang.WEIGHT_CHECK_FILENAME | ||||
_WEIGHT_READ_CONTENTS = filesetlang.WEIGHT_READ_CONTENTS | ||||
_WEIGHT_STATUS = filesetlang.WEIGHT_STATUS | ||||
_WEIGHT_STATUS_THOROUGH = filesetlang.WEIGHT_STATUS_THOROUGH | ||||
|
r38841 | # helpers for processing parsed tree | ||
getsymbol = filesetlang.getsymbol | ||||
getstring = filesetlang.getstring | ||||
_getkindpat = filesetlang.getkindpat | ||||
getpattern = filesetlang.getpattern | ||||
getargs = filesetlang.getargs | ||||
|
r38617 | |||
|
r38711 | def getmatch(mctx, x): | ||
|
r14551 | if not x: | ||
raise error.ParseError(_("missing argument")) | ||||
return methods[x[0]](mctx, *x[1:]) | ||||
|
r38711 | def stringmatch(mctx, x): | ||
return mctx.matcher([x]) | ||||
|
r14551 | |||
|
r38711 | def kindpatmatch(mctx, x, y): | ||
return stringmatch(mctx, _getkindpat(x, y, matchmod.allpatternkinds, | ||||
_("pattern must be a string"))) | ||||
|
r35759 | |||
|
r38901 | def patternsmatch(mctx, *xs): | ||
allkinds = matchmod.allpatternkinds | ||||
patterns = [getpattern(x, allkinds, _("pattern must be a string")) | ||||
for x in xs] | ||||
return mctx.matcher(patterns) | ||||
|
r38711 | def andmatch(mctx, x, y): | ||
xm = getmatch(mctx, x) | ||||
ym = getmatch(mctx, y) | ||||
return matchmod.intersectmatchers(xm, ym) | ||||
|
r14551 | |||
|
r38840 | def ormatch(mctx, *xs): | ||
ms = [getmatch(mctx, x) for x in xs] | ||||
return matchmod.unionmatcher(ms) | ||||
|
r14551 | |||
|
r38711 | def notmatch(mctx, x): | ||
m = getmatch(mctx, x) | ||||
return mctx.predicate(lambda f: not m(f), predrepr=('<not %r>', m)) | ||||
|
r14551 | |||
|
r38711 | def minusmatch(mctx, x, y): | ||
xm = getmatch(mctx, x) | ||||
ym = getmatch(mctx, y) | ||||
return matchmod.differencematcher(xm, ym) | ||||
|
r17363 | |||
|
r38839 | def listmatch(mctx, *xs): | ||
|
r27518 | raise error.ParseError(_("can't use a list in this context"), | ||
|
r38846 | hint=_('see \'hg help "filesets.x or y"\'')) | ||
|
r14551 | |||
|
r38617 | def func(mctx, a, b): | ||
funcname = getsymbol(a) | ||||
if funcname in symbols: | ||||
|
r38712 | return symbols[funcname](mctx, b) | ||
|
r38617 | |||
keep = lambda fn: getattr(fn, '__doc__', None) is not None | ||||
syms = [s for (s, fn) in symbols.items() if keep(fn)] | ||||
raise error.UnknownIdentifier(funcname, syms) | ||||
|
r27460 | # symbols are callable like: | ||
# fun(mctx, x) | ||||
# with: | ||||
# mctx - current matchctx instance | ||||
# x - argument in tree form | ||||
|
r38841 | symbols = filesetlang.symbols | ||
|
r27460 | |||
|
r27461 | # filesets using matchctx.status() | ||
|
r27463 | _statuscallers = set() | ||
|
r27461 | |||
|
r28448 | predicate = registrar.filesetpredicate() | ||
|
r27460 | |||
|
r38899 | @predicate('modified()', callstatus=True, weight=_WEIGHT_STATUS) | ||
|
r14677 | def modified(mctx, x): | ||
|
r27460 | """File that is modified according to :hg:`status`. | ||
|
r14681 | """ | ||
|
r14785 | # i18n: "modified" is a keyword | ||
|
r14677 | getargs(x, 0, 0, _("modified takes no arguments")) | ||
|
r31697 | s = set(mctx.status().modified) | ||
|
r38711 | return mctx.predicate(s.__contains__, predrepr='modified') | ||
|
r14677 | |||
|
r38899 | @predicate('added()', callstatus=True, weight=_WEIGHT_STATUS) | ||
|
r14677 | def added(mctx, x): | ||
|
r27460 | """File that is added according to :hg:`status`. | ||
|
r14681 | """ | ||
|
r14785 | # i18n: "added" is a keyword | ||
|
r14677 | getargs(x, 0, 0, _("added takes no arguments")) | ||
|
r31697 | s = set(mctx.status().added) | ||
|
r38711 | return mctx.predicate(s.__contains__, predrepr='added') | ||
|
r14677 | |||
|
r38899 | @predicate('removed()', callstatus=True, weight=_WEIGHT_STATUS) | ||
|
r14677 | def removed(mctx, x): | ||
|
r27460 | """File that is removed according to :hg:`status`. | ||
|
r14681 | """ | ||
|
r14785 | # i18n: "removed" is a keyword | ||
|
r14677 | getargs(x, 0, 0, _("removed takes no arguments")) | ||
|
r31697 | s = set(mctx.status().removed) | ||
|
r38711 | return mctx.predicate(s.__contains__, predrepr='removed') | ||
|
r14677 | |||
|
r38899 | @predicate('deleted()', callstatus=True, weight=_WEIGHT_STATUS) | ||
|
r14677 | def deleted(mctx, x): | ||
|
r27460 | """Alias for ``missing()``. | ||
|
r14681 | """ | ||
|
r14785 | # i18n: "deleted" is a keyword | ||
|
r14677 | getargs(x, 0, 0, _("deleted takes no arguments")) | ||
|
r31697 | s = set(mctx.status().deleted) | ||
|
r38711 | return mctx.predicate(s.__contains__, predrepr='deleted') | ||
|
r14677 | |||
|
r38899 | @predicate('missing()', callstatus=True, weight=_WEIGHT_STATUS) | ||
|
r27024 | def missing(mctx, x): | ||
|
r27460 | """File that is missing according to :hg:`status`. | ||
|
r27024 | """ | ||
# i18n: "missing" is a keyword | ||||
getargs(x, 0, 0, _("missing takes no arguments")) | ||||
|
r31697 | s = set(mctx.status().deleted) | ||
|
r38711 | return mctx.predicate(s.__contains__, predrepr='deleted') | ||
|
r27024 | |||
|
r38899 | @predicate('unknown()', callstatus=True, weight=_WEIGHT_STATUS_THOROUGH) | ||
|
r14677 | def unknown(mctx, x): | ||
|
r38711 | """File that is unknown according to :hg:`status`.""" | ||
|
r14785 | # i18n: "unknown" is a keyword | ||
|
r14677 | getargs(x, 0, 0, _("unknown takes no arguments")) | ||
|
r31697 | s = set(mctx.status().unknown) | ||
|
r38711 | return mctx.predicate(s.__contains__, predrepr='unknown') | ||
|
r14677 | |||
|
r38899 | @predicate('ignored()', callstatus=True, weight=_WEIGHT_STATUS_THOROUGH) | ||
|
r14677 | def ignored(mctx, x): | ||
|
r38711 | """File that is ignored according to :hg:`status`.""" | ||
|
r14785 | # i18n: "ignored" is a keyword | ||
|
r14677 | getargs(x, 0, 0, _("ignored takes no arguments")) | ||
|
r31697 | s = set(mctx.status().ignored) | ||
|
r38711 | return mctx.predicate(s.__contains__, predrepr='ignored') | ||
|
r14677 | |||
|
r38899 | @predicate('clean()', callstatus=True, weight=_WEIGHT_STATUS) | ||
|
r14677 | def clean(mctx, x): | ||
|
r27460 | """File that is clean according to :hg:`status`. | ||
|
r14681 | """ | ||
|
r14785 | # i18n: "clean" is a keyword | ||
|
r14677 | getargs(x, 0, 0, _("clean takes no arguments")) | ||
|
r31697 | s = set(mctx.status().clean) | ||
|
r38711 | return mctx.predicate(s.__contains__, predrepr='clean') | ||
|
r14677 | |||
|
r38708 | @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")) | ||||
|
r38711 | return mctx.predicate(mctx.ctx.__contains__, predrepr='tracked') | ||
|
r38708 | |||
|
r38899 | @predicate('binary()', weight=_WEIGHT_READ_CONTENTS) | ||
|
r14676 | def binary(mctx, x): | ||
|
r27460 | """File that appears to be binary (contains NUL bytes). | ||
|
r14681 | """ | ||
|
r14785 | # i18n: "binary" is a keyword | ||
|
r14676 | getargs(x, 0, 0, _("binary takes no arguments")) | ||
|
r38711 | return mctx.fpredicate(lambda fctx: fctx.isbinary(), | ||
predrepr='binary', cache=True) | ||||
|
r14676 | |||
|
r38712 | @predicate('exec()') | ||
|
r14676 | def exec_(mctx, x): | ||
|
r27460 | """File that is marked as executable. | ||
|
r14681 | """ | ||
|
r14785 | # i18n: "exec" is a keyword | ||
|
r14676 | getargs(x, 0, 0, _("exec takes no arguments")) | ||
|
r38711 | ctx = mctx.ctx | ||
return mctx.predicate(lambda f: ctx.flags(f) == 'x', predrepr='exec') | ||||
|
r14676 | |||
|
r38712 | @predicate('symlink()') | ||
|
r14676 | def symlink(mctx, x): | ||
|
r27460 | """File that is marked as a symlink. | ||
|
r14681 | """ | ||
|
r14785 | # i18n: "symlink" is a keyword | ||
|
r14676 | getargs(x, 0, 0, _("symlink takes no arguments")) | ||
|
r38711 | ctx = mctx.ctx | ||
return mctx.predicate(lambda f: ctx.flags(f) == 'l', predrepr='symlink') | ||||
|
r14676 | |||
|
r38899 | @predicate('resolved()', weight=_WEIGHT_STATUS) | ||
|
r14679 | def resolved(mctx, x): | ||
|
r27460 | """File that is marked resolved according to :hg:`resolve -l`. | ||
|
r14681 | """ | ||
|
r14785 | # i18n: "resolved" is a keyword | ||
|
r14679 | getargs(x, 0, 0, _("resolved takes no arguments")) | ||
if mctx.ctx.rev() is not None: | ||||
|
r38711 | return mctx.never() | ||
|
r26995 | ms = merge.mergestate.read(mctx.ctx.repo()) | ||
|
r38711 | return mctx.predicate(lambda f: f in ms and ms[f] == 'r', | ||
predrepr='resolved') | ||||
|
r14679 | |||
|
r38899 | @predicate('unresolved()', weight=_WEIGHT_STATUS) | ||
|
r14679 | def unresolved(mctx, x): | ||
|
r27460 | """File that is marked unresolved according to :hg:`resolve -l`. | ||
|
r14681 | """ | ||
|
r14785 | # i18n: "unresolved" is a keyword | ||
|
r14679 | getargs(x, 0, 0, _("unresolved takes no arguments")) | ||
if mctx.ctx.rev() is not None: | ||||
|
r38711 | return mctx.never() | ||
|
r26995 | ms = merge.mergestate.read(mctx.ctx.repo()) | ||
|
r38711 | return mctx.predicate(lambda f: f in ms and ms[f] == 'u', | ||
predrepr='unresolved') | ||||
|
r14679 | |||
|
r38899 | @predicate('hgignore()', weight=_WEIGHT_STATUS) | ||
|
r14680 | def hgignore(mctx, x): | ||
|
r27460 | """File that matches the active .hgignore pattern. | ||
|
r14681 | """ | ||
|
r23113 | # i18n: "hgignore" is a keyword | ||
|
r14680 | getargs(x, 0, 0, _("hgignore takes no arguments")) | ||
|
r38711 | return mctx.ctx.repo().dirstate._ignore | ||
|
r14680 | |||
|
r38899 | @predicate('portable()', weight=_WEIGHT_CHECK_FILENAME) | ||
|
r24408 | def portable(mctx, x): | ||
|
r27460 | """File that has a portable name. (This doesn't include filenames with case | ||
|
r24408 | collisions.) | ||
""" | ||||
# i18n: "portable" is a keyword | ||||
getargs(x, 0, 0, _("portable takes no arguments")) | ||||
|
r38711 | return mctx.predicate(lambda f: util.checkwinfilename(f) is None, | ||
predrepr='portable') | ||||
|
r24408 | |||
|
r38899 | @predicate('grep(regex)', weight=_WEIGHT_READ_CONTENTS) | ||
|
r14682 | def grep(mctx, x): | ||
|
r27460 | """File contains the given regular expression. | ||
|
r14682 | """ | ||
|
r17368 | try: | ||
# i18n: "grep" is a keyword | ||||
r = re.compile(getstring(x, _("grep requires a pattern"))) | ||||
|
r25660 | except re.error as e: | ||
|
r38094 | raise error.ParseError(_('invalid match pattern: %s') % | ||
stringutil.forcebytestr(e)) | ||||
|
r38711 | return mctx.fpredicate(lambda fctx: r.search(fctx.data()), | ||
predrepr=('grep(%r)', r.pattern), cache=True) | ||||
|
r14682 | |||
|
r14683 | def _sizetomax(s): | ||
try: | ||||
r25925 | s = s.strip().lower() | |||
|
r19194 | for k, v in util._sizeunits: | ||
|
r14683 | if s.endswith(k): | ||
# max(4k) = 5k - 1, max(4.5k) = 4.6k - 1 | ||||
n = s[:-len(k)] | ||||
inc = 1.0 | ||||
if "." in n: | ||||
inc /= 10 ** len(n.split(".")[1]) | ||||
return int((float(n) + inc) * v) - 1 | ||||
# no extension, this is a precise value | ||||
return int(s) | ||||
except ValueError: | ||||
|
r14716 | raise error.ParseError(_("couldn't parse size: %s") % s) | ||
|
r14683 | |||
|
r38709 | def sizematcher(expr): | ||
|
r35633 | """Return a function(size) -> bool from the ``size()`` expression""" | ||
|
r38709 | expr = expr.strip() | ||
|
r35633 | if '-' in expr: # do we have a range? | ||
a, b = expr.split('-', 1) | ||||
a = util.sizetoint(a) | ||||
b = util.sizetoint(b) | ||||
return lambda x: x >= a and x <= b | ||||
elif expr.startswith("<="): | ||||
a = util.sizetoint(expr[2:]) | ||||
return lambda x: x <= a | ||||
elif expr.startswith("<"): | ||||
a = util.sizetoint(expr[1:]) | ||||
return lambda x: x < a | ||||
elif expr.startswith(">="): | ||||
a = util.sizetoint(expr[2:]) | ||||
return lambda x: x >= a | ||||
elif expr.startswith(">"): | ||||
a = util.sizetoint(expr[1:]) | ||||
return lambda x: x > a | ||||
|
r36523 | else: | ||
|
r35633 | a = util.sizetoint(expr) | ||
b = _sizetomax(expr) | ||||
return lambda x: x >= a and x <= b | ||||
|
r38899 | @predicate('size(expression)', weight=_WEIGHT_STATUS) | ||
|
r14683 | def size(mctx, x): | ||
|
r27460 | """File size matches the given expression. Examples: | ||
|
r14683 | |||
|
r29987 | - size('1k') - files from 1024 to 2047 bytes | ||
- size('< 20k') - files less than 20480 bytes | ||||
- size('>= .5MB') - files at least 524288 bytes | ||||
- size('4k - 1MB') - files from 4096 bytes to 1048576 bytes | ||||
|
r14683 | """ | ||
|
r38709 | # i18n: "size" is a keyword | ||
expr = getstring(x, _("size requires an expression")) | ||||
m = sizematcher(expr) | ||||
|
r38711 | return mctx.fpredicate(lambda fctx: m(fctx.size()), | ||
predrepr=('size(%r)', expr), cache=True) | ||||
|
r14683 | |||
|
r38899 | @predicate('encoding(name)', weight=_WEIGHT_READ_CONTENTS) | ||
|
r14684 | def encoding(mctx, x): | ||
|
r27460 | """File can be successfully decoded with the given character | ||
|
r14684 | encoding. May not be useful for encodings other than ASCII and | ||
UTF-8. | ||||
""" | ||||
|
r14785 | # i18n: "encoding" is a keyword | ||
|
r14684 | enc = getstring(x, _("encoding requires an encoding name")) | ||
|
r38711 | def encp(fctx): | ||
d = fctx.data() | ||||
|
r14684 | try: | ||
|
r38347 | d.decode(pycompat.sysstr(enc)) | ||
|
r38711 | return True | ||
|
r14684 | except LookupError: | ||
|
r26587 | raise error.Abort(_("unknown encoding '%s'") % enc) | ||
|
r14684 | except UnicodeDecodeError: | ||
|
r38711 | return False | ||
|
r14684 | |||
|
r38711 | return mctx.fpredicate(encp, predrepr=('encoding(%r)', enc), cache=True) | ||
|
r14684 | |||
|
r38899 | @predicate('eol(style)', weight=_WEIGHT_READ_CONTENTS) | ||
|
r18842 | def eol(mctx, x): | ||
|
r27460 | """File contains newlines of the given style (dos, unix, mac). Binary | ||
|
r18842 | files are excluded, files with mixed line endings match multiple | ||
styles. | ||||
""" | ||||
|
r28056 | # i18n: "eol" is a keyword | ||
enc = getstring(x, _("eol requires a style name")) | ||||
|
r18842 | |||
|
r38711 | def eolp(fctx): | ||
|
r38433 | if fctx.isbinary(): | ||
|
r38711 | return False | ||
|
r38433 | d = fctx.data() | ||
|
r18842 | if (enc == 'dos' or enc == 'win') and '\r\n' in d: | ||
|
r38711 | return True | ||
|
r18842 | elif enc == 'unix' and re.search('(?<!\r)\n', d): | ||
|
r38711 | return True | ||
|
r18842 | elif enc == 'mac' and re.search('\r(?!\n)', d): | ||
|
r38711 | return True | ||
return False | ||||
return mctx.fpredicate(eolp, predrepr=('eol(%r)', enc), cache=True) | ||||
|
r18842 | |||
|
r27460 | @predicate('copied()') | ||
|
r14685 | def copied(mctx, x): | ||
|
r27460 | """File that is recorded as being copied. | ||
|
r14685 | """ | ||
|
r14785 | # i18n: "copied" is a keyword | ||
|
r14718 | getargs(x, 0, 0, _("copied takes no arguments")) | ||
|
r38711 | def copiedp(fctx): | ||
p = fctx.parents() | ||||
return p and p[0].path() != fctx.path() | ||||
return mctx.fpredicate(copiedp, predrepr='copied', cache=True) | ||||
|
r14685 | |||
|
r38899 | @predicate('revs(revs, pattern)', weight=_WEIGHT_STATUS) | ||
|
r31193 | def revs(mctx, x): | ||
|
r31254 | """Evaluate set in the specified revisions. If the revset match multiple | ||
revs, this will return file matching pattern in any of the revision. | ||||
|
r31193 | """ | ||
# i18n: "revs" is a keyword | ||||
r, x = getargs(x, 2, 2, _("revs takes two arguments")) | ||||
# i18n: "revs" is a keyword | ||||
revspec = getstring(r, _("first argument to revs must be a revision")) | ||||
repo = mctx.ctx.repo() | ||||
revs = scmutil.revrange(repo, [revspec]) | ||||
|
r38711 | matchers = [] | ||
|
r31193 | for r in revs: | ||
ctx = repo[r] | ||||
|
r38913 | mc = mctx.switch(ctx.p1(), ctx, _buildstatus(ctx.p1(), ctx, x)) | ||
|
r38912 | matchers.append(getmatch(mc, x)) | ||
|
r38711 | if not matchers: | ||
return mctx.never() | ||||
if len(matchers) == 1: | ||||
return matchers[0] | ||||
return matchmod.unionmatcher(matchers) | ||||
|
r31193 | |||
|
r38899 | @predicate('status(base, rev, pattern)', weight=_WEIGHT_STATUS) | ||
|
r31195 | def status(mctx, x): | ||
|
r31254 | """Evaluate predicate using status change between ``base`` and | ||
|
r31195 | ``rev``. Examples: | ||
- ``status(3, 7, added())`` - matches files added from "3" to "7" | ||||
""" | ||||
repo = mctx.ctx.repo() | ||||
# i18n: "status" is a keyword | ||||
b, r, x = getargs(x, 3, 3, _("status takes three arguments")) | ||||
# i18n: "status" is a keyword | ||||
baseerr = _("first argument to status must be a revision") | ||||
baserevspec = getstring(b, baseerr) | ||||
if not baserevspec: | ||||
raise error.ParseError(baseerr) | ||||
reverr = _("second argument to status must be a revision") | ||||
revspec = getstring(r, reverr) | ||||
if not revspec: | ||||
raise error.ParseError(reverr) | ||||
|
r37274 | basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec]) | ||
|
r38913 | mc = mctx.switch(basectx, ctx, _buildstatus(basectx, ctx, x)) | ||
|
r38912 | return getmatch(mc, x) | ||
|
r31195 | |||
|
r27460 | @predicate('subrepo([pattern])') | ||
|
r16443 | def subrepo(mctx, x): | ||
|
r27460 | """Subrepositories whose paths match the given pattern. | ||
|
r16443 | """ | ||
# i18n: "subrepo" is a keyword | ||||
getargs(x, 0, 1, _("subrepo takes at most one argument")) | ||||
ctx = mctx.ctx | ||||
|
r38711 | sstate = ctx.substate | ||
|
r16443 | if x: | ||
|
r35759 | pat = getpattern(x, matchmod.allpatternkinds, | ||
# i18n: "subrepo" is a keyword | ||||
_("subrepo requires a pattern or no arguments")) | ||||
|
r16443 | fast = not matchmod.patkind(pat) | ||
if fast: | ||||
def m(s): | ||||
return (s == pat) | ||||
else: | ||||
|
r24334 | m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx) | ||
|
r38711 | return mctx.predicate(lambda f: f in sstate and m(f), | ||
predrepr=('subrepo(%r)', pat)) | ||||
|
r16443 | else: | ||
|
r38711 | return mctx.predicate(sstate.__contains__, predrepr='subrepo') | ||
|
r16443 | |||
|
r14551 | methods = { | ||
|
r38711 | 'string': stringmatch, | ||
'symbol': stringmatch, | ||||
'kindpat': kindpatmatch, | ||||
|
r38901 | 'patterns': patternsmatch, | ||
|
r38711 | 'and': andmatch, | ||
'or': ormatch, | ||||
'minus': minusmatch, | ||||
'list': listmatch, | ||||
'not': notmatch, | ||||
|
r14676 | 'func': func, | ||
|
r14551 | } | ||
class matchctx(object): | ||||
|
r38913 | def __init__(self, basectx, ctx, status=None, badfn=None): | ||
self._basectx = basectx | ||||
|
r14551 | self.ctx = ctx | ||
|
r14677 | self._status = status | ||
|
r38632 | self._badfn = badfn | ||
|
r38712 | |||
|
r14677 | def status(self): | ||
return self._status | ||||
|
r38706 | |||
|
r14673 | def matcher(self, patterns): | ||
|
r38632 | return self.ctx.match(patterns, badfn=self._badfn) | ||
|
r38706 | |||
def predicate(self, predfn, predrepr=None, cache=False): | ||||
"""Create a matcher to select files by predfn(filename)""" | ||||
if cache: | ||||
predfn = util.cachefunc(predfn) | ||||
repo = self.ctx.repo() | ||||
return matchmod.predicatematcher(repo.root, repo.getcwd(), predfn, | ||||
predrepr=predrepr, badfn=self._badfn) | ||||
def fpredicate(self, predfn, predrepr=None, cache=False): | ||||
"""Create a matcher to select files by predfn(fctx) at the current | ||||
revision | ||||
Missing files are ignored. | ||||
""" | ||||
ctx = self.ctx | ||||
if ctx.rev() is None: | ||||
def fctxpredfn(f): | ||||
try: | ||||
fctx = ctx[f] | ||||
except error.LookupError: | ||||
return False | ||||
try: | ||||
fctx.audit() | ||||
except error.Abort: | ||||
return False | ||||
try: | ||||
return predfn(fctx) | ||||
except (IOError, OSError) as e: | ||||
|
r38781 | # open()-ing a directory fails with EACCES on Windows | ||
if e.errno in (errno.ENOENT, errno.EACCES, errno.ENOTDIR, | ||||
errno.EISDIR): | ||||
|
r38706 | return False | ||
raise | ||||
else: | ||||
def fctxpredfn(f): | ||||
try: | ||||
fctx = ctx[f] | ||||
except error.LookupError: | ||||
return False | ||||
return predfn(fctx) | ||||
return self.predicate(fctxpredfn, predrepr=predrepr, cache=cache) | ||||
def never(self): | ||||
"""Create a matcher to select nothing""" | ||||
repo = self.ctx.repo() | ||||
return matchmod.nevermatcher(repo.root, repo.getcwd(), | ||||
badfn=self._badfn) | ||||
|
r38913 | def switch(self, basectx, ctx, status=None): | ||
return matchctx(basectx, ctx, status, self._badfn) | ||||
|
r14551 | |||
|
r31192 | # filesets using matchctx.switch() | ||
_switchcallers = [ | ||||
|
r31193 | 'revs', | ||
|
r31195 | 'status', | ||
|
r31192 | ] | ||
|
r31188 | |||
|
r14678 | def _intree(funcs, tree): | ||
if isinstance(tree, tuple): | ||||
if tree[0] == 'func' and tree[1][0] == 'symbol': | ||||
if tree[1][1] in funcs: | ||||
return True | ||||
|
r31192 | if tree[1][1] in _switchcallers: | ||
# arguments won't be evaluated in the current context | ||||
return False | ||||
|
r14678 | for s in tree[1:]: | ||
if _intree(funcs, s): | ||||
return True | ||||
return False | ||||
|
r38631 | def match(ctx, expr, badfn=None): | ||
"""Create a matcher for a single fileset expression""" | ||||
|
r38841 | tree = filesetlang.parse(expr) | ||
|
r38862 | tree = filesetlang.analyze(tree) | ||
|
r38865 | tree = filesetlang.optimize(tree) | ||
|
r38913 | mctx = matchctx(ctx.p1(), ctx, _buildstatus(ctx.p1(), ctx, tree), | ||
badfn=badfn) | ||||
|
r38711 | return getmatch(mctx, tree) | ||
|
r14678 | |||
|
r38912 | def _buildstatus(basectx, ctx, tree): | ||
|
r14678 | # do we need status info? | ||
|
r31194 | |||
|
r38712 | if _intree(_statuscallers, tree): | ||
|
r14678 | unknown = _intree(['unknown'], tree) | ||
ignored = _intree(['ignored'], tree) | ||||
|
r38795 | return basectx.status(ctx, listunknown=unknown, listignored=ignored, | ||
listclean=True) | ||||
|
r14678 | else: | ||
|
r31191 | return None | ||
|
r14681 | |||
|
r28447 | def loadpredicate(ui, extname, registrarobj): | ||
"""Load fileset predicates from specified registrarobj | ||||
""" | ||||
for name, func in registrarobj._table.iteritems(): | ||||
symbols[name] = func | ||||
if func._callstatus: | ||||
_statuscallers.add(name) | ||||
|
r38712 | # load built-in predicates explicitly to setup _statuscallers | ||
|
r28448 | loadpredicate(None, None, predicate) | ||
|
r14681 | # tell hggettext to extract docstrings from these functions: | ||
i18nfunctions = symbols.values() | ||||