##// END OF EJS Templates
fileset: add kind:pat operator...
Yuya Nishihara -
r35759:73432eee default
parent child Browse files
Show More
@@ -45,7 +45,7 b' Configs::'
45
45
46 # Which files to track in LFS. Path tests are "**.extname" for file
46 # Which files to track in LFS. Path tests are "**.extname" for file
47 # extensions, and "path:under/some/directory" for path prefix. Both
47 # extensions, and "path:under/some/directory" for path prefix. Both
48 # are relative to the repository root, and the latter must be quoted.
48 # are relative to the repository root.
49 # File size can be tested with the "size()" fileset, and tests can be
49 # File size can be tested with the "size()" fileset, and tests can be
50 # joined with fileset operators. (See "hg help filesets.operators".)
50 # joined with fileset operators. (See "hg help filesets.operators".)
51 #
51 #
@@ -55,9 +55,9 b' Configs::'
55 # - size(">20MB") # larger than 20MB
55 # - size(">20MB") # larger than 20MB
56 # - !**.txt # anything not a *.txt file
56 # - !**.txt # anything not a *.txt file
57 # - **.zip | **.tar.gz | **.7z # some types of compressed files
57 # - **.zip | **.tar.gz | **.7z # some types of compressed files
58 # - "path:bin" # files under "bin" in the project root
58 # - path:bin # files under "bin" in the project root
59 # - (**.php & size(">2MB")) | (**.js & size(">5MB")) | **.tar.gz
59 # - (**.php & size(">2MB")) | (**.js & size(">5MB")) | **.tar.gz
60 # | ("path:bin" & !"path:/bin/README") | size(">1GB")
60 # | (path:bin & !path:/bin/README) | size(">1GB")
61 # (default: none())
61 # (default: none())
62 #
62 #
63 # This is ignored if there is a tracked '.hglfs' file, and this setting
63 # This is ignored if there is a tracked '.hglfs' file, and this setting
@@ -24,6 +24,7 b' from . import ('
24 elements = {
24 elements = {
25 # token-type: binding-strength, primary, prefix, infix, suffix
25 # token-type: binding-strength, primary, prefix, infix, suffix
26 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
26 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
27 ":": (15, None, None, ("kindpat", 15), None),
27 "-": (5, None, ("negate", 19), ("minus", 5), None),
28 "-": (5, None, ("negate", 19), ("minus", 5), None),
28 "not": (10, None, ("not", 10), None, None),
29 "not": (10, None, ("not", 10), None, None),
29 "!": (10, None, ("not", 10), None, None),
30 "!": (10, None, ("not", 10), None, None),
@@ -50,7 +51,7 b' def tokenize(program):'
50 c = program[pos]
51 c = program[pos]
51 if c.isspace(): # skip inter-token whitespace
52 if c.isspace(): # skip inter-token whitespace
52 pass
53 pass
53 elif c in "(),-|&+!": # handle simple operators
54 elif c in "(),-:|&+!": # handle simple operators
54 yield (c, None, pos)
55 yield (c, None, pos)
55 elif (c in '"\'' or c == 'r' and
56 elif (c in '"\'' or c == 'r' and
56 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
57 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
@@ -110,6 +111,18 b' def getstring(x, err):'
110 return x[1]
111 return x[1]
111 raise error.ParseError(err)
112 raise error.ParseError(err)
112
113
114 def _getkindpat(x, y, allkinds, err):
115 kind = getsymbol(x)
116 pat = getstring(y, err)
117 if kind not in allkinds:
118 raise error.ParseError(_("invalid pattern kind: %s") % kind)
119 return '%s:%s' % (kind, pat)
120
121 def getpattern(x, allkinds, err):
122 if x and x[0] == 'kindpat':
123 return _getkindpat(x[1], x[2], allkinds, err)
124 return getstring(x, err)
125
113 def getset(mctx, x):
126 def getset(mctx, x):
114 if not x:
127 if not x:
115 raise error.ParseError(_("missing argument"))
128 raise error.ParseError(_("missing argument"))
@@ -119,6 +132,10 b' def stringset(mctx, x):'
119 m = mctx.matcher([x])
132 m = mctx.matcher([x])
120 return [f for f in mctx.subset if m(f)]
133 return [f for f in mctx.subset if m(f)]
121
134
135 def kindpatset(mctx, x, y):
136 return stringset(mctx, _getkindpat(x, y, matchmod.allpatternkinds,
137 _("pattern must be a string")))
138
122 def andset(mctx, x, y):
139 def andset(mctx, x, y):
123 return getset(mctx.narrow(getset(mctx, x)), y)
140 return getset(mctx.narrow(getset(mctx, x)), y)
124
141
@@ -507,8 +524,9 b' def subrepo(mctx, x):'
507 ctx = mctx.ctx
524 ctx = mctx.ctx
508 sstate = sorted(ctx.substate)
525 sstate = sorted(ctx.substate)
509 if x:
526 if x:
510 # i18n: "subrepo" is a keyword
527 pat = getpattern(x, matchmod.allpatternkinds,
511 pat = getstring(x, _("subrepo requires a pattern or no arguments"))
528 # i18n: "subrepo" is a keyword
529 _("subrepo requires a pattern or no arguments"))
512 fast = not matchmod.patkind(pat)
530 fast = not matchmod.patkind(pat)
513 if fast:
531 if fast:
514 def m(s):
532 def m(s):
@@ -522,6 +540,7 b' def subrepo(mctx, x):'
522 methods = {
540 methods = {
523 'string': stringset,
541 'string': stringset,
524 'symbol': stringset,
542 'symbol': stringset,
543 'kindpat': kindpatset,
525 'and': andset,
544 'and': andset,
526 'or': orset,
545 'or': orset,
527 'minus': minusset,
546 'minus': minusset,
@@ -9,7 +9,8 b' Identifiers such as filenames or pattern'
9 or double quotes if they contain characters outside of
9 or double quotes if they contain characters outside of
10 ``[.*{}[]?/\_a-zA-Z0-9\x80-\xff]`` or if they match one of the
10 ``[.*{}[]?/\_a-zA-Z0-9\x80-\xff]`` or if they match one of the
11 predefined predicates. This generally applies to file patterns other
11 predefined predicates. This generally applies to file patterns other
12 than globs and arguments for predicates.
12 than globs and arguments for predicates. Pattern prefixes such as
13 ``path:`` may be specified without quoting.
13
14
14 Special characters can be used in quoted identifiers by escaping them,
15 Special characters can be used in quoted identifiers by escaping them,
15 e.g., ``\n`` is interpreted as a newline. To prevent them from being
16 e.g., ``\n`` is interpreted as a newline. To prevent them from being
@@ -75,4 +76,4 b' Some sample queries:'
75
76
76 - Remove files listed in foo.lst that contain the letter a or b::
77 - Remove files listed in foo.lst that contain the letter a or b::
77
78
78 hg remove "set: 'listfile:foo.lst' and (**a* or **b*)"
79 hg remove "set: listfile:foo.lst and (**a* or **b*)"
@@ -17,16 +17,14 b' 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)
@@ -78,7 +76,7 b' def compile(text):'
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".
@@ -27,6 +27,24 b' Test operators and basic patterns'
27 (string 're:a\\d')
27 (string 're:a\\d')
28 a1
28 a1
29 a2
29 a2
30 $ fileset -v '!re:"a\d"'
31 (not
32 (kindpat
33 (symbol 're')
34 (string 'a\\d')))
35 b1
36 b2
37 $ fileset -v 'path:a1 or glob:b?'
38 (or
39 (kindpat
40 (symbol 'path')
41 (symbol 'a1'))
42 (kindpat
43 (symbol 'glob')
44 (symbol 'b?')))
45 a1
46 b1
47 b2
30 $ fileset -v 'a1 or a2'
48 $ fileset -v 'a1 or a2'
31 (or
49 (or
32 (symbol 'a1')
50 (symbol 'a1')
@@ -80,6 +98,22 b' Test invalid syntax'
80 hg: parse error: can't use negate operator in this context
98 hg: parse error: can't use negate operator in this context
81 [255]
99 [255]
82
100
101 $ fileset '"path":.'
102 hg: parse error: not a symbol
103 [255]
104 $ fileset 'path:foo bar'
105 hg: parse error at 9: invalid token
106 [255]
107 $ fileset 'foo:bar:baz'
108 hg: parse error: not a symbol
109 [255]
110 $ fileset 'foo:bar()'
111 hg: parse error: pattern must be a string
112 [255]
113 $ fileset 'foo:bar'
114 hg: parse error: invalid pattern kind: foo
115 [255]
116
83 Test files status
117 Test files status
84
118
85 $ rm a1
119 $ rm a1
@@ -346,6 +380,9 b' Test with a revision'
346 $ fileset -r4 'subrepo("re:su.*")'
380 $ fileset -r4 'subrepo("re:su.*")'
347 sub
381 sub
348 sub2
382 sub2
383 $ fileset -r4 'subrepo(re:su.*)'
384 sub
385 sub2
349 $ fileset -r4 'subrepo("sub")'
386 $ fileset -r4 'subrepo("sub")'
350 sub
387 sub
351 $ fileset -r4 'b2 or c1'
388 $ fileset -r4 'b2 or c1'
@@ -23,7 +23,7 b" check('none()', [], [('a.php', 123), ('b"
23 check('!!!!((!(!!all())))', [], [('a.php', 123), ('b.txt', 0)])
23 check('!!!!((!(!!all())))', [], [('a.php', 123), ('b.txt', 0)])
24
24
25 check('"path:a" & (**.b | **.c)', [('a/b.b', 0), ('a/c.c', 0)], [('b/c.c', 0)])
25 check('"path:a" & (**.b | **.c)', [('a/b.b', 0), ('a/c.c', 0)], [('b/c.c', 0)])
26 check('("path:a" & **.b) | **.c',
26 check('(path:a & **.b) | **.c',
27 [('a/b.b', 0), ('a/c.c', 0), ('b/c.c', 0)], [])
27 [('a/b.b', 0), ('a/c.c', 0), ('b/c.c', 0)], [])
28
28
29 check('**.bin - size("<20B")', [('b.bin', 21)], [('a.bin', 11), ('b.txt', 21)])
29 check('**.bin - size("<20B")', [('b.bin', 21)], [('a.bin', 11), ('b.txt', 21)])
General Comments 0
You need to be logged in to leave comments. Login now