fileset.py
672 lines
| 21.1 KiB
| text/x-python
|
PythonLexer
/ mercurial / fileset.py
Matt Mackall
|
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. | ||||
Gregory Szorc
|
r25938 | from __future__ import absolute_import | ||
Yuya Nishihara
|
r38706 | import errno | ||
Augie Fackler
|
r20034 | import re | ||
Gregory Szorc
|
r25938 | |||
from .i18n import _ | ||||
from . import ( | ||||
error, | ||||
Yuya Nishihara
|
r35757 | match as matchmod, | ||
Gregory Szorc
|
r25938 | merge, | ||
parser, | ||||
Pulkit Goyal
|
r32523 | pycompat, | ||
FUJIWARA Katsunori
|
r28448 | registrar, | ||
Pierre-Yves David
|
r31193 | scmutil, | ||
Gregory Szorc
|
r25938 | util, | ||
) | ||||
Yuya Nishihara
|
r37102 | from .utils import ( | ||
stringutil, | ||||
) | ||||
Matt Mackall
|
r14511 | |||
elements = { | ||||
Yuya Nishihara
|
r25815 | # token-type: binding-strength, primary, prefix, infix, suffix | ||
"(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None), | ||||
Yuya Nishihara
|
r35759 | ":": (15, None, None, ("kindpat", 15), None), | ||
Yuya Nishihara
|
r25815 | "-": (5, None, ("negate", 19), ("minus", 5), None), | ||
"not": (10, None, ("not", 10), None, None), | ||||
"!": (10, None, ("not", 10), None, None), | ||||
"and": (5, None, None, ("and", 5), None), | ||||
"&": (5, None, None, ("and", 5), None), | ||||
"or": (4, None, None, ("or", 4), None), | ||||
"|": (4, None, None, ("or", 4), None), | ||||
"+": (4, None, None, ("or", 4), None), | ||||
",": (2, None, None, ("list", 2), None), | ||||
")": (0, None, None, None, None), | ||||
"symbol": (0, "symbol", None, None, None), | ||||
"string": (0, "string", None, None, None), | ||||
"end": (0, None, None, None, None), | ||||
Matt Mackall
|
r14511 | } | ||
Martin von Zweigbergk
|
r32291 | keywords = {'and', 'or', 'not'} | ||
Matt Mackall
|
r14511 | |||
Matt Mackall
|
r19470 | globchars = ".*{}[]?/\\_" | ||
Matt Mackall
|
r14551 | |||
Matt Mackall
|
r14511 | def tokenize(program): | ||
pos, l = 0, len(program) | ||||
Pulkit Goyal
|
r32523 | program = pycompat.bytestr(program) | ||
Matt Mackall
|
r14511 | while pos < l: | ||
c = program[pos] | ||||
if c.isspace(): # skip inter-token whitespace | ||||
pass | ||||
Yuya Nishihara
|
r35759 | elif c in "(),-:|&+!": # handle simple operators | ||
Matt Mackall
|
r14511 | yield (c, None, pos) | ||
elif (c in '"\'' or c == 'r' and | ||||
program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings | ||||
if c == 'r': | ||||
pos += 1 | ||||
c = program[pos] | ||||
decode = lambda x: x | ||||
else: | ||||
Yuya Nishihara
|
r26233 | decode = parser.unescapestr | ||
Matt Mackall
|
r14511 | pos += 1 | ||
s = pos | ||||
while pos < l: # find closing quote | ||||
d = program[pos] | ||||
if d == '\\': # skip over escaped characters | ||||
pos += 2 | ||||
continue | ||||
if d == c: | ||||
yield ('string', decode(program[s:pos]), s) | ||||
break | ||||
pos += 1 | ||||
else: | ||||
raise error.ParseError(_("unterminated string"), s) | ||||
Matt Mackall
|
r14551 | elif c.isalnum() or c in globchars or ord(c) > 127: | ||
Matt Mackall
|
r14513 | # gather up a symbol/keyword | ||
Matt Mackall
|
r14511 | s = pos | ||
pos += 1 | ||||
while pos < l: # find end of symbol | ||||
d = program[pos] | ||||
Matt Mackall
|
r14551 | if not (d.isalnum() or d in globchars or ord(d) > 127): | ||
Matt Mackall
|
r14511 | break | ||
pos += 1 | ||||
sym = program[s:pos] | ||||
if sym in keywords: # operator keywords | ||||
yield (sym, None, s) | ||||
else: | ||||
yield ('symbol', sym, s) | ||||
pos -= 1 | ||||
else: | ||||
raise error.ParseError(_("syntax error"), pos) | ||||
pos += 1 | ||||
yield ('end', None, pos) | ||||
Yuya Nishihara
|
r20208 | def parse(expr): | ||
Yuya Nishihara
|
r25654 | p = parser.parser(elements) | ||
tree, pos = p.parse(tokenize(expr)) | ||||
Yuya Nishihara
|
r25252 | if pos != len(expr): | ||
raise error.ParseError(_("invalid token"), pos) | ||||
return tree | ||||
Matt Mackall
|
r14511 | |||
Yuya Nishihara
|
r35709 | def getsymbol(x): | ||
if x and x[0] == 'symbol': | ||||
return x[1] | ||||
raise error.ParseError(_('not a symbol')) | ||||
Matt Mackall
|
r14551 | def getstring(x, err): | ||
if x and (x[0] == 'string' or x[0] == 'symbol'): | ||||
return x[1] | ||||
raise error.ParseError(err) | ||||
Yuya Nishihara
|
r35759 | def _getkindpat(x, y, allkinds, err): | ||
kind = getsymbol(x) | ||||
pat = getstring(y, err) | ||||
if kind not in allkinds: | ||||
raise error.ParseError(_("invalid pattern kind: %s") % kind) | ||||
return '%s:%s' % (kind, pat) | ||||
def getpattern(x, allkinds, err): | ||||
if x and x[0] == 'kindpat': | ||||
return _getkindpat(x[1], x[2], allkinds, err) | ||||
return getstring(x, err) | ||||
Yuya Nishihara
|
r38617 | def getlist(x): | ||
if not x: | ||||
return [] | ||||
if x[0] == 'list': | ||||
return getlist(x[1]) + [x[2]] | ||||
return [x] | ||||
def getargs(x, min, max, err): | ||||
l = getlist(x) | ||||
if len(l) < min or len(l) > max: | ||||
raise error.ParseError(err) | ||||
return l | ||||
Yuya Nishihara
|
r38711 | def getmatch(mctx, x): | ||
Matt Mackall
|
r14551 | if not x: | ||
raise error.ParseError(_("missing argument")) | ||||
return methods[x[0]](mctx, *x[1:]) | ||||
Yuya Nishihara
|
r38711 | def stringmatch(mctx, x): | ||
return mctx.matcher([x]) | ||||
Matt Mackall
|
r14551 | |||
Yuya Nishihara
|
r38711 | def kindpatmatch(mctx, x, y): | ||
return stringmatch(mctx, _getkindpat(x, y, matchmod.allpatternkinds, | ||||
_("pattern must be a string"))) | ||||
Yuya Nishihara
|
r35759 | |||
Yuya Nishihara
|
r38711 | def andmatch(mctx, x, y): | ||
xm = getmatch(mctx, x) | ||||
ym = getmatch(mctx, y) | ||||
return matchmod.intersectmatchers(xm, ym) | ||||
Matt Mackall
|
r14551 | |||
Yuya Nishihara
|
r38711 | def ormatch(mctx, x, y): | ||
xm = getmatch(mctx, x) | ||||
ym = getmatch(mctx, y) | ||||
return matchmod.unionmatcher([xm, ym]) | ||||
Matt Mackall
|
r14551 | |||
Yuya Nishihara
|
r38711 | def notmatch(mctx, x): | ||
m = getmatch(mctx, x) | ||||
return mctx.predicate(lambda f: not m(f), predrepr=('<not %r>', m)) | ||||
Matt Mackall
|
r14551 | |||
Yuya Nishihara
|
r38711 | def minusmatch(mctx, x, y): | ||
xm = getmatch(mctx, x) | ||||
ym = getmatch(mctx, y) | ||||
return matchmod.differencematcher(xm, ym) | ||||
Patrick Mezard
|
r17363 | |||
Yuya Nishihara
|
r38711 | def negatematch(mctx, x): | ||
Yuya Nishihara
|
r35710 | raise error.ParseError(_("can't use negate operator in this context")) | ||
Yuya Nishihara
|
r38711 | def listmatch(mctx, x, y): | ||
timeless
|
r27518 | raise error.ParseError(_("can't use a list in this context"), | ||
hint=_('see hg help "filesets.x or y"')) | ||||
Matt Mackall
|
r14551 | |||
Yuya Nishihara
|
r38617 | def func(mctx, a, b): | ||
funcname = getsymbol(a) | ||||
if funcname in symbols: | ||||
Yuya Nishihara
|
r38712 | return symbols[funcname](mctx, b) | ||
Yuya Nishihara
|
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) | ||||
FUJIWARA Katsunori
|
r27460 | # symbols are callable like: | ||
# fun(mctx, x) | ||||
# with: | ||||
# mctx - current matchctx instance | ||||
# x - argument in tree form | ||||
symbols = {} | ||||
FUJIWARA Katsunori
|
r27461 | # filesets using matchctx.status() | ||
FUJIWARA Katsunori
|
r27463 | _statuscallers = set() | ||
FUJIWARA Katsunori
|
r27461 | |||
FUJIWARA Katsunori
|
r28448 | predicate = registrar.filesetpredicate() | ||
FUJIWARA Katsunori
|
r27460 | |||
FUJIWARA Katsunori
|
r27461 | @predicate('modified()', callstatus=True) | ||
Matt Mackall
|
r14677 | def modified(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File that is modified according to :hg:`status`. | ||
Matt Mackall
|
r14681 | """ | ||
Wagner Bruna
|
r14785 | # i18n: "modified" is a keyword | ||
Matt Mackall
|
r14677 | getargs(x, 0, 0, _("modified takes no arguments")) | ||
Gregory Szorc
|
r31697 | s = set(mctx.status().modified) | ||
Yuya Nishihara
|
r38711 | return mctx.predicate(s.__contains__, predrepr='modified') | ||
Matt Mackall
|
r14677 | |||
FUJIWARA Katsunori
|
r27461 | @predicate('added()', callstatus=True) | ||
Matt Mackall
|
r14677 | def added(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File that is added according to :hg:`status`. | ||
Matt Mackall
|
r14681 | """ | ||
Wagner Bruna
|
r14785 | # i18n: "added" is a keyword | ||
Matt Mackall
|
r14677 | getargs(x, 0, 0, _("added takes no arguments")) | ||
Gregory Szorc
|
r31697 | s = set(mctx.status().added) | ||
Yuya Nishihara
|
r38711 | return mctx.predicate(s.__contains__, predrepr='added') | ||
Matt Mackall
|
r14677 | |||
FUJIWARA Katsunori
|
r27461 | @predicate('removed()', callstatus=True) | ||
Matt Mackall
|
r14677 | def removed(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File that is removed according to :hg:`status`. | ||
Matt Mackall
|
r14681 | """ | ||
Wagner Bruna
|
r14785 | # i18n: "removed" is a keyword | ||
Matt Mackall
|
r14677 | getargs(x, 0, 0, _("removed takes no arguments")) | ||
Gregory Szorc
|
r31697 | s = set(mctx.status().removed) | ||
Yuya Nishihara
|
r38711 | return mctx.predicate(s.__contains__, predrepr='removed') | ||
Matt Mackall
|
r14677 | |||
FUJIWARA Katsunori
|
r27461 | @predicate('deleted()', callstatus=True) | ||
Matt Mackall
|
r14677 | def deleted(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """Alias for ``missing()``. | ||
Matt Mackall
|
r14681 | """ | ||
Wagner Bruna
|
r14785 | # i18n: "deleted" is a keyword | ||
Matt Mackall
|
r14677 | getargs(x, 0, 0, _("deleted takes no arguments")) | ||
Gregory Szorc
|
r31697 | s = set(mctx.status().deleted) | ||
Yuya Nishihara
|
r38711 | return mctx.predicate(s.__contains__, predrepr='deleted') | ||
Matt Mackall
|
r14677 | |||
FUJIWARA Katsunori
|
r27461 | @predicate('missing()', callstatus=True) | ||
liscju
|
r27024 | def missing(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File that is missing according to :hg:`status`. | ||
liscju
|
r27024 | """ | ||
# i18n: "missing" is a keyword | ||||
getargs(x, 0, 0, _("missing takes no arguments")) | ||||
Gregory Szorc
|
r31697 | s = set(mctx.status().deleted) | ||
Yuya Nishihara
|
r38711 | return mctx.predicate(s.__contains__, predrepr='deleted') | ||
liscju
|
r27024 | |||
FUJIWARA Katsunori
|
r27461 | @predicate('unknown()', callstatus=True) | ||
Matt Mackall
|
r14677 | def unknown(mctx, x): | ||
Yuya Nishihara
|
r38711 | """File that is unknown according to :hg:`status`.""" | ||
Wagner Bruna
|
r14785 | # i18n: "unknown" is a keyword | ||
Matt Mackall
|
r14677 | getargs(x, 0, 0, _("unknown takes no arguments")) | ||
Gregory Szorc
|
r31697 | s = set(mctx.status().unknown) | ||
Yuya Nishihara
|
r38711 | return mctx.predicate(s.__contains__, predrepr='unknown') | ||
Matt Mackall
|
r14677 | |||
FUJIWARA Katsunori
|
r27461 | @predicate('ignored()', callstatus=True) | ||
Matt Mackall
|
r14677 | def ignored(mctx, x): | ||
Yuya Nishihara
|
r38711 | """File that is ignored according to :hg:`status`.""" | ||
Wagner Bruna
|
r14785 | # i18n: "ignored" is a keyword | ||
Matt Mackall
|
r14677 | getargs(x, 0, 0, _("ignored takes no arguments")) | ||
Gregory Szorc
|
r31697 | s = set(mctx.status().ignored) | ||
Yuya Nishihara
|
r38711 | return mctx.predicate(s.__contains__, predrepr='ignored') | ||
Matt Mackall
|
r14677 | |||
FUJIWARA Katsunori
|
r27461 | @predicate('clean()', callstatus=True) | ||
Matt Mackall
|
r14677 | def clean(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File that is clean according to :hg:`status`. | ||
Matt Mackall
|
r14681 | """ | ||
Wagner Bruna
|
r14785 | # i18n: "clean" is a keyword | ||
Matt Mackall
|
r14677 | getargs(x, 0, 0, _("clean takes no arguments")) | ||
Gregory Szorc
|
r31697 | s = set(mctx.status().clean) | ||
Yuya Nishihara
|
r38711 | return mctx.predicate(s.__contains__, predrepr='clean') | ||
Matt Mackall
|
r14677 | |||
Yuya Nishihara
|
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")) | ||||
Yuya Nishihara
|
r38711 | return mctx.predicate(mctx.ctx.__contains__, predrepr='tracked') | ||
Yuya Nishihara
|
r38708 | |||
Yuya Nishihara
|
r38712 | @predicate('binary()') | ||
Matt Mackall
|
r14676 | def binary(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File that appears to be binary (contains NUL bytes). | ||
Matt Mackall
|
r14681 | """ | ||
Wagner Bruna
|
r14785 | # i18n: "binary" is a keyword | ||
Matt Mackall
|
r14676 | getargs(x, 0, 0, _("binary takes no arguments")) | ||
Yuya Nishihara
|
r38711 | return mctx.fpredicate(lambda fctx: fctx.isbinary(), | ||
predrepr='binary', cache=True) | ||||
Matt Mackall
|
r14676 | |||
Yuya Nishihara
|
r38712 | @predicate('exec()') | ||
Matt Mackall
|
r14676 | def exec_(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File that is marked as executable. | ||
Matt Mackall
|
r14681 | """ | ||
Wagner Bruna
|
r14785 | # i18n: "exec" is a keyword | ||
Matt Mackall
|
r14676 | getargs(x, 0, 0, _("exec takes no arguments")) | ||
Yuya Nishihara
|
r38711 | ctx = mctx.ctx | ||
return mctx.predicate(lambda f: ctx.flags(f) == 'x', predrepr='exec') | ||||
Matt Mackall
|
r14676 | |||
Yuya Nishihara
|
r38712 | @predicate('symlink()') | ||
Matt Mackall
|
r14676 | def symlink(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File that is marked as a symlink. | ||
Matt Mackall
|
r14681 | """ | ||
Wagner Bruna
|
r14785 | # i18n: "symlink" is a keyword | ||
Matt Mackall
|
r14676 | getargs(x, 0, 0, _("symlink takes no arguments")) | ||
Yuya Nishihara
|
r38711 | ctx = mctx.ctx | ||
return mctx.predicate(lambda f: ctx.flags(f) == 'l', predrepr='symlink') | ||||
Matt Mackall
|
r14676 | |||
FUJIWARA Katsunori
|
r27460 | @predicate('resolved()') | ||
Matt Mackall
|
r14679 | def resolved(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File that is marked resolved according to :hg:`resolve -l`. | ||
Matt Mackall
|
r14681 | """ | ||
Wagner Bruna
|
r14785 | # i18n: "resolved" is a keyword | ||
Matt Mackall
|
r14679 | getargs(x, 0, 0, _("resolved takes no arguments")) | ||
if mctx.ctx.rev() is not None: | ||||
Yuya Nishihara
|
r38711 | return mctx.never() | ||
Siddharth Agarwal
|
r26995 | ms = merge.mergestate.read(mctx.ctx.repo()) | ||
Yuya Nishihara
|
r38711 | return mctx.predicate(lambda f: f in ms and ms[f] == 'r', | ||
predrepr='resolved') | ||||
Matt Mackall
|
r14679 | |||
FUJIWARA Katsunori
|
r27460 | @predicate('unresolved()') | ||
Matt Mackall
|
r14679 | def unresolved(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File that is marked unresolved according to :hg:`resolve -l`. | ||
Matt Mackall
|
r14681 | """ | ||
Wagner Bruna
|
r14785 | # i18n: "unresolved" is a keyword | ||
Matt Mackall
|
r14679 | getargs(x, 0, 0, _("unresolved takes no arguments")) | ||
if mctx.ctx.rev() is not None: | ||||
Yuya Nishihara
|
r38711 | return mctx.never() | ||
Siddharth Agarwal
|
r26995 | ms = merge.mergestate.read(mctx.ctx.repo()) | ||
Yuya Nishihara
|
r38711 | return mctx.predicate(lambda f: f in ms and ms[f] == 'u', | ||
predrepr='unresolved') | ||||
Matt Mackall
|
r14679 | |||
FUJIWARA Katsunori
|
r27460 | @predicate('hgignore()') | ||
Matt Mackall
|
r14680 | def hgignore(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File that matches the active .hgignore pattern. | ||
Matt Mackall
|
r14681 | """ | ||
FUJIWARA Katsunori
|
r23113 | # i18n: "hgignore" is a keyword | ||
Matt Mackall
|
r14680 | getargs(x, 0, 0, _("hgignore takes no arguments")) | ||
Yuya Nishihara
|
r38711 | return mctx.ctx.repo().dirstate._ignore | ||
Matt Mackall
|
r14680 | |||
FUJIWARA Katsunori
|
r27460 | @predicate('portable()') | ||
Siddharth Agarwal
|
r24408 | def portable(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File that has a portable name. (This doesn't include filenames with case | ||
Siddharth Agarwal
|
r24408 | collisions.) | ||
""" | ||||
# i18n: "portable" is a keyword | ||||
getargs(x, 0, 0, _("portable takes no arguments")) | ||||
Yuya Nishihara
|
r38711 | return mctx.predicate(lambda f: util.checkwinfilename(f) is None, | ||
predrepr='portable') | ||||
Siddharth Agarwal
|
r24408 | |||
Yuya Nishihara
|
r38712 | @predicate('grep(regex)') | ||
Matt Mackall
|
r14682 | def grep(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File contains the given regular expression. | ||
Matt Mackall
|
r14682 | """ | ||
Patrick Mezard
|
r17368 | try: | ||
# i18n: "grep" is a keyword | ||||
r = re.compile(getstring(x, _("grep requires a pattern"))) | ||||
Gregory Szorc
|
r25660 | except re.error as e: | ||
Pulkit Goyal
|
r38094 | raise error.ParseError(_('invalid match pattern: %s') % | ||
stringutil.forcebytestr(e)) | ||||
Yuya Nishihara
|
r38711 | return mctx.fpredicate(lambda fctx: r.search(fctx.data()), | ||
predrepr=('grep(%r)', r.pattern), cache=True) | ||||
Matt Mackall
|
r14682 | |||
Matt Mackall
|
r14683 | def _sizetomax(s): | ||
try: | ||||
r25925 | s = s.strip().lower() | |||
Bryan O'Sullivan
|
r19194 | for k, v in util._sizeunits: | ||
Matt Mackall
|
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: | ||||
Mads Kiilerich
|
r14716 | raise error.ParseError(_("couldn't parse size: %s") % s) | ||
Matt Mackall
|
r14683 | |||
Yuya Nishihara
|
r38709 | def sizematcher(expr): | ||
Matt Harbison
|
r35633 | """Return a function(size) -> bool from the ``size()`` expression""" | ||
Yuya Nishihara
|
r38709 | expr = expr.strip() | ||
Matt Harbison
|
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 | ||||
Yuya Nishihara
|
r36523 | else: | ||
Matt Harbison
|
r35633 | a = util.sizetoint(expr) | ||
b = _sizetomax(expr) | ||||
return lambda x: x >= a and x <= b | ||||
Yuya Nishihara
|
r38712 | @predicate('size(expression)') | ||
Matt Mackall
|
r14683 | def size(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File size matches the given expression. Examples: | ||
Matt Mackall
|
r14683 | |||
timeless
|
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 | ||||
Matt Mackall
|
r14683 | """ | ||
Yuya Nishihara
|
r38709 | # i18n: "size" is a keyword | ||
expr = getstring(x, _("size requires an expression")) | ||||
m = sizematcher(expr) | ||||
Yuya Nishihara
|
r38711 | return mctx.fpredicate(lambda fctx: m(fctx.size()), | ||
predrepr=('size(%r)', expr), cache=True) | ||||
Matt Mackall
|
r14683 | |||
Yuya Nishihara
|
r38712 | @predicate('encoding(name)') | ||
Matt Mackall
|
r14684 | def encoding(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File can be successfully decoded with the given character | ||
Matt Mackall
|
r14684 | encoding. May not be useful for encodings other than ASCII and | ||
UTF-8. | ||||
""" | ||||
Wagner Bruna
|
r14785 | # i18n: "encoding" is a keyword | ||
Matt Mackall
|
r14684 | enc = getstring(x, _("encoding requires an encoding name")) | ||
Yuya Nishihara
|
r38711 | def encp(fctx): | ||
d = fctx.data() | ||||
Matt Mackall
|
r14684 | try: | ||
Yuya Nishihara
|
r38347 | d.decode(pycompat.sysstr(enc)) | ||
Yuya Nishihara
|
r38711 | return True | ||
Matt Mackall
|
r14684 | except LookupError: | ||
Pierre-Yves David
|
r26587 | raise error.Abort(_("unknown encoding '%s'") % enc) | ||
Matt Mackall
|
r14684 | except UnicodeDecodeError: | ||
Yuya Nishihara
|
r38711 | return False | ||
Matt Mackall
|
r14684 | |||
Yuya Nishihara
|
r38711 | return mctx.fpredicate(encp, predrepr=('encoding(%r)', enc), cache=True) | ||
Matt Mackall
|
r14684 | |||
Yuya Nishihara
|
r38712 | @predicate('eol(style)') | ||
Matt Mackall
|
r18842 | def eol(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File contains newlines of the given style (dos, unix, mac). Binary | ||
Matt Mackall
|
r18842 | files are excluded, files with mixed line endings match multiple | ||
styles. | ||||
""" | ||||
Matt Harbison
|
r28056 | # i18n: "eol" is a keyword | ||
enc = getstring(x, _("eol requires a style name")) | ||||
Matt Mackall
|
r18842 | |||
Yuya Nishihara
|
r38711 | def eolp(fctx): | ||
Matt Harbison
|
r38433 | if fctx.isbinary(): | ||
Yuya Nishihara
|
r38711 | return False | ||
Matt Harbison
|
r38433 | d = fctx.data() | ||
Matt Mackall
|
r18842 | if (enc == 'dos' or enc == 'win') and '\r\n' in d: | ||
Yuya Nishihara
|
r38711 | return True | ||
Matt Mackall
|
r18842 | elif enc == 'unix' and re.search('(?<!\r)\n', d): | ||
Yuya Nishihara
|
r38711 | return True | ||
Matt Mackall
|
r18842 | elif enc == 'mac' and re.search('\r(?!\n)', d): | ||
Yuya Nishihara
|
r38711 | return True | ||
return False | ||||
return mctx.fpredicate(eolp, predrepr=('eol(%r)', enc), cache=True) | ||||
Matt Mackall
|
r18842 | |||
FUJIWARA Katsunori
|
r27460 | @predicate('copied()') | ||
Matt Mackall
|
r14685 | def copied(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """File that is recorded as being copied. | ||
Matt Mackall
|
r14685 | """ | ||
Wagner Bruna
|
r14785 | # i18n: "copied" is a keyword | ||
Mads Kiilerich
|
r14718 | getargs(x, 0, 0, _("copied takes no arguments")) | ||
Yuya Nishihara
|
r38711 | def copiedp(fctx): | ||
p = fctx.parents() | ||||
return p and p[0].path() != fctx.path() | ||||
return mctx.fpredicate(copiedp, predrepr='copied', cache=True) | ||||
Matt Mackall
|
r14685 | |||
Pierre-Yves David
|
r31193 | @predicate('revs(revs, pattern)') | ||
def revs(mctx, x): | ||||
Yuya Nishihara
|
r31254 | """Evaluate set in the specified revisions. If the revset match multiple | ||
revs, this will return file matching pattern in any of the revision. | ||||
Pierre-Yves David
|
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]) | ||||
Yuya Nishihara
|
r38711 | matchers = [] | ||
Pierre-Yves David
|
r31193 | for r in revs: | ||
ctx = repo[r] | ||||
Yuya Nishihara
|
r38711 | matchers.append(getmatch(mctx.switch(ctx, _buildstatus(ctx, x)), x)) | ||
if not matchers: | ||||
return mctx.never() | ||||
if len(matchers) == 1: | ||||
return matchers[0] | ||||
return matchmod.unionmatcher(matchers) | ||||
Pierre-Yves David
|
r31193 | |||
Pierre-Yves David
|
r31195 | @predicate('status(base, rev, pattern)') | ||
def status(mctx, x): | ||||
Yuya Nishihara
|
r31254 | """Evaluate predicate using status change between ``base`` and | ||
Pierre-Yves David
|
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) | ||||
Martin von Zweigbergk
|
r37274 | basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec]) | ||
Yuya Nishihara
|
r38711 | return getmatch(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x) | ||
Pierre-Yves David
|
r31195 | |||
FUJIWARA Katsunori
|
r27460 | @predicate('subrepo([pattern])') | ||
Angel Ezquerra
|
r16443 | def subrepo(mctx, x): | ||
FUJIWARA Katsunori
|
r27460 | """Subrepositories whose paths match the given pattern. | ||
Angel Ezquerra
|
r16443 | """ | ||
# i18n: "subrepo" is a keyword | ||||
getargs(x, 0, 1, _("subrepo takes at most one argument")) | ||||
ctx = mctx.ctx | ||||
Yuya Nishihara
|
r38711 | sstate = ctx.substate | ||
Angel Ezquerra
|
r16443 | if x: | ||
Yuya Nishihara
|
r35759 | pat = getpattern(x, matchmod.allpatternkinds, | ||
# i18n: "subrepo" is a keyword | ||||
_("subrepo requires a pattern or no arguments")) | ||||
Angel Ezquerra
|
r16443 | fast = not matchmod.patkind(pat) | ||
if fast: | ||||
def m(s): | ||||
return (s == pat) | ||||
else: | ||||
Matt Harbison
|
r24334 | m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx) | ||
Yuya Nishihara
|
r38711 | return mctx.predicate(lambda f: f in sstate and m(f), | ||
predrepr=('subrepo(%r)', pat)) | ||||
Angel Ezquerra
|
r16443 | else: | ||
Yuya Nishihara
|
r38711 | return mctx.predicate(sstate.__contains__, predrepr='subrepo') | ||
Angel Ezquerra
|
r16443 | |||
Matt Mackall
|
r14551 | methods = { | ||
Yuya Nishihara
|
r38711 | 'string': stringmatch, | ||
'symbol': stringmatch, | ||||
'kindpat': kindpatmatch, | ||||
'and': andmatch, | ||||
'or': ormatch, | ||||
'minus': minusmatch, | ||||
'negate': negatematch, | ||||
'list': listmatch, | ||||
'group': getmatch, | ||||
'not': notmatch, | ||||
Matt Mackall
|
r14676 | 'func': func, | ||
Matt Mackall
|
r14551 | } | ||
class matchctx(object): | ||||
Yuya Nishihara
|
r38713 | def __init__(self, ctx, status=None, badfn=None): | ||
Matt Mackall
|
r14551 | self.ctx = ctx | ||
Matt Mackall
|
r14677 | self._status = status | ||
Yuya Nishihara
|
r38632 | self._badfn = badfn | ||
Yuya Nishihara
|
r38712 | |||
Matt Mackall
|
r14677 | def status(self): | ||
return self._status | ||||
Yuya Nishihara
|
r38706 | |||
Matt Mackall
|
r14673 | def matcher(self, patterns): | ||
Yuya Nishihara
|
r38632 | return self.ctx.match(patterns, badfn=self._badfn) | ||
Yuya Nishihara
|
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: | ||||
Yuya Nishihara
|
r38781 | # open()-ing a directory fails with EACCES on Windows | ||
if e.errno in (errno.ENOENT, errno.EACCES, errno.ENOTDIR, | ||||
errno.EISDIR): | ||||
Yuya Nishihara
|
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) | ||||
Yuya Nishihara
|
r31192 | def switch(self, ctx, status=None): | ||
Yuya Nishihara
|
r38713 | return matchctx(ctx, status, self._badfn) | ||
Matt Mackall
|
r14551 | |||
Yuya Nishihara
|
r31192 | # filesets using matchctx.switch() | ||
_switchcallers = [ | ||||
Pierre-Yves David
|
r31193 | 'revs', | ||
Pierre-Yves David
|
r31195 | 'status', | ||
Yuya Nishihara
|
r31192 | ] | ||
Yuya Nishihara
|
r31188 | |||
Matt Mackall
|
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 | ||||
Yuya Nishihara
|
r31192 | if tree[1][1] in _switchcallers: | ||
# arguments won't be evaluated in the current context | ||||
return False | ||||
Matt Mackall
|
r14678 | for s in tree[1:]: | ||
if _intree(funcs, s): | ||||
return True | ||||
return False | ||||
Yuya Nishihara
|
r38631 | def match(ctx, expr, badfn=None): | ||
"""Create a matcher for a single fileset expression""" | ||||
Yuya Nishihara
|
r25252 | tree = parse(expr) | ||
Yuya Nishihara
|
r38714 | mctx = matchctx(ctx, _buildstatus(ctx, tree), badfn=badfn) | ||
Yuya Nishihara
|
r38711 | return getmatch(mctx, tree) | ||
Matt Mackall
|
r14678 | |||
Pierre-Yves David
|
r31194 | def _buildstatus(ctx, tree, basectx=None): | ||
Matt Mackall
|
r14678 | # do we need status info? | ||
Pierre-Yves David
|
r31194 | |||
Yuya Nishihara
|
r38712 | if _intree(_statuscallers, tree): | ||
Matt Mackall
|
r14678 | unknown = _intree(['unknown'], tree) | ||
ignored = _intree(['ignored'], tree) | ||||
Pierre-Yves David
|
r31194 | if basectx is None: | ||
basectx = ctx.p1() | ||||
Martin von Zweigbergk
|
r38795 | return basectx.status(ctx, listunknown=unknown, listignored=ignored, | ||
listclean=True) | ||||
Matt Mackall
|
r14678 | else: | ||
Yuya Nishihara
|
r31191 | return None | ||
Matt Mackall
|
r14681 | |||
Yuya Nishihara
|
r25255 | def prettyformat(tree): | ||
return parser.prettyformat(tree, ('string', 'symbol')) | ||||
FUJIWARA Katsunori
|
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) | ||||
Yuya Nishihara
|
r38712 | # load built-in predicates explicitly to setup _statuscallers | ||
FUJIWARA Katsunori
|
r28448 | loadpredicate(None, None, predicate) | ||
Matt Mackall
|
r14681 | # tell hggettext to extract docstrings from these functions: | ||
i18nfunctions = symbols.values() | ||||