comparison mercurial/minifileset.py @ 35741:73432eee0ac4

fileset: add kind:pat operator ":" isn't taken as a symbol character but an infix operator so we can write e.g. "path:'foo bar'" as well as "'path:foo bar'". An invalid pattern kind is rejected in the former form as we know a kind is specified explicitly. The binding strength is copied from "x:y" range operator of revset. Perhaps it can be adjusted later if we want to parse "foo:bar()" as "(foo:bar)()", not "foo:(bar())". We can also add "kind:" postfix operator if we want. One possible confusion is that the scope of the leading "set:" vs "kind:pat" operator. The former is consumed by a matcher so applies to the whole fileset expression: $ hg files 'set:foo() or kind:bar or baz' ^^^^^^^^^^^^^^^^^^^^^^^^ Whereas the scope of kind:pat operator is narrow: $ hg files 'set:foo() or kind:bar or baz' ^^^
author Yuya Nishihara <yuya@tcha.org>
date Sun, 14 Jan 2018 13:29:15 +0900
parents 06a757b9e334
children d5288b966e2f
comparison
equal deleted inserted replaced
35740:06a757b9e334 35741:73432eee0ac4
15 15
16 def _compile(tree): 16 def _compile(tree):
17 if not tree: 17 if not tree:
18 raise error.ParseError(_("missing argument")) 18 raise error.ParseError(_("missing argument"))
19 op = tree[0] 19 op = tree[0]
20 if op in {'symbol', 'string'}: 20 if op in {'symbol', 'string', 'kindpat'}:
21 name = fileset.getstring(tree, _('invalid file pattern')) 21 name = fileset.getpattern(tree, {'path'}, _('invalid file pattern'))
22 if name.startswith('**'): # file extension test, ex. "**.tar.gz" 22 if name.startswith('**'): # file extension test, ex. "**.tar.gz"
23 ext = name[2:] 23 ext = name[2:]
24 for c in ext: 24 for c in ext:
25 if c in '*{}[]?/\\': 25 if c in '*{}[]?/\\':
26 raise error.ParseError(_('reserved character: %s') % c) 26 raise error.ParseError(_('reserved character: %s') % c)
27 return lambda n, s: n.endswith(ext) 27 return lambda n, s: n.endswith(ext)
28 # TODO: teach fileset about 'path:', so that this can be a symbol and
29 # not require quoting.
30 elif name.startswith('path:'): # directory or full path test 28 elif name.startswith('path:'): # directory or full path test
31 p = name[5:] # prefix 29 p = name[5:] # prefix
32 pl = len(p) 30 pl = len(p)
33 f = lambda n, s: n.startswith(p) and (len(n) == pl or n[pl] == '/') 31 f = lambda n, s: n.startswith(p) and (len(n) == pl or n[pl] == '/')
34 return f 32 return f
76 common logic operations, and parenthesis for grouping. The supported path 74 common logic operations, and parenthesis for grouping. The supported path
77 tests are '**.extname' for file extension test, and '"path:dir/subdir"' 75 tests are '**.extname' for file extension test, and '"path:dir/subdir"'
78 for prefix test. The ``size()`` predicate is borrowed from filesets to test 76 for prefix test. The ``size()`` predicate is borrowed from filesets to test
79 file size. The predicates ``all()`` and ``none()`` are also supported. 77 file size. The predicates ``all()`` and ``none()`` are also supported.
80 78
81 '(**.php & size(">10MB")) | **.zip | ("path:bin" & !"path:bin/README")' for 79 '(**.php & size(">10MB")) | **.zip | (path:bin & !path:bin/README)' for
82 example, will catch all php files whose size is greater than 10 MB, all 80 example, will catch all php files whose size is greater than 10 MB, all
83 files whose name ends with ".zip", and all files under "bin" in the repo 81 files whose name ends with ".zip", and all files under "bin" in the repo
84 root except for "bin/README". 82 root except for "bin/README".
85 """ 83 """
86 tree = fileset.parse(text) 84 tree = fileset.parse(text)