fileset.py
638 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 _ | ||||
|
r43359 | from .pycompat import getattr | ||
|
r25938 | from . import ( | ||
error, | ||||
|
r38841 | filesetlang, | ||
|
r35757 | match as matchmod, | ||
|
r25938 | merge, | ||
|
r32523 | pycompat, | ||
|
r28448 | registrar, | ||
|
r31193 | scmutil, | ||
|
r25938 | util, | ||
) | ||||
|
r43346 | 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 | |||
|
r43346 | |||
|
r38711 | def getmatch(mctx, x): | ||
|
r14551 | if not x: | ||
|
r43347 | raise error.ParseError(_(b"missing argument")) | ||
|
r14551 | return methods[x[0]](mctx, *x[1:]) | ||
|
r43346 | |||
|
r38915 | def getmatchwithstatus(mctx, x, hint): | ||
|
r43347 | keys = set(getstring(hint, b'status hint must be a string').split()) | ||
|
r38916 | return getmatch(mctx.withstatus(keys), x) | ||
|
r38915 | |||
|
r43346 | |||
|
r38711 | def stringmatch(mctx, x): | ||
return mctx.matcher([x]) | ||||
|
r14551 | |||
|
r43346 | |||
|
r38711 | def kindpatmatch(mctx, x, y): | ||
|
r43346 | return stringmatch( | ||
mctx, | ||||
_getkindpat( | ||||
|
r43347 | x, y, matchmod.allpatternkinds, _(b"pattern must be a string") | ||
|
r43346 | ), | ||
) | ||||
|
r35759 | |||
|
r38901 | def patternsmatch(mctx, *xs): | ||
allkinds = matchmod.allpatternkinds | ||||
|
r43346 | patterns = [ | ||
|
r43347 | getpattern(x, allkinds, _(b"pattern must be a string")) for x in xs | ||
|
r43346 | ] | ||
|
r38901 | return mctx.matcher(patterns) | ||
|
r43346 | |||
|
r38711 | def andmatch(mctx, x, y): | ||
xm = getmatch(mctx, x) | ||||
|
r38918 | ym = getmatch(mctx.narrowed(xm), y) | ||
|
r38711 | return matchmod.intersectmatchers(xm, ym) | ||
|
r14551 | |||
|
r43346 | |||
|
r38840 | def ormatch(mctx, *xs): | ||
ms = [getmatch(mctx, x) for x in xs] | ||||
return matchmod.unionmatcher(ms) | ||||
|
r14551 | |||
|
r43346 | |||
|
r38711 | def notmatch(mctx, x): | ||
m = getmatch(mctx, x) | ||||
|
r43347 | return mctx.predicate(lambda f: not m(f), predrepr=(b'<not %r>', m)) | ||
|
r14551 | |||
|
r43346 | |||
|
r38711 | def minusmatch(mctx, x, y): | ||
xm = getmatch(mctx, x) | ||||
|
r38918 | ym = getmatch(mctx.narrowed(xm), y) | ||
|
r38711 | return matchmod.differencematcher(xm, ym) | ||
|
r17363 | |||
|
r43346 | |||
|
r38839 | def listmatch(mctx, *xs): | ||
|
r43346 | raise error.ParseError( | ||
|
r43347 | _(b"can't use a list in this context"), | ||
hint=_(b'see \'hg help "filesets.x or y"\''), | ||||
|
r43346 | ) | ||
|
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) | ||||
|
r43346 | |||
|
r27460 | # symbols are callable like: | ||
# fun(mctx, x) | ||||
# with: | ||||
# mctx - current matchctx instance | ||||
# x - argument in tree form | ||||
|
r38841 | symbols = filesetlang.symbols | ||
|
r27460 | |||
|
r38963 | predicate = registrar.filesetpredicate(symbols) | ||
|
r27460 | |||
|
r43346 | |||
|
r43347 | @predicate(b'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 | ||
|
r43347 | getargs(x, 0, 0, _(b"modified takes no arguments")) | ||
|
r31697 | s = set(mctx.status().modified) | ||
|
r43347 | return mctx.predicate(s.__contains__, predrepr=b'modified') | ||
|
r14677 | |||
|
r43346 | |||
|
r43347 | @predicate(b'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 | ||
|
r43347 | getargs(x, 0, 0, _(b"added takes no arguments")) | ||
|
r31697 | s = set(mctx.status().added) | ||
|
r43347 | return mctx.predicate(s.__contains__, predrepr=b'added') | ||
|
r14677 | |||
|
r43346 | |||
|
r43347 | @predicate(b'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 | ||
|
r43347 | getargs(x, 0, 0, _(b"removed takes no arguments")) | ||
|
r31697 | s = set(mctx.status().removed) | ||
|
r43347 | return mctx.predicate(s.__contains__, predrepr=b'removed') | ||
|
r14677 | |||
|
r43346 | |||
|
r43347 | @predicate(b'deleted()', callstatus=True, weight=_WEIGHT_STATUS) | ||
|
r14677 | def deleted(mctx, x): | ||
|
r27460 | """Alias for ``missing()``. | ||
|
r14681 | """ | ||
|
r14785 | # i18n: "deleted" is a keyword | ||
|
r43347 | getargs(x, 0, 0, _(b"deleted takes no arguments")) | ||
|
r31697 | s = set(mctx.status().deleted) | ||
|
r43347 | return mctx.predicate(s.__contains__, predrepr=b'deleted') | ||
|
r14677 | |||
|
r43346 | |||
|
r43347 | @predicate(b'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 | ||||
|
r43347 | getargs(x, 0, 0, _(b"missing takes no arguments")) | ||
|
r31697 | s = set(mctx.status().deleted) | ||
|
r43347 | return mctx.predicate(s.__contains__, predrepr=b'deleted') | ||
|
r27024 | |||
|
r43346 | |||
|
r43347 | @predicate(b'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 | ||
|
r43347 | getargs(x, 0, 0, _(b"unknown takes no arguments")) | ||
|
r31697 | s = set(mctx.status().unknown) | ||
|
r43347 | return mctx.predicate(s.__contains__, predrepr=b'unknown') | ||
|
r14677 | |||
|
r43346 | |||
|
r43347 | @predicate(b'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 | ||
|
r43347 | getargs(x, 0, 0, _(b"ignored takes no arguments")) | ||
|
r31697 | s = set(mctx.status().ignored) | ||
|
r43347 | return mctx.predicate(s.__contains__, predrepr=b'ignored') | ||
|
r14677 | |||
|
r43346 | |||
|
r43347 | @predicate(b'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 | ||
|
r43347 | getargs(x, 0, 0, _(b"clean takes no arguments")) | ||
|
r31697 | s = set(mctx.status().clean) | ||
|
r43347 | return mctx.predicate(s.__contains__, predrepr=b'clean') | ||
|
r14677 | |||
|
r43346 | |||
|
r43347 | @predicate(b'tracked()') | ||
|
r38708 | def tracked(mctx, x): | ||
"""File that is under Mercurial control.""" | ||||
# i18n: "tracked" is a keyword | ||||
|
r43347 | getargs(x, 0, 0, _(b"tracked takes no arguments")) | ||
return mctx.predicate(mctx.ctx.__contains__, predrepr=b'tracked') | ||||
|
r38708 | |||
|
r43346 | |||
|
r43347 | @predicate(b'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 | ||
|
r43347 | getargs(x, 0, 0, _(b"binary takes no arguments")) | ||
|
r43346 | return mctx.fpredicate( | ||
|
r43347 | lambda fctx: fctx.isbinary(), predrepr=b'binary', cache=True | ||
|
r43346 | ) | ||
|
r14676 | |||
|
r43347 | @predicate(b'exec()') | ||
|
r14676 | def exec_(mctx, x): | ||
|
r27460 | """File that is marked as executable. | ||
|
r14681 | """ | ||
|
r14785 | # i18n: "exec" is a keyword | ||
|
r43347 | getargs(x, 0, 0, _(b"exec takes no arguments")) | ||
|
r38711 | ctx = mctx.ctx | ||
|
r43347 | return mctx.predicate(lambda f: ctx.flags(f) == b'x', predrepr=b'exec') | ||
|
r14676 | |||
|
r43346 | |||
|
r43347 | @predicate(b'symlink()') | ||
|
r14676 | def symlink(mctx, x): | ||
|
r27460 | """File that is marked as a symlink. | ||
|
r14681 | """ | ||
|
r14785 | # i18n: "symlink" is a keyword | ||
|
r43347 | getargs(x, 0, 0, _(b"symlink takes no arguments")) | ||
|
r38711 | ctx = mctx.ctx | ||
|
r43347 | return mctx.predicate(lambda f: ctx.flags(f) == b'l', predrepr=b'symlink') | ||
|
r14676 | |||
|
r43346 | |||
|
r43347 | @predicate(b'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 | ||
|
r43347 | getargs(x, 0, 0, _(b"resolved takes no arguments")) | ||
|
r14679 | if mctx.ctx.rev() is not None: | ||
|
r38711 | return mctx.never() | ||
|
r26995 | ms = merge.mergestate.read(mctx.ctx.repo()) | ||
|
r43346 | return mctx.predicate( | ||
|
r43347 | lambda f: f in ms and ms[f] == b'r', predrepr=b'resolved' | ||
|
r43346 | ) | ||
|
r14679 | |||
|
r43347 | @predicate(b'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 | ||
|
r43347 | getargs(x, 0, 0, _(b"unresolved takes no arguments")) | ||
|
r14679 | if mctx.ctx.rev() is not None: | ||
|
r38711 | return mctx.never() | ||
|
r26995 | ms = merge.mergestate.read(mctx.ctx.repo()) | ||
|
r43346 | return mctx.predicate( | ||
|
r43347 | lambda f: f in ms and ms[f] == b'u', predrepr=b'unresolved' | ||
|
r43346 | ) | ||
|
r14679 | |||
|
r43347 | @predicate(b'hgignore()', weight=_WEIGHT_STATUS) | ||
|
r14680 | def hgignore(mctx, x): | ||
|
r27460 | """File that matches the active .hgignore pattern. | ||
|
r14681 | """ | ||
|
r23113 | # i18n: "hgignore" is a keyword | ||
|
r43347 | getargs(x, 0, 0, _(b"hgignore takes no arguments")) | ||
|
r38711 | return mctx.ctx.repo().dirstate._ignore | ||
|
r14680 | |||
|
r43346 | |||
|
r43347 | @predicate(b'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 | ||||
|
r43347 | getargs(x, 0, 0, _(b"portable takes no arguments")) | ||
|
r43346 | return mctx.predicate( | ||
|
r43347 | lambda f: util.checkwinfilename(f) is None, predrepr=b'portable' | ||
|
r43346 | ) | ||
|
r24408 | |||
|
r43347 | @predicate(b'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 | ||||
|
r43347 | r = re.compile(getstring(x, _(b"grep requires a pattern"))) | ||
|
r25660 | except re.error as e: | ||
|
r43346 | raise error.ParseError( | ||
|
r43347 | _(b'invalid match pattern: %s') % stringutil.forcebytestr(e) | ||
|
r43346 | ) | ||
return mctx.fpredicate( | ||||
lambda fctx: r.search(fctx.data()), | ||||
|
r43347 | predrepr=(b'grep(%r)', r.pattern), | ||
|
r43346 | 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 | ||||
|
r43346 | n = s[: -len(k)] | ||
|
r14683 | inc = 1.0 | ||
|
r43347 | if b"." in n: | ||
inc /= 10 ** len(n.split(b".")[1]) | ||||
|
r14683 | return int((float(n) + inc) * v) - 1 | ||
# no extension, this is a precise value | ||||
return int(s) | ||||
except ValueError: | ||||
|
r43347 | raise error.ParseError(_(b"couldn't parse size: %s") % s) | ||
|
r14683 | |||
|
r43346 | |||
|
r38709 | def sizematcher(expr): | ||
|
r35633 | """Return a function(size) -> bool from the ``size()`` expression""" | ||
|
r38709 | expr = expr.strip() | ||
|
r43347 | if b'-' in expr: # do we have a range? | ||
a, b = expr.split(b'-', 1) | ||||
|
r35633 | a = util.sizetoint(a) | ||
b = util.sizetoint(b) | ||||
return lambda x: x >= a and x <= b | ||||
|
r43347 | elif expr.startswith(b"<="): | ||
|
r35633 | a = util.sizetoint(expr[2:]) | ||
return lambda x: x <= a | ||||
|
r43347 | elif expr.startswith(b"<"): | ||
|
r35633 | a = util.sizetoint(expr[1:]) | ||
return lambda x: x < a | ||||
|
r43347 | elif expr.startswith(b">="): | ||
|
r35633 | a = util.sizetoint(expr[2:]) | ||
return lambda x: x >= a | ||||
|
r43347 | elif expr.startswith(b">"): | ||
|
r35633 | 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 | ||||
|
r43346 | |||
|
r43347 | @predicate(b'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 | ||
|
r43347 | expr = getstring(x, _(b"size requires an expression")) | ||
|
r38709 | m = sizematcher(expr) | ||
|
r43346 | return mctx.fpredicate( | ||
|
r43347 | lambda fctx: m(fctx.size()), predrepr=(b'size(%r)', expr), cache=True | ||
|
r43346 | ) | ||
|
r14683 | |||
|
r43347 | @predicate(b'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 | ||
|
r43347 | enc = getstring(x, _(b"encoding requires an encoding name")) | ||
|
r14684 | |||
|
r38711 | def encp(fctx): | ||
d = fctx.data() | ||||
|
r14684 | try: | ||
|
r38347 | d.decode(pycompat.sysstr(enc)) | ||
|
r38711 | return True | ||
|
r14684 | except LookupError: | ||
|
r43347 | raise error.Abort(_(b"unknown encoding '%s'") % enc) | ||
|
r14684 | except UnicodeDecodeError: | ||
|
r38711 | return False | ||
|
r14684 | |||
|
r43347 | return mctx.fpredicate(encp, predrepr=(b'encoding(%r)', enc), cache=True) | ||
|
r14684 | |||
|
r43346 | |||
|
r43347 | @predicate(b'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 | ||
|
r43347 | enc = getstring(x, _(b"eol requires a style name")) | ||
|
r18842 | |||
|
r38711 | def eolp(fctx): | ||
|
r38433 | if fctx.isbinary(): | ||
|
r38711 | return False | ||
|
r38433 | d = fctx.data() | ||
|
r43347 | if (enc == b'dos' or enc == b'win') and b'\r\n' in d: | ||
|
r38711 | return True | ||
|
r43347 | elif enc == b'unix' and re.search(b'(?<!\r)\n', d): | ||
|
r38711 | return True | ||
|
r43347 | elif enc == b'mac' and re.search(b'\r(?!\n)', d): | ||
|
r38711 | return True | ||
return False | ||||
|
r43346 | |||
|
r43347 | return mctx.fpredicate(eolp, predrepr=(b'eol(%r)', enc), cache=True) | ||
|
r18842 | |||
|
r43346 | |||
|
r43347 | @predicate(b'copied()') | ||
|
r14685 | def copied(mctx, x): | ||
|
r27460 | """File that is recorded as being copied. | ||
|
r14685 | """ | ||
|
r14785 | # i18n: "copied" is a keyword | ||
|
r43347 | getargs(x, 0, 0, _(b"copied takes no arguments")) | ||
|
r43346 | |||
|
r38711 | def copiedp(fctx): | ||
p = fctx.parents() | ||||
return p and p[0].path() != fctx.path() | ||||
|
r43346 | |||
|
r43347 | return mctx.fpredicate(copiedp, predrepr=b'copied', cache=True) | ||
|
r14685 | |||
|
r43346 | |||
|
r43347 | @predicate(b'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 | ||||
|
r43347 | r, x = getargs(x, 2, 2, _(b"revs takes two arguments")) | ||
|
r31193 | # i18n: "revs" is a keyword | ||
|
r43347 | revspec = getstring(r, _(b"first argument to revs must be a revision")) | ||
|
r31193 | repo = mctx.ctx.repo() | ||
revs = scmutil.revrange(repo, [revspec]) | ||||
|
r38711 | matchers = [] | ||
|
r31193 | for r in revs: | ||
ctx = repo[r] | ||||
|
r38914 | mc = mctx.switch(ctx.p1(), ctx) | ||
|
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 | |||
|
r43346 | |||
|
r43347 | @predicate(b'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 | ||||
|
r43347 | b, r, x = getargs(x, 3, 3, _(b"status takes three arguments")) | ||
|
r31195 | # i18n: "status" is a keyword | ||
|
r43347 | baseerr = _(b"first argument to status must be a revision") | ||
|
r31195 | baserevspec = getstring(b, baseerr) | ||
if not baserevspec: | ||||
raise error.ParseError(baseerr) | ||||
|
r43347 | reverr = _(b"second argument to status must be a revision") | ||
|
r31195 | revspec = getstring(r, reverr) | ||
if not revspec: | ||||
raise error.ParseError(reverr) | ||||
|
r37274 | basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec]) | ||
|
r38914 | mc = mctx.switch(basectx, ctx) | ||
|
r38912 | return getmatch(mc, x) | ||
|
r31195 | |||
|
r43346 | |||
|
r43347 | @predicate(b'subrepo([pattern])') | ||
|
r16443 | def subrepo(mctx, x): | ||
|
r27460 | """Subrepositories whose paths match the given pattern. | ||
|
r16443 | """ | ||
# i18n: "subrepo" is a keyword | ||||
|
r43347 | getargs(x, 0, 1, _(b"subrepo takes at most one argument")) | ||
|
r16443 | ctx = mctx.ctx | ||
|
r38711 | sstate = ctx.substate | ||
|
r16443 | if x: | ||
|
r43346 | pat = getpattern( | ||
x, | ||||
matchmod.allpatternkinds, | ||||
# i18n: "subrepo" is a keyword | ||||
|
r43347 | _(b"subrepo requires a pattern or no arguments"), | ||
|
r43346 | ) | ||
|
r16443 | fast = not matchmod.patkind(pat) | ||
if fast: | ||||
|
r43346 | |||
|
r16443 | def m(s): | ||
|
r43346 | return s == pat | ||
|
r16443 | else: | ||
|
r43347 | m = matchmod.match(ctx.repo().root, b'', [pat], ctx=ctx) | ||
|
r43346 | return mctx.predicate( | ||
|
r43347 | lambda f: f in sstate and m(f), predrepr=(b'subrepo(%r)', pat) | ||
|
r43346 | ) | ||
|
r16443 | else: | ||
|
r43347 | return mctx.predicate(sstate.__contains__, predrepr=b'subrepo') | ||
|
r16443 | |||
|
r43346 | |||
|
r14551 | methods = { | ||
|
r43347 | b'withstatus': getmatchwithstatus, | ||
b'string': stringmatch, | ||||
b'symbol': stringmatch, | ||||
b'kindpat': kindpatmatch, | ||||
b'patterns': patternsmatch, | ||||
b'and': andmatch, | ||||
b'or': ormatch, | ||||
b'minus': minusmatch, | ||||
b'list': listmatch, | ||||
b'not': notmatch, | ||||
b'func': func, | ||||
|
r14551 | } | ||
|
r43346 | |||
|
r14551 | class matchctx(object): | ||
|
r44461 | def __init__(self, basectx, ctx, cwd, badfn=None): | ||
|
r38913 | self._basectx = basectx | ||
|
r14551 | self.ctx = ctx | ||
|
r38632 | self._badfn = badfn | ||
|
r38918 | self._match = None | ||
|
r38914 | self._status = None | ||
|
r44461 | self.cwd = cwd | ||
|
r38914 | |||
|
r38918 | def narrowed(self, match): | ||
"""Create matchctx for a sub-tree narrowed by the given matcher""" | ||||
|
r44461 | mctx = matchctx(self._basectx, self.ctx, self.cwd, self._badfn) | ||
|
r38918 | mctx._match = match | ||
# leave wider status which we don't have to care | ||||
mctx._status = self._status | ||||
return mctx | ||||
|
r38917 | def switch(self, basectx, ctx): | ||
|
r44461 | mctx = matchctx(basectx, ctx, self.cwd, self._badfn) | ||
|
r38918 | mctx._match = self._match | ||
return mctx | ||||
|
r38917 | |||
|
r38916 | def withstatus(self, keys): | ||
"""Create matchctx which has precomputed status specified by the keys""" | ||||
|
r44461 | mctx = matchctx(self._basectx, self.ctx, self.cwd, self._badfn) | ||
|
r38918 | mctx._match = self._match | ||
|
r38916 | mctx._buildstatus(keys) | ||
return mctx | ||||
def _buildstatus(self, keys): | ||||
|
r43346 | self._status = self._basectx.status( | ||
self.ctx, | ||||
self._match, | ||||
|
r43347 | listignored=b'ignored' in keys, | ||
listclean=b'clean' in keys, | ||||
listunknown=b'unknown' in keys, | ||||
|
r43346 | ) | ||
|
r38712 | |||
|
r14677 | def status(self): | ||
return self._status | ||||
|
r38706 | |||
|
r14673 | def matcher(self, patterns): | ||
|
r44461 | return self.ctx.match(patterns, badfn=self._badfn, cwd=self.cwd) | ||
|
r38706 | |||
def predicate(self, predfn, predrepr=None, cache=False): | ||||
"""Create a matcher to select files by predfn(filename)""" | ||||
if cache: | ||||
predfn = util.cachefunc(predfn) | ||||
|
r43346 | return matchmod.predicatematcher( | ||
predfn, predrepr=predrepr, badfn=self._badfn | ||||
) | ||||
|
r38706 | |||
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: | ||||
|
r43346 | |||
|
r38706 | 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 | ||
|
r43346 | if e.errno in ( | ||
errno.ENOENT, | ||||
errno.EACCES, | ||||
errno.ENOTDIR, | ||||
errno.EISDIR, | ||||
): | ||||
|
r38706 | return False | ||
raise | ||||
|
r43346 | |||
|
r38706 | else: | ||
|
r43346 | |||
|
r38706 | def fctxpredfn(f): | ||
try: | ||||
fctx = ctx[f] | ||||
except error.LookupError: | ||||
return False | ||||
return predfn(fctx) | ||||
|
r43346 | |||
|
r38706 | return self.predicate(fctxpredfn, predrepr=predrepr, cache=cache) | ||
def never(self): | ||||
"""Create a matcher to select nothing""" | ||||
|
r41825 | return matchmod.never(badfn=self._badfn) | ||
|
r38706 | |||
|
r43346 | |||
|
r44461 | def match(ctx, cwd, expr, badfn=None): | ||
|
r38631 | """Create a matcher for a single fileset expression""" | ||
|
r38841 | tree = filesetlang.parse(expr) | ||
|
r38862 | tree = filesetlang.analyze(tree) | ||
|
r38865 | tree = filesetlang.optimize(tree) | ||
|
r44461 | mctx = matchctx(ctx.p1(), ctx, cwd, badfn=badfn) | ||
|
r38711 | return getmatch(mctx, tree) | ||
|
r14678 | |||
|
r14681 | |||
|
r28447 | def loadpredicate(ui, extname, registrarobj): | ||
"""Load fileset predicates from specified registrarobj | ||||
""" | ||||
|
r43376 | for name, func in pycompat.iteritems(registrarobj._table): | ||
|
r28447 | symbols[name] = func | ||
|
r43346 | |||
|
r14681 | # tell hggettext to extract docstrings from these functions: | ||
i18nfunctions = symbols.values() | ||||