mercurial/fileset.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43089 c59eb1560c44
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
    37 getargs = filesetlang.getargs
    37 getargs = filesetlang.getargs
    38 
    38 
    39 
    39 
    40 def getmatch(mctx, x):
    40 def getmatch(mctx, x):
    41     if not x:
    41     if not x:
    42         raise error.ParseError(_("missing argument"))
    42         raise error.ParseError(_(b"missing argument"))
    43     return methods[x[0]](mctx, *x[1:])
    43     return methods[x[0]](mctx, *x[1:])
    44 
    44 
    45 
    45 
    46 def getmatchwithstatus(mctx, x, hint):
    46 def getmatchwithstatus(mctx, x, hint):
    47     keys = set(getstring(hint, 'status hint must be a string').split())
    47     keys = set(getstring(hint, b'status hint must be a string').split())
    48     return getmatch(mctx.withstatus(keys), x)
    48     return getmatch(mctx.withstatus(keys), x)
    49 
    49 
    50 
    50 
    51 def stringmatch(mctx, x):
    51 def stringmatch(mctx, x):
    52     return mctx.matcher([x])
    52     return mctx.matcher([x])
    54 
    54 
    55 def kindpatmatch(mctx, x, y):
    55 def kindpatmatch(mctx, x, y):
    56     return stringmatch(
    56     return stringmatch(
    57         mctx,
    57         mctx,
    58         _getkindpat(
    58         _getkindpat(
    59             x, y, matchmod.allpatternkinds, _("pattern must be a string")
    59             x, y, matchmod.allpatternkinds, _(b"pattern must be a string")
    60         ),
    60         ),
    61     )
    61     )
    62 
    62 
    63 
    63 
    64 def patternsmatch(mctx, *xs):
    64 def patternsmatch(mctx, *xs):
    65     allkinds = matchmod.allpatternkinds
    65     allkinds = matchmod.allpatternkinds
    66     patterns = [
    66     patterns = [
    67         getpattern(x, allkinds, _("pattern must be a string")) for x in xs
    67         getpattern(x, allkinds, _(b"pattern must be a string")) for x in xs
    68     ]
    68     ]
    69     return mctx.matcher(patterns)
    69     return mctx.matcher(patterns)
    70 
    70 
    71 
    71 
    72 def andmatch(mctx, x, y):
    72 def andmatch(mctx, x, y):
    80     return matchmod.unionmatcher(ms)
    80     return matchmod.unionmatcher(ms)
    81 
    81 
    82 
    82 
    83 def notmatch(mctx, x):
    83 def notmatch(mctx, x):
    84     m = getmatch(mctx, x)
    84     m = getmatch(mctx, x)
    85     return mctx.predicate(lambda f: not m(f), predrepr=('<not %r>', m))
    85     return mctx.predicate(lambda f: not m(f), predrepr=(b'<not %r>', m))
    86 
    86 
    87 
    87 
    88 def minusmatch(mctx, x, y):
    88 def minusmatch(mctx, x, y):
    89     xm = getmatch(mctx, x)
    89     xm = getmatch(mctx, x)
    90     ym = getmatch(mctx.narrowed(xm), y)
    90     ym = getmatch(mctx.narrowed(xm), y)
    91     return matchmod.differencematcher(xm, ym)
    91     return matchmod.differencematcher(xm, ym)
    92 
    92 
    93 
    93 
    94 def listmatch(mctx, *xs):
    94 def listmatch(mctx, *xs):
    95     raise error.ParseError(
    95     raise error.ParseError(
    96         _("can't use a list in this context"),
    96         _(b"can't use a list in this context"),
    97         hint=_('see \'hg help "filesets.x or y"\''),
    97         hint=_(b'see \'hg help "filesets.x or y"\''),
    98     )
    98     )
    99 
    99 
   100 
   100 
   101 def func(mctx, a, b):
   101 def func(mctx, a, b):
   102     funcname = getsymbol(a)
   102     funcname = getsymbol(a)
   117 symbols = filesetlang.symbols
   117 symbols = filesetlang.symbols
   118 
   118 
   119 predicate = registrar.filesetpredicate(symbols)
   119 predicate = registrar.filesetpredicate(symbols)
   120 
   120 
   121 
   121 
   122 @predicate('modified()', callstatus=True, weight=_WEIGHT_STATUS)
   122 @predicate(b'modified()', callstatus=True, weight=_WEIGHT_STATUS)
   123 def modified(mctx, x):
   123 def modified(mctx, x):
   124     """File that is modified according to :hg:`status`.
   124     """File that is modified according to :hg:`status`.
   125     """
   125     """
   126     # i18n: "modified" is a keyword
   126     # i18n: "modified" is a keyword
   127     getargs(x, 0, 0, _("modified takes no arguments"))
   127     getargs(x, 0, 0, _(b"modified takes no arguments"))
   128     s = set(mctx.status().modified)
   128     s = set(mctx.status().modified)
   129     return mctx.predicate(s.__contains__, predrepr='modified')
   129     return mctx.predicate(s.__contains__, predrepr=b'modified')
   130 
   130 
   131 
   131 
   132 @predicate('added()', callstatus=True, weight=_WEIGHT_STATUS)
   132 @predicate(b'added()', callstatus=True, weight=_WEIGHT_STATUS)
   133 def added(mctx, x):
   133 def added(mctx, x):
   134     """File that is added according to :hg:`status`.
   134     """File that is added according to :hg:`status`.
   135     """
   135     """
   136     # i18n: "added" is a keyword
   136     # i18n: "added" is a keyword
   137     getargs(x, 0, 0, _("added takes no arguments"))
   137     getargs(x, 0, 0, _(b"added takes no arguments"))
   138     s = set(mctx.status().added)
   138     s = set(mctx.status().added)
   139     return mctx.predicate(s.__contains__, predrepr='added')
   139     return mctx.predicate(s.__contains__, predrepr=b'added')
   140 
   140 
   141 
   141 
   142 @predicate('removed()', callstatus=True, weight=_WEIGHT_STATUS)
   142 @predicate(b'removed()', callstatus=True, weight=_WEIGHT_STATUS)
   143 def removed(mctx, x):
   143 def removed(mctx, x):
   144     """File that is removed according to :hg:`status`.
   144     """File that is removed according to :hg:`status`.
   145     """
   145     """
   146     # i18n: "removed" is a keyword
   146     # i18n: "removed" is a keyword
   147     getargs(x, 0, 0, _("removed takes no arguments"))
   147     getargs(x, 0, 0, _(b"removed takes no arguments"))
   148     s = set(mctx.status().removed)
   148     s = set(mctx.status().removed)
   149     return mctx.predicate(s.__contains__, predrepr='removed')
   149     return mctx.predicate(s.__contains__, predrepr=b'removed')
   150 
   150 
   151 
   151 
   152 @predicate('deleted()', callstatus=True, weight=_WEIGHT_STATUS)
   152 @predicate(b'deleted()', callstatus=True, weight=_WEIGHT_STATUS)
   153 def deleted(mctx, x):
   153 def deleted(mctx, x):
   154     """Alias for ``missing()``.
   154     """Alias for ``missing()``.
   155     """
   155     """
   156     # i18n: "deleted" is a keyword
   156     # i18n: "deleted" is a keyword
   157     getargs(x, 0, 0, _("deleted takes no arguments"))
   157     getargs(x, 0, 0, _(b"deleted takes no arguments"))
   158     s = set(mctx.status().deleted)
   158     s = set(mctx.status().deleted)
   159     return mctx.predicate(s.__contains__, predrepr='deleted')
   159     return mctx.predicate(s.__contains__, predrepr=b'deleted')
   160 
   160 
   161 
   161 
   162 @predicate('missing()', callstatus=True, weight=_WEIGHT_STATUS)
   162 @predicate(b'missing()', callstatus=True, weight=_WEIGHT_STATUS)
   163 def missing(mctx, x):
   163 def missing(mctx, x):
   164     """File that is missing according to :hg:`status`.
   164     """File that is missing according to :hg:`status`.
   165     """
   165     """
   166     # i18n: "missing" is a keyword
   166     # i18n: "missing" is a keyword
   167     getargs(x, 0, 0, _("missing takes no arguments"))
   167     getargs(x, 0, 0, _(b"missing takes no arguments"))
   168     s = set(mctx.status().deleted)
   168     s = set(mctx.status().deleted)
   169     return mctx.predicate(s.__contains__, predrepr='deleted')
   169     return mctx.predicate(s.__contains__, predrepr=b'deleted')
   170 
   170 
   171 
   171 
   172 @predicate('unknown()', callstatus=True, weight=_WEIGHT_STATUS_THOROUGH)
   172 @predicate(b'unknown()', callstatus=True, weight=_WEIGHT_STATUS_THOROUGH)
   173 def unknown(mctx, x):
   173 def unknown(mctx, x):
   174     """File that is unknown according to :hg:`status`."""
   174     """File that is unknown according to :hg:`status`."""
   175     # i18n: "unknown" is a keyword
   175     # i18n: "unknown" is a keyword
   176     getargs(x, 0, 0, _("unknown takes no arguments"))
   176     getargs(x, 0, 0, _(b"unknown takes no arguments"))
   177     s = set(mctx.status().unknown)
   177     s = set(mctx.status().unknown)
   178     return mctx.predicate(s.__contains__, predrepr='unknown')
   178     return mctx.predicate(s.__contains__, predrepr=b'unknown')
   179 
   179 
   180 
   180 
   181 @predicate('ignored()', callstatus=True, weight=_WEIGHT_STATUS_THOROUGH)
   181 @predicate(b'ignored()', callstatus=True, weight=_WEIGHT_STATUS_THOROUGH)
   182 def ignored(mctx, x):
   182 def ignored(mctx, x):
   183     """File that is ignored according to :hg:`status`."""
   183     """File that is ignored according to :hg:`status`."""
   184     # i18n: "ignored" is a keyword
   184     # i18n: "ignored" is a keyword
   185     getargs(x, 0, 0, _("ignored takes no arguments"))
   185     getargs(x, 0, 0, _(b"ignored takes no arguments"))
   186     s = set(mctx.status().ignored)
   186     s = set(mctx.status().ignored)
   187     return mctx.predicate(s.__contains__, predrepr='ignored')
   187     return mctx.predicate(s.__contains__, predrepr=b'ignored')
   188 
   188 
   189 
   189 
   190 @predicate('clean()', callstatus=True, weight=_WEIGHT_STATUS)
   190 @predicate(b'clean()', callstatus=True, weight=_WEIGHT_STATUS)
   191 def clean(mctx, x):
   191 def clean(mctx, x):
   192     """File that is clean according to :hg:`status`.
   192     """File that is clean according to :hg:`status`.
   193     """
   193     """
   194     # i18n: "clean" is a keyword
   194     # i18n: "clean" is a keyword
   195     getargs(x, 0, 0, _("clean takes no arguments"))
   195     getargs(x, 0, 0, _(b"clean takes no arguments"))
   196     s = set(mctx.status().clean)
   196     s = set(mctx.status().clean)
   197     return mctx.predicate(s.__contains__, predrepr='clean')
   197     return mctx.predicate(s.__contains__, predrepr=b'clean')
   198 
   198 
   199 
   199 
   200 @predicate('tracked()')
   200 @predicate(b'tracked()')
   201 def tracked(mctx, x):
   201 def tracked(mctx, x):
   202     """File that is under Mercurial control."""
   202     """File that is under Mercurial control."""
   203     # i18n: "tracked" is a keyword
   203     # i18n: "tracked" is a keyword
   204     getargs(x, 0, 0, _("tracked takes no arguments"))
   204     getargs(x, 0, 0, _(b"tracked takes no arguments"))
   205     return mctx.predicate(mctx.ctx.__contains__, predrepr='tracked')
   205     return mctx.predicate(mctx.ctx.__contains__, predrepr=b'tracked')
   206 
   206 
   207 
   207 
   208 @predicate('binary()', weight=_WEIGHT_READ_CONTENTS)
   208 @predicate(b'binary()', weight=_WEIGHT_READ_CONTENTS)
   209 def binary(mctx, x):
   209 def binary(mctx, x):
   210     """File that appears to be binary (contains NUL bytes).
   210     """File that appears to be binary (contains NUL bytes).
   211     """
   211     """
   212     # i18n: "binary" is a keyword
   212     # i18n: "binary" is a keyword
   213     getargs(x, 0, 0, _("binary takes no arguments"))
   213     getargs(x, 0, 0, _(b"binary takes no arguments"))
   214     return mctx.fpredicate(
   214     return mctx.fpredicate(
   215         lambda fctx: fctx.isbinary(), predrepr='binary', cache=True
   215         lambda fctx: fctx.isbinary(), predrepr=b'binary', cache=True
   216     )
   216     )
   217 
   217 
   218 
   218 
   219 @predicate('exec()')
   219 @predicate(b'exec()')
   220 def exec_(mctx, x):
   220 def exec_(mctx, x):
   221     """File that is marked as executable.
   221     """File that is marked as executable.
   222     """
   222     """
   223     # i18n: "exec" is a keyword
   223     # i18n: "exec" is a keyword
   224     getargs(x, 0, 0, _("exec takes no arguments"))
   224     getargs(x, 0, 0, _(b"exec takes no arguments"))
   225     ctx = mctx.ctx
   225     ctx = mctx.ctx
   226     return mctx.predicate(lambda f: ctx.flags(f) == 'x', predrepr='exec')
   226     return mctx.predicate(lambda f: ctx.flags(f) == b'x', predrepr=b'exec')
   227 
   227 
   228 
   228 
   229 @predicate('symlink()')
   229 @predicate(b'symlink()')
   230 def symlink(mctx, x):
   230 def symlink(mctx, x):
   231     """File that is marked as a symlink.
   231     """File that is marked as a symlink.
   232     """
   232     """
   233     # i18n: "symlink" is a keyword
   233     # i18n: "symlink" is a keyword
   234     getargs(x, 0, 0, _("symlink takes no arguments"))
   234     getargs(x, 0, 0, _(b"symlink takes no arguments"))
   235     ctx = mctx.ctx
   235     ctx = mctx.ctx
   236     return mctx.predicate(lambda f: ctx.flags(f) == 'l', predrepr='symlink')
   236     return mctx.predicate(lambda f: ctx.flags(f) == b'l', predrepr=b'symlink')
   237 
   237 
   238 
   238 
   239 @predicate('resolved()', weight=_WEIGHT_STATUS)
   239 @predicate(b'resolved()', weight=_WEIGHT_STATUS)
   240 def resolved(mctx, x):
   240 def resolved(mctx, x):
   241     """File that is marked resolved according to :hg:`resolve -l`.
   241     """File that is marked resolved according to :hg:`resolve -l`.
   242     """
   242     """
   243     # i18n: "resolved" is a keyword
   243     # i18n: "resolved" is a keyword
   244     getargs(x, 0, 0, _("resolved takes no arguments"))
   244     getargs(x, 0, 0, _(b"resolved takes no arguments"))
   245     if mctx.ctx.rev() is not None:
   245     if mctx.ctx.rev() is not None:
   246         return mctx.never()
   246         return mctx.never()
   247     ms = merge.mergestate.read(mctx.ctx.repo())
   247     ms = merge.mergestate.read(mctx.ctx.repo())
   248     return mctx.predicate(
   248     return mctx.predicate(
   249         lambda f: f in ms and ms[f] == 'r', predrepr='resolved'
   249         lambda f: f in ms and ms[f] == b'r', predrepr=b'resolved'
   250     )
   250     )
   251 
   251 
   252 
   252 
   253 @predicate('unresolved()', weight=_WEIGHT_STATUS)
   253 @predicate(b'unresolved()', weight=_WEIGHT_STATUS)
   254 def unresolved(mctx, x):
   254 def unresolved(mctx, x):
   255     """File that is marked unresolved according to :hg:`resolve -l`.
   255     """File that is marked unresolved according to :hg:`resolve -l`.
   256     """
   256     """
   257     # i18n: "unresolved" is a keyword
   257     # i18n: "unresolved" is a keyword
   258     getargs(x, 0, 0, _("unresolved takes no arguments"))
   258     getargs(x, 0, 0, _(b"unresolved takes no arguments"))
   259     if mctx.ctx.rev() is not None:
   259     if mctx.ctx.rev() is not None:
   260         return mctx.never()
   260         return mctx.never()
   261     ms = merge.mergestate.read(mctx.ctx.repo())
   261     ms = merge.mergestate.read(mctx.ctx.repo())
   262     return mctx.predicate(
   262     return mctx.predicate(
   263         lambda f: f in ms and ms[f] == 'u', predrepr='unresolved'
   263         lambda f: f in ms and ms[f] == b'u', predrepr=b'unresolved'
   264     )
   264     )
   265 
   265 
   266 
   266 
   267 @predicate('hgignore()', weight=_WEIGHT_STATUS)
   267 @predicate(b'hgignore()', weight=_WEIGHT_STATUS)
   268 def hgignore(mctx, x):
   268 def hgignore(mctx, x):
   269     """File that matches the active .hgignore pattern.
   269     """File that matches the active .hgignore pattern.
   270     """
   270     """
   271     # i18n: "hgignore" is a keyword
   271     # i18n: "hgignore" is a keyword
   272     getargs(x, 0, 0, _("hgignore takes no arguments"))
   272     getargs(x, 0, 0, _(b"hgignore takes no arguments"))
   273     return mctx.ctx.repo().dirstate._ignore
   273     return mctx.ctx.repo().dirstate._ignore
   274 
   274 
   275 
   275 
   276 @predicate('portable()', weight=_WEIGHT_CHECK_FILENAME)
   276 @predicate(b'portable()', weight=_WEIGHT_CHECK_FILENAME)
   277 def portable(mctx, x):
   277 def portable(mctx, x):
   278     """File that has a portable name. (This doesn't include filenames with case
   278     """File that has a portable name. (This doesn't include filenames with case
   279     collisions.)
   279     collisions.)
   280     """
   280     """
   281     # i18n: "portable" is a keyword
   281     # i18n: "portable" is a keyword
   282     getargs(x, 0, 0, _("portable takes no arguments"))
   282     getargs(x, 0, 0, _(b"portable takes no arguments"))
   283     return mctx.predicate(
   283     return mctx.predicate(
   284         lambda f: util.checkwinfilename(f) is None, predrepr='portable'
   284         lambda f: util.checkwinfilename(f) is None, predrepr=b'portable'
   285     )
   285     )
   286 
   286 
   287 
   287 
   288 @predicate('grep(regex)', weight=_WEIGHT_READ_CONTENTS)
   288 @predicate(b'grep(regex)', weight=_WEIGHT_READ_CONTENTS)
   289 def grep(mctx, x):
   289 def grep(mctx, x):
   290     """File contains the given regular expression.
   290     """File contains the given regular expression.
   291     """
   291     """
   292     try:
   292     try:
   293         # i18n: "grep" is a keyword
   293         # i18n: "grep" is a keyword
   294         r = re.compile(getstring(x, _("grep requires a pattern")))
   294         r = re.compile(getstring(x, _(b"grep requires a pattern")))
   295     except re.error as e:
   295     except re.error as e:
   296         raise error.ParseError(
   296         raise error.ParseError(
   297             _('invalid match pattern: %s') % stringutil.forcebytestr(e)
   297             _(b'invalid match pattern: %s') % stringutil.forcebytestr(e)
   298         )
   298         )
   299     return mctx.fpredicate(
   299     return mctx.fpredicate(
   300         lambda fctx: r.search(fctx.data()),
   300         lambda fctx: r.search(fctx.data()),
   301         predrepr=('grep(%r)', r.pattern),
   301         predrepr=(b'grep(%r)', r.pattern),
   302         cache=True,
   302         cache=True,
   303     )
   303     )
   304 
   304 
   305 
   305 
   306 def _sizetomax(s):
   306 def _sizetomax(s):
   309         for k, v in util._sizeunits:
   309         for k, v in util._sizeunits:
   310             if s.endswith(k):
   310             if s.endswith(k):
   311                 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
   311                 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
   312                 n = s[: -len(k)]
   312                 n = s[: -len(k)]
   313                 inc = 1.0
   313                 inc = 1.0
   314                 if "." in n:
   314                 if b"." in n:
   315                     inc /= 10 ** len(n.split(".")[1])
   315                     inc /= 10 ** len(n.split(b".")[1])
   316                 return int((float(n) + inc) * v) - 1
   316                 return int((float(n) + inc) * v) - 1
   317         # no extension, this is a precise value
   317         # no extension, this is a precise value
   318         return int(s)
   318         return int(s)
   319     except ValueError:
   319     except ValueError:
   320         raise error.ParseError(_("couldn't parse size: %s") % s)
   320         raise error.ParseError(_(b"couldn't parse size: %s") % s)
   321 
   321 
   322 
   322 
   323 def sizematcher(expr):
   323 def sizematcher(expr):
   324     """Return a function(size) -> bool from the ``size()`` expression"""
   324     """Return a function(size) -> bool from the ``size()`` expression"""
   325     expr = expr.strip()
   325     expr = expr.strip()
   326     if '-' in expr:  # do we have a range?
   326     if b'-' in expr:  # do we have a range?
   327         a, b = expr.split('-', 1)
   327         a, b = expr.split(b'-', 1)
   328         a = util.sizetoint(a)
   328         a = util.sizetoint(a)
   329         b = util.sizetoint(b)
   329         b = util.sizetoint(b)
   330         return lambda x: x >= a and x <= b
   330         return lambda x: x >= a and x <= b
   331     elif expr.startswith("<="):
   331     elif expr.startswith(b"<="):
   332         a = util.sizetoint(expr[2:])
   332         a = util.sizetoint(expr[2:])
   333         return lambda x: x <= a
   333         return lambda x: x <= a
   334     elif expr.startswith("<"):
   334     elif expr.startswith(b"<"):
   335         a = util.sizetoint(expr[1:])
   335         a = util.sizetoint(expr[1:])
   336         return lambda x: x < a
   336         return lambda x: x < a
   337     elif expr.startswith(">="):
   337     elif expr.startswith(b">="):
   338         a = util.sizetoint(expr[2:])
   338         a = util.sizetoint(expr[2:])
   339         return lambda x: x >= a
   339         return lambda x: x >= a
   340     elif expr.startswith(">"):
   340     elif expr.startswith(b">"):
   341         a = util.sizetoint(expr[1:])
   341         a = util.sizetoint(expr[1:])
   342         return lambda x: x > a
   342         return lambda x: x > a
   343     else:
   343     else:
   344         a = util.sizetoint(expr)
   344         a = util.sizetoint(expr)
   345         b = _sizetomax(expr)
   345         b = _sizetomax(expr)
   346         return lambda x: x >= a and x <= b
   346         return lambda x: x >= a and x <= b
   347 
   347 
   348 
   348 
   349 @predicate('size(expression)', weight=_WEIGHT_STATUS)
   349 @predicate(b'size(expression)', weight=_WEIGHT_STATUS)
   350 def size(mctx, x):
   350 def size(mctx, x):
   351     """File size matches the given expression. Examples:
   351     """File size matches the given expression. Examples:
   352 
   352 
   353     - size('1k') - files from 1024 to 2047 bytes
   353     - size('1k') - files from 1024 to 2047 bytes
   354     - size('< 20k') - files less than 20480 bytes
   354     - size('< 20k') - files less than 20480 bytes
   355     - size('>= .5MB') - files at least 524288 bytes
   355     - size('>= .5MB') - files at least 524288 bytes
   356     - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
   356     - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
   357     """
   357     """
   358     # i18n: "size" is a keyword
   358     # i18n: "size" is a keyword
   359     expr = getstring(x, _("size requires an expression"))
   359     expr = getstring(x, _(b"size requires an expression"))
   360     m = sizematcher(expr)
   360     m = sizematcher(expr)
   361     return mctx.fpredicate(
   361     return mctx.fpredicate(
   362         lambda fctx: m(fctx.size()), predrepr=('size(%r)', expr), cache=True
   362         lambda fctx: m(fctx.size()), predrepr=(b'size(%r)', expr), cache=True
   363     )
   363     )
   364 
   364 
   365 
   365 
   366 @predicate('encoding(name)', weight=_WEIGHT_READ_CONTENTS)
   366 @predicate(b'encoding(name)', weight=_WEIGHT_READ_CONTENTS)
   367 def encoding(mctx, x):
   367 def encoding(mctx, x):
   368     """File can be successfully decoded with the given character
   368     """File can be successfully decoded with the given character
   369     encoding. May not be useful for encodings other than ASCII and
   369     encoding. May not be useful for encodings other than ASCII and
   370     UTF-8.
   370     UTF-8.
   371     """
   371     """
   372 
   372 
   373     # i18n: "encoding" is a keyword
   373     # i18n: "encoding" is a keyword
   374     enc = getstring(x, _("encoding requires an encoding name"))
   374     enc = getstring(x, _(b"encoding requires an encoding name"))
   375 
   375 
   376     def encp(fctx):
   376     def encp(fctx):
   377         d = fctx.data()
   377         d = fctx.data()
   378         try:
   378         try:
   379             d.decode(pycompat.sysstr(enc))
   379             d.decode(pycompat.sysstr(enc))
   380             return True
   380             return True
   381         except LookupError:
   381         except LookupError:
   382             raise error.Abort(_("unknown encoding '%s'") % enc)
   382             raise error.Abort(_(b"unknown encoding '%s'") % enc)
   383         except UnicodeDecodeError:
   383         except UnicodeDecodeError:
   384             return False
   384             return False
   385 
   385 
   386     return mctx.fpredicate(encp, predrepr=('encoding(%r)', enc), cache=True)
   386     return mctx.fpredicate(encp, predrepr=(b'encoding(%r)', enc), cache=True)
   387 
   387 
   388 
   388 
   389 @predicate('eol(style)', weight=_WEIGHT_READ_CONTENTS)
   389 @predicate(b'eol(style)', weight=_WEIGHT_READ_CONTENTS)
   390 def eol(mctx, x):
   390 def eol(mctx, x):
   391     """File contains newlines of the given style (dos, unix, mac). Binary
   391     """File contains newlines of the given style (dos, unix, mac). Binary
   392     files are excluded, files with mixed line endings match multiple
   392     files are excluded, files with mixed line endings match multiple
   393     styles.
   393     styles.
   394     """
   394     """
   395 
   395 
   396     # i18n: "eol" is a keyword
   396     # i18n: "eol" is a keyword
   397     enc = getstring(x, _("eol requires a style name"))
   397     enc = getstring(x, _(b"eol requires a style name"))
   398 
   398 
   399     def eolp(fctx):
   399     def eolp(fctx):
   400         if fctx.isbinary():
   400         if fctx.isbinary():
   401             return False
   401             return False
   402         d = fctx.data()
   402         d = fctx.data()
   403         if (enc == 'dos' or enc == 'win') and '\r\n' in d:
   403         if (enc == b'dos' or enc == b'win') and b'\r\n' in d:
   404             return True
   404             return True
   405         elif enc == 'unix' and re.search('(?<!\r)\n', d):
   405         elif enc == b'unix' and re.search(b'(?<!\r)\n', d):
   406             return True
   406             return True
   407         elif enc == 'mac' and re.search('\r(?!\n)', d):
   407         elif enc == b'mac' and re.search(b'\r(?!\n)', d):
   408             return True
   408             return True
   409         return False
   409         return False
   410 
   410 
   411     return mctx.fpredicate(eolp, predrepr=('eol(%r)', enc), cache=True)
   411     return mctx.fpredicate(eolp, predrepr=(b'eol(%r)', enc), cache=True)
   412 
   412 
   413 
   413 
   414 @predicate('copied()')
   414 @predicate(b'copied()')
   415 def copied(mctx, x):
   415 def copied(mctx, x):
   416     """File that is recorded as being copied.
   416     """File that is recorded as being copied.
   417     """
   417     """
   418     # i18n: "copied" is a keyword
   418     # i18n: "copied" is a keyword
   419     getargs(x, 0, 0, _("copied takes no arguments"))
   419     getargs(x, 0, 0, _(b"copied takes no arguments"))
   420 
   420 
   421     def copiedp(fctx):
   421     def copiedp(fctx):
   422         p = fctx.parents()
   422         p = fctx.parents()
   423         return p and p[0].path() != fctx.path()
   423         return p and p[0].path() != fctx.path()
   424 
   424 
   425     return mctx.fpredicate(copiedp, predrepr='copied', cache=True)
   425     return mctx.fpredicate(copiedp, predrepr=b'copied', cache=True)
   426 
   426 
   427 
   427 
   428 @predicate('revs(revs, pattern)', weight=_WEIGHT_STATUS)
   428 @predicate(b'revs(revs, pattern)', weight=_WEIGHT_STATUS)
   429 def revs(mctx, x):
   429 def revs(mctx, x):
   430     """Evaluate set in the specified revisions. If the revset match multiple
   430     """Evaluate set in the specified revisions. If the revset match multiple
   431     revs, this will return file matching pattern in any of the revision.
   431     revs, this will return file matching pattern in any of the revision.
   432     """
   432     """
   433     # i18n: "revs" is a keyword
   433     # i18n: "revs" is a keyword
   434     r, x = getargs(x, 2, 2, _("revs takes two arguments"))
   434     r, x = getargs(x, 2, 2, _(b"revs takes two arguments"))
   435     # i18n: "revs" is a keyword
   435     # i18n: "revs" is a keyword
   436     revspec = getstring(r, _("first argument to revs must be a revision"))
   436     revspec = getstring(r, _(b"first argument to revs must be a revision"))
   437     repo = mctx.ctx.repo()
   437     repo = mctx.ctx.repo()
   438     revs = scmutil.revrange(repo, [revspec])
   438     revs = scmutil.revrange(repo, [revspec])
   439 
   439 
   440     matchers = []
   440     matchers = []
   441     for r in revs:
   441     for r in revs:
   447     if len(matchers) == 1:
   447     if len(matchers) == 1:
   448         return matchers[0]
   448         return matchers[0]
   449     return matchmod.unionmatcher(matchers)
   449     return matchmod.unionmatcher(matchers)
   450 
   450 
   451 
   451 
   452 @predicate('status(base, rev, pattern)', weight=_WEIGHT_STATUS)
   452 @predicate(b'status(base, rev, pattern)', weight=_WEIGHT_STATUS)
   453 def status(mctx, x):
   453 def status(mctx, x):
   454     """Evaluate predicate using status change between ``base`` and
   454     """Evaluate predicate using status change between ``base`` and
   455     ``rev``. Examples:
   455     ``rev``. Examples:
   456 
   456 
   457     - ``status(3, 7, added())`` - matches files added from "3" to "7"
   457     - ``status(3, 7, added())`` - matches files added from "3" to "7"
   458     """
   458     """
   459     repo = mctx.ctx.repo()
   459     repo = mctx.ctx.repo()
   460     # i18n: "status" is a keyword
   460     # i18n: "status" is a keyword
   461     b, r, x = getargs(x, 3, 3, _("status takes three arguments"))
   461     b, r, x = getargs(x, 3, 3, _(b"status takes three arguments"))
   462     # i18n: "status" is a keyword
   462     # i18n: "status" is a keyword
   463     baseerr = _("first argument to status must be a revision")
   463     baseerr = _(b"first argument to status must be a revision")
   464     baserevspec = getstring(b, baseerr)
   464     baserevspec = getstring(b, baseerr)
   465     if not baserevspec:
   465     if not baserevspec:
   466         raise error.ParseError(baseerr)
   466         raise error.ParseError(baseerr)
   467     reverr = _("second argument to status must be a revision")
   467     reverr = _(b"second argument to status must be a revision")
   468     revspec = getstring(r, reverr)
   468     revspec = getstring(r, reverr)
   469     if not revspec:
   469     if not revspec:
   470         raise error.ParseError(reverr)
   470         raise error.ParseError(reverr)
   471     basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
   471     basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
   472     mc = mctx.switch(basectx, ctx)
   472     mc = mctx.switch(basectx, ctx)
   473     return getmatch(mc, x)
   473     return getmatch(mc, x)
   474 
   474 
   475 
   475 
   476 @predicate('subrepo([pattern])')
   476 @predicate(b'subrepo([pattern])')
   477 def subrepo(mctx, x):
   477 def subrepo(mctx, x):
   478     """Subrepositories whose paths match the given pattern.
   478     """Subrepositories whose paths match the given pattern.
   479     """
   479     """
   480     # i18n: "subrepo" is a keyword
   480     # i18n: "subrepo" is a keyword
   481     getargs(x, 0, 1, _("subrepo takes at most one argument"))
   481     getargs(x, 0, 1, _(b"subrepo takes at most one argument"))
   482     ctx = mctx.ctx
   482     ctx = mctx.ctx
   483     sstate = ctx.substate
   483     sstate = ctx.substate
   484     if x:
   484     if x:
   485         pat = getpattern(
   485         pat = getpattern(
   486             x,
   486             x,
   487             matchmod.allpatternkinds,
   487             matchmod.allpatternkinds,
   488             # i18n: "subrepo" is a keyword
   488             # i18n: "subrepo" is a keyword
   489             _("subrepo requires a pattern or no arguments"),
   489             _(b"subrepo requires a pattern or no arguments"),
   490         )
   490         )
   491         fast = not matchmod.patkind(pat)
   491         fast = not matchmod.patkind(pat)
   492         if fast:
   492         if fast:
   493 
   493 
   494             def m(s):
   494             def m(s):
   495                 return s == pat
   495                 return s == pat
   496 
   496 
   497         else:
   497         else:
   498             m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
   498             m = matchmod.match(ctx.repo().root, b'', [pat], ctx=ctx)
   499         return mctx.predicate(
   499         return mctx.predicate(
   500             lambda f: f in sstate and m(f), predrepr=('subrepo(%r)', pat)
   500             lambda f: f in sstate and m(f), predrepr=(b'subrepo(%r)', pat)
   501         )
   501         )
   502     else:
   502     else:
   503         return mctx.predicate(sstate.__contains__, predrepr='subrepo')
   503         return mctx.predicate(sstate.__contains__, predrepr=b'subrepo')
   504 
   504 
   505 
   505 
   506 methods = {
   506 methods = {
   507     'withstatus': getmatchwithstatus,
   507     b'withstatus': getmatchwithstatus,
   508     'string': stringmatch,
   508     b'string': stringmatch,
   509     'symbol': stringmatch,
   509     b'symbol': stringmatch,
   510     'kindpat': kindpatmatch,
   510     b'kindpat': kindpatmatch,
   511     'patterns': patternsmatch,
   511     b'patterns': patternsmatch,
   512     'and': andmatch,
   512     b'and': andmatch,
   513     'or': ormatch,
   513     b'or': ormatch,
   514     'minus': minusmatch,
   514     b'minus': minusmatch,
   515     'list': listmatch,
   515     b'list': listmatch,
   516     'not': notmatch,
   516     b'not': notmatch,
   517     'func': func,
   517     b'func': func,
   518 }
   518 }
   519 
   519 
   520 
   520 
   521 class matchctx(object):
   521 class matchctx(object):
   522     def __init__(self, basectx, ctx, badfn=None):
   522     def __init__(self, basectx, ctx, badfn=None):
   548 
   548 
   549     def _buildstatus(self, keys):
   549     def _buildstatus(self, keys):
   550         self._status = self._basectx.status(
   550         self._status = self._basectx.status(
   551             self.ctx,
   551             self.ctx,
   552             self._match,
   552             self._match,
   553             listignored='ignored' in keys,
   553             listignored=b'ignored' in keys,
   554             listclean='clean' in keys,
   554             listclean=b'clean' in keys,
   555             listunknown='unknown' in keys,
   555             listunknown=b'unknown' in keys,
   556         )
   556         )
   557 
   557 
   558     def status(self):
   558     def status(self):
   559         return self._status
   559         return self._status
   560 
   560