##// END OF EJS Templates
fileset: raise ProgrammingError for bad existing() calls...
Yuya Nishihara -
r38348:8783f128 default
parent child Browse files
Show More
@@ -1,663 +1,664 b''
1 # fileset.py - file set queries for mercurial
1 # fileset.py - file set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import re
10 import re
11
11
12 from .i18n import _
12 from .i18n import _
13 from . import (
13 from . import (
14 error,
14 error,
15 match as matchmod,
15 match as matchmod,
16 merge,
16 merge,
17 parser,
17 parser,
18 pycompat,
18 pycompat,
19 registrar,
19 registrar,
20 scmutil,
20 scmutil,
21 util,
21 util,
22 )
22 )
23 from .utils import (
23 from .utils import (
24 stringutil,
24 stringutil,
25 )
25 )
26
26
27 elements = {
27 elements = {
28 # token-type: binding-strength, primary, prefix, infix, suffix
28 # token-type: binding-strength, primary, prefix, infix, suffix
29 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
29 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
30 ":": (15, None, None, ("kindpat", 15), None),
30 ":": (15, None, None, ("kindpat", 15), None),
31 "-": (5, None, ("negate", 19), ("minus", 5), None),
31 "-": (5, None, ("negate", 19), ("minus", 5), None),
32 "not": (10, None, ("not", 10), None, None),
32 "not": (10, None, ("not", 10), None, None),
33 "!": (10, None, ("not", 10), None, None),
33 "!": (10, None, ("not", 10), None, None),
34 "and": (5, None, None, ("and", 5), None),
34 "and": (5, None, None, ("and", 5), None),
35 "&": (5, None, None, ("and", 5), None),
35 "&": (5, None, None, ("and", 5), None),
36 "or": (4, None, None, ("or", 4), None),
36 "or": (4, None, None, ("or", 4), None),
37 "|": (4, None, None, ("or", 4), None),
37 "|": (4, None, None, ("or", 4), None),
38 "+": (4, None, None, ("or", 4), None),
38 "+": (4, None, None, ("or", 4), None),
39 ",": (2, None, None, ("list", 2), None),
39 ",": (2, None, None, ("list", 2), None),
40 ")": (0, None, None, None, None),
40 ")": (0, None, None, None, None),
41 "symbol": (0, "symbol", None, None, None),
41 "symbol": (0, "symbol", None, None, None),
42 "string": (0, "string", None, None, None),
42 "string": (0, "string", None, None, None),
43 "end": (0, None, None, None, None),
43 "end": (0, None, None, None, None),
44 }
44 }
45
45
46 keywords = {'and', 'or', 'not'}
46 keywords = {'and', 'or', 'not'}
47
47
48 globchars = ".*{}[]?/\\_"
48 globchars = ".*{}[]?/\\_"
49
49
50 def tokenize(program):
50 def tokenize(program):
51 pos, l = 0, len(program)
51 pos, l = 0, len(program)
52 program = pycompat.bytestr(program)
52 program = pycompat.bytestr(program)
53 while pos < l:
53 while pos < l:
54 c = program[pos]
54 c = program[pos]
55 if c.isspace(): # skip inter-token whitespace
55 if c.isspace(): # skip inter-token whitespace
56 pass
56 pass
57 elif c in "(),-:|&+!": # handle simple operators
57 elif c in "(),-:|&+!": # handle simple operators
58 yield (c, None, pos)
58 yield (c, None, pos)
59 elif (c in '"\'' or c == 'r' and
59 elif (c in '"\'' or c == 'r' and
60 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
60 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
61 if c == 'r':
61 if c == 'r':
62 pos += 1
62 pos += 1
63 c = program[pos]
63 c = program[pos]
64 decode = lambda x: x
64 decode = lambda x: x
65 else:
65 else:
66 decode = parser.unescapestr
66 decode = parser.unescapestr
67 pos += 1
67 pos += 1
68 s = pos
68 s = pos
69 while pos < l: # find closing quote
69 while pos < l: # find closing quote
70 d = program[pos]
70 d = program[pos]
71 if d == '\\': # skip over escaped characters
71 if d == '\\': # skip over escaped characters
72 pos += 2
72 pos += 2
73 continue
73 continue
74 if d == c:
74 if d == c:
75 yield ('string', decode(program[s:pos]), s)
75 yield ('string', decode(program[s:pos]), s)
76 break
76 break
77 pos += 1
77 pos += 1
78 else:
78 else:
79 raise error.ParseError(_("unterminated string"), s)
79 raise error.ParseError(_("unterminated string"), s)
80 elif c.isalnum() or c in globchars or ord(c) > 127:
80 elif c.isalnum() or c in globchars or ord(c) > 127:
81 # gather up a symbol/keyword
81 # gather up a symbol/keyword
82 s = pos
82 s = pos
83 pos += 1
83 pos += 1
84 while pos < l: # find end of symbol
84 while pos < l: # find end of symbol
85 d = program[pos]
85 d = program[pos]
86 if not (d.isalnum() or d in globchars or ord(d) > 127):
86 if not (d.isalnum() or d in globchars or ord(d) > 127):
87 break
87 break
88 pos += 1
88 pos += 1
89 sym = program[s:pos]
89 sym = program[s:pos]
90 if sym in keywords: # operator keywords
90 if sym in keywords: # operator keywords
91 yield (sym, None, s)
91 yield (sym, None, s)
92 else:
92 else:
93 yield ('symbol', sym, s)
93 yield ('symbol', sym, s)
94 pos -= 1
94 pos -= 1
95 else:
95 else:
96 raise error.ParseError(_("syntax error"), pos)
96 raise error.ParseError(_("syntax error"), pos)
97 pos += 1
97 pos += 1
98 yield ('end', None, pos)
98 yield ('end', None, pos)
99
99
100 def parse(expr):
100 def parse(expr):
101 p = parser.parser(elements)
101 p = parser.parser(elements)
102 tree, pos = p.parse(tokenize(expr))
102 tree, pos = p.parse(tokenize(expr))
103 if pos != len(expr):
103 if pos != len(expr):
104 raise error.ParseError(_("invalid token"), pos)
104 raise error.ParseError(_("invalid token"), pos)
105 return tree
105 return tree
106
106
107 def getsymbol(x):
107 def getsymbol(x):
108 if x and x[0] == 'symbol':
108 if x and x[0] == 'symbol':
109 return x[1]
109 return x[1]
110 raise error.ParseError(_('not a symbol'))
110 raise error.ParseError(_('not a symbol'))
111
111
112 def getstring(x, err):
112 def getstring(x, err):
113 if x and (x[0] == 'string' or x[0] == 'symbol'):
113 if x and (x[0] == 'string' or x[0] == 'symbol'):
114 return x[1]
114 return x[1]
115 raise error.ParseError(err)
115 raise error.ParseError(err)
116
116
117 def _getkindpat(x, y, allkinds, err):
117 def _getkindpat(x, y, allkinds, err):
118 kind = getsymbol(x)
118 kind = getsymbol(x)
119 pat = getstring(y, err)
119 pat = getstring(y, err)
120 if kind not in allkinds:
120 if kind not in allkinds:
121 raise error.ParseError(_("invalid pattern kind: %s") % kind)
121 raise error.ParseError(_("invalid pattern kind: %s") % kind)
122 return '%s:%s' % (kind, pat)
122 return '%s:%s' % (kind, pat)
123
123
124 def getpattern(x, allkinds, err):
124 def getpattern(x, allkinds, err):
125 if x and x[0] == 'kindpat':
125 if x and x[0] == 'kindpat':
126 return _getkindpat(x[1], x[2], allkinds, err)
126 return _getkindpat(x[1], x[2], allkinds, err)
127 return getstring(x, err)
127 return getstring(x, err)
128
128
129 def getset(mctx, x):
129 def getset(mctx, x):
130 if not x:
130 if not x:
131 raise error.ParseError(_("missing argument"))
131 raise error.ParseError(_("missing argument"))
132 return methods[x[0]](mctx, *x[1:])
132 return methods[x[0]](mctx, *x[1:])
133
133
134 def stringset(mctx, x):
134 def stringset(mctx, x):
135 m = mctx.matcher([x])
135 m = mctx.matcher([x])
136 return [f for f in mctx.subset if m(f)]
136 return [f for f in mctx.subset if m(f)]
137
137
138 def kindpatset(mctx, x, y):
138 def kindpatset(mctx, x, y):
139 return stringset(mctx, _getkindpat(x, y, matchmod.allpatternkinds,
139 return stringset(mctx, _getkindpat(x, y, matchmod.allpatternkinds,
140 _("pattern must be a string")))
140 _("pattern must be a string")))
141
141
142 def andset(mctx, x, y):
142 def andset(mctx, x, y):
143 return getset(mctx.narrow(getset(mctx, x)), y)
143 return getset(mctx.narrow(getset(mctx, x)), y)
144
144
145 def orset(mctx, x, y):
145 def orset(mctx, x, y):
146 # needs optimizing
146 # needs optimizing
147 xl = getset(mctx, x)
147 xl = getset(mctx, x)
148 yl = getset(mctx, y)
148 yl = getset(mctx, y)
149 return xl + [f for f in yl if f not in xl]
149 return xl + [f for f in yl if f not in xl]
150
150
151 def notset(mctx, x):
151 def notset(mctx, x):
152 s = set(getset(mctx, x))
152 s = set(getset(mctx, x))
153 return [r for r in mctx.subset if r not in s]
153 return [r for r in mctx.subset if r not in s]
154
154
155 def minusset(mctx, x, y):
155 def minusset(mctx, x, y):
156 xl = getset(mctx, x)
156 xl = getset(mctx, x)
157 yl = set(getset(mctx, y))
157 yl = set(getset(mctx, y))
158 return [f for f in xl if f not in yl]
158 return [f for f in xl if f not in yl]
159
159
160 def negateset(mctx, x):
160 def negateset(mctx, x):
161 raise error.ParseError(_("can't use negate operator in this context"))
161 raise error.ParseError(_("can't use negate operator in this context"))
162
162
163 def listset(mctx, a, b):
163 def listset(mctx, a, b):
164 raise error.ParseError(_("can't use a list in this context"),
164 raise error.ParseError(_("can't use a list in this context"),
165 hint=_('see hg help "filesets.x or y"'))
165 hint=_('see hg help "filesets.x or y"'))
166
166
167 # symbols are callable like:
167 # symbols are callable like:
168 # fun(mctx, x)
168 # fun(mctx, x)
169 # with:
169 # with:
170 # mctx - current matchctx instance
170 # mctx - current matchctx instance
171 # x - argument in tree form
171 # x - argument in tree form
172 symbols = {}
172 symbols = {}
173
173
174 # filesets using matchctx.status()
174 # filesets using matchctx.status()
175 _statuscallers = set()
175 _statuscallers = set()
176
176
177 # filesets using matchctx.existing()
177 # filesets using matchctx.existing()
178 _existingcallers = set()
178 _existingcallers = set()
179
179
180 predicate = registrar.filesetpredicate()
180 predicate = registrar.filesetpredicate()
181
181
182 @predicate('modified()', callstatus=True)
182 @predicate('modified()', callstatus=True)
183 def modified(mctx, x):
183 def modified(mctx, x):
184 """File that is modified according to :hg:`status`.
184 """File that is modified according to :hg:`status`.
185 """
185 """
186 # i18n: "modified" is a keyword
186 # i18n: "modified" is a keyword
187 getargs(x, 0, 0, _("modified takes no arguments"))
187 getargs(x, 0, 0, _("modified takes no arguments"))
188 s = set(mctx.status().modified)
188 s = set(mctx.status().modified)
189 return [f for f in mctx.subset if f in s]
189 return [f for f in mctx.subset if f in s]
190
190
191 @predicate('added()', callstatus=True)
191 @predicate('added()', callstatus=True)
192 def added(mctx, x):
192 def added(mctx, x):
193 """File that is added according to :hg:`status`.
193 """File that is added according to :hg:`status`.
194 """
194 """
195 # i18n: "added" is a keyword
195 # i18n: "added" is a keyword
196 getargs(x, 0, 0, _("added takes no arguments"))
196 getargs(x, 0, 0, _("added takes no arguments"))
197 s = set(mctx.status().added)
197 s = set(mctx.status().added)
198 return [f for f in mctx.subset if f in s]
198 return [f for f in mctx.subset if f in s]
199
199
200 @predicate('removed()', callstatus=True)
200 @predicate('removed()', callstatus=True)
201 def removed(mctx, x):
201 def removed(mctx, x):
202 """File that is removed according to :hg:`status`.
202 """File that is removed according to :hg:`status`.
203 """
203 """
204 # i18n: "removed" is a keyword
204 # i18n: "removed" is a keyword
205 getargs(x, 0, 0, _("removed takes no arguments"))
205 getargs(x, 0, 0, _("removed takes no arguments"))
206 s = set(mctx.status().removed)
206 s = set(mctx.status().removed)
207 return [f for f in mctx.subset if f in s]
207 return [f for f in mctx.subset if f in s]
208
208
209 @predicate('deleted()', callstatus=True)
209 @predicate('deleted()', callstatus=True)
210 def deleted(mctx, x):
210 def deleted(mctx, x):
211 """Alias for ``missing()``.
211 """Alias for ``missing()``.
212 """
212 """
213 # i18n: "deleted" is a keyword
213 # i18n: "deleted" is a keyword
214 getargs(x, 0, 0, _("deleted takes no arguments"))
214 getargs(x, 0, 0, _("deleted takes no arguments"))
215 s = set(mctx.status().deleted)
215 s = set(mctx.status().deleted)
216 return [f for f in mctx.subset if f in s]
216 return [f for f in mctx.subset if f in s]
217
217
218 @predicate('missing()', callstatus=True)
218 @predicate('missing()', callstatus=True)
219 def missing(mctx, x):
219 def missing(mctx, x):
220 """File that is missing according to :hg:`status`.
220 """File that is missing according to :hg:`status`.
221 """
221 """
222 # i18n: "missing" is a keyword
222 # i18n: "missing" is a keyword
223 getargs(x, 0, 0, _("missing takes no arguments"))
223 getargs(x, 0, 0, _("missing takes no arguments"))
224 s = set(mctx.status().deleted)
224 s = set(mctx.status().deleted)
225 return [f for f in mctx.subset if f in s]
225 return [f for f in mctx.subset if f in s]
226
226
227 @predicate('unknown()', callstatus=True)
227 @predicate('unknown()', callstatus=True)
228 def unknown(mctx, x):
228 def unknown(mctx, x):
229 """File that is unknown according to :hg:`status`. These files will only be
229 """File that is unknown according to :hg:`status`. These files will only be
230 considered if this predicate is used.
230 considered if this predicate is used.
231 """
231 """
232 # i18n: "unknown" is a keyword
232 # i18n: "unknown" is a keyword
233 getargs(x, 0, 0, _("unknown takes no arguments"))
233 getargs(x, 0, 0, _("unknown takes no arguments"))
234 s = set(mctx.status().unknown)
234 s = set(mctx.status().unknown)
235 return [f for f in mctx.subset if f in s]
235 return [f for f in mctx.subset if f in s]
236
236
237 @predicate('ignored()', callstatus=True)
237 @predicate('ignored()', callstatus=True)
238 def ignored(mctx, x):
238 def ignored(mctx, x):
239 """File that is ignored according to :hg:`status`. These files will only be
239 """File that is ignored according to :hg:`status`. These files will only be
240 considered if this predicate is used.
240 considered if this predicate is used.
241 """
241 """
242 # i18n: "ignored" is a keyword
242 # i18n: "ignored" is a keyword
243 getargs(x, 0, 0, _("ignored takes no arguments"))
243 getargs(x, 0, 0, _("ignored takes no arguments"))
244 s = set(mctx.status().ignored)
244 s = set(mctx.status().ignored)
245 return [f for f in mctx.subset if f in s]
245 return [f for f in mctx.subset if f in s]
246
246
247 @predicate('clean()', callstatus=True)
247 @predicate('clean()', callstatus=True)
248 def clean(mctx, x):
248 def clean(mctx, x):
249 """File that is clean according to :hg:`status`.
249 """File that is clean according to :hg:`status`.
250 """
250 """
251 # i18n: "clean" is a keyword
251 # i18n: "clean" is a keyword
252 getargs(x, 0, 0, _("clean takes no arguments"))
252 getargs(x, 0, 0, _("clean takes no arguments"))
253 s = set(mctx.status().clean)
253 s = set(mctx.status().clean)
254 return [f for f in mctx.subset if f in s]
254 return [f for f in mctx.subset if f in s]
255
255
256 def func(mctx, a, b):
256 def func(mctx, a, b):
257 funcname = getsymbol(a)
257 funcname = getsymbol(a)
258 if funcname in symbols:
258 if funcname in symbols:
259 enabled = mctx._existingenabled
259 enabled = mctx._existingenabled
260 mctx._existingenabled = funcname in _existingcallers
260 mctx._existingenabled = funcname in _existingcallers
261 try:
261 try:
262 return symbols[funcname](mctx, b)
262 return symbols[funcname](mctx, b)
263 finally:
263 finally:
264 mctx._existingenabled = enabled
264 mctx._existingenabled = enabled
265
265
266 keep = lambda fn: getattr(fn, '__doc__', None) is not None
266 keep = lambda fn: getattr(fn, '__doc__', None) is not None
267
267
268 syms = [s for (s, fn) in symbols.items() if keep(fn)]
268 syms = [s for (s, fn) in symbols.items() if keep(fn)]
269 raise error.UnknownIdentifier(funcname, syms)
269 raise error.UnknownIdentifier(funcname, syms)
270
270
271 def getlist(x):
271 def getlist(x):
272 if not x:
272 if not x:
273 return []
273 return []
274 if x[0] == 'list':
274 if x[0] == 'list':
275 return getlist(x[1]) + [x[2]]
275 return getlist(x[1]) + [x[2]]
276 return [x]
276 return [x]
277
277
278 def getargs(x, min, max, err):
278 def getargs(x, min, max, err):
279 l = getlist(x)
279 l = getlist(x)
280 if len(l) < min or len(l) > max:
280 if len(l) < min or len(l) > max:
281 raise error.ParseError(err)
281 raise error.ParseError(err)
282 return l
282 return l
283
283
284 @predicate('binary()', callexisting=True)
284 @predicate('binary()', callexisting=True)
285 def binary(mctx, x):
285 def binary(mctx, x):
286 """File that appears to be binary (contains NUL bytes).
286 """File that appears to be binary (contains NUL bytes).
287 """
287 """
288 # i18n: "binary" is a keyword
288 # i18n: "binary" is a keyword
289 getargs(x, 0, 0, _("binary takes no arguments"))
289 getargs(x, 0, 0, _("binary takes no arguments"))
290 return [f for f in mctx.existing() if mctx.ctx[f].isbinary()]
290 return [f for f in mctx.existing() if mctx.ctx[f].isbinary()]
291
291
292 @predicate('exec()', callexisting=True)
292 @predicate('exec()', callexisting=True)
293 def exec_(mctx, x):
293 def exec_(mctx, x):
294 """File that is marked as executable.
294 """File that is marked as executable.
295 """
295 """
296 # i18n: "exec" is a keyword
296 # i18n: "exec" is a keyword
297 getargs(x, 0, 0, _("exec takes no arguments"))
297 getargs(x, 0, 0, _("exec takes no arguments"))
298 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x']
298 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x']
299
299
300 @predicate('symlink()', callexisting=True)
300 @predicate('symlink()', callexisting=True)
301 def symlink(mctx, x):
301 def symlink(mctx, x):
302 """File that is marked as a symlink.
302 """File that is marked as a symlink.
303 """
303 """
304 # i18n: "symlink" is a keyword
304 # i18n: "symlink" is a keyword
305 getargs(x, 0, 0, _("symlink takes no arguments"))
305 getargs(x, 0, 0, _("symlink takes no arguments"))
306 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l']
306 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l']
307
307
308 @predicate('resolved()')
308 @predicate('resolved()')
309 def resolved(mctx, x):
309 def resolved(mctx, x):
310 """File that is marked resolved according to :hg:`resolve -l`.
310 """File that is marked resolved according to :hg:`resolve -l`.
311 """
311 """
312 # i18n: "resolved" is a keyword
312 # i18n: "resolved" is a keyword
313 getargs(x, 0, 0, _("resolved takes no arguments"))
313 getargs(x, 0, 0, _("resolved takes no arguments"))
314 if mctx.ctx.rev() is not None:
314 if mctx.ctx.rev() is not None:
315 return []
315 return []
316 ms = merge.mergestate.read(mctx.ctx.repo())
316 ms = merge.mergestate.read(mctx.ctx.repo())
317 return [f for f in mctx.subset if f in ms and ms[f] == 'r']
317 return [f for f in mctx.subset if f in ms and ms[f] == 'r']
318
318
319 @predicate('unresolved()')
319 @predicate('unresolved()')
320 def unresolved(mctx, x):
320 def unresolved(mctx, x):
321 """File that is marked unresolved according to :hg:`resolve -l`.
321 """File that is marked unresolved according to :hg:`resolve -l`.
322 """
322 """
323 # i18n: "unresolved" is a keyword
323 # i18n: "unresolved" is a keyword
324 getargs(x, 0, 0, _("unresolved takes no arguments"))
324 getargs(x, 0, 0, _("unresolved takes no arguments"))
325 if mctx.ctx.rev() is not None:
325 if mctx.ctx.rev() is not None:
326 return []
326 return []
327 ms = merge.mergestate.read(mctx.ctx.repo())
327 ms = merge.mergestate.read(mctx.ctx.repo())
328 return [f for f in mctx.subset if f in ms and ms[f] == 'u']
328 return [f for f in mctx.subset if f in ms and ms[f] == 'u']
329
329
330 @predicate('hgignore()')
330 @predicate('hgignore()')
331 def hgignore(mctx, x):
331 def hgignore(mctx, x):
332 """File that matches the active .hgignore pattern.
332 """File that matches the active .hgignore pattern.
333 """
333 """
334 # i18n: "hgignore" is a keyword
334 # i18n: "hgignore" is a keyword
335 getargs(x, 0, 0, _("hgignore takes no arguments"))
335 getargs(x, 0, 0, _("hgignore takes no arguments"))
336 ignore = mctx.ctx.repo().dirstate._ignore
336 ignore = mctx.ctx.repo().dirstate._ignore
337 return [f for f in mctx.subset if ignore(f)]
337 return [f for f in mctx.subset if ignore(f)]
338
338
339 @predicate('portable()')
339 @predicate('portable()')
340 def portable(mctx, x):
340 def portable(mctx, x):
341 """File that has a portable name. (This doesn't include filenames with case
341 """File that has a portable name. (This doesn't include filenames with case
342 collisions.)
342 collisions.)
343 """
343 """
344 # i18n: "portable" is a keyword
344 # i18n: "portable" is a keyword
345 getargs(x, 0, 0, _("portable takes no arguments"))
345 getargs(x, 0, 0, _("portable takes no arguments"))
346 checkwinfilename = util.checkwinfilename
346 checkwinfilename = util.checkwinfilename
347 return [f for f in mctx.subset if checkwinfilename(f) is None]
347 return [f for f in mctx.subset if checkwinfilename(f) is None]
348
348
349 @predicate('grep(regex)', callexisting=True)
349 @predicate('grep(regex)', callexisting=True)
350 def grep(mctx, x):
350 def grep(mctx, x):
351 """File contains the given regular expression.
351 """File contains the given regular expression.
352 """
352 """
353 try:
353 try:
354 # i18n: "grep" is a keyword
354 # i18n: "grep" is a keyword
355 r = re.compile(getstring(x, _("grep requires a pattern")))
355 r = re.compile(getstring(x, _("grep requires a pattern")))
356 except re.error as e:
356 except re.error as e:
357 raise error.ParseError(_('invalid match pattern: %s') %
357 raise error.ParseError(_('invalid match pattern: %s') %
358 stringutil.forcebytestr(e))
358 stringutil.forcebytestr(e))
359 return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())]
359 return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())]
360
360
361 def _sizetomax(s):
361 def _sizetomax(s):
362 try:
362 try:
363 s = s.strip().lower()
363 s = s.strip().lower()
364 for k, v in util._sizeunits:
364 for k, v in util._sizeunits:
365 if s.endswith(k):
365 if s.endswith(k):
366 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
366 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
367 n = s[:-len(k)]
367 n = s[:-len(k)]
368 inc = 1.0
368 inc = 1.0
369 if "." in n:
369 if "." in n:
370 inc /= 10 ** len(n.split(".")[1])
370 inc /= 10 ** len(n.split(".")[1])
371 return int((float(n) + inc) * v) - 1
371 return int((float(n) + inc) * v) - 1
372 # no extension, this is a precise value
372 # no extension, this is a precise value
373 return int(s)
373 return int(s)
374 except ValueError:
374 except ValueError:
375 raise error.ParseError(_("couldn't parse size: %s") % s)
375 raise error.ParseError(_("couldn't parse size: %s") % s)
376
376
377 def sizematcher(x):
377 def sizematcher(x):
378 """Return a function(size) -> bool from the ``size()`` expression"""
378 """Return a function(size) -> bool from the ``size()`` expression"""
379
379
380 # i18n: "size" is a keyword
380 # i18n: "size" is a keyword
381 expr = getstring(x, _("size requires an expression")).strip()
381 expr = getstring(x, _("size requires an expression")).strip()
382 if '-' in expr: # do we have a range?
382 if '-' in expr: # do we have a range?
383 a, b = expr.split('-', 1)
383 a, b = expr.split('-', 1)
384 a = util.sizetoint(a)
384 a = util.sizetoint(a)
385 b = util.sizetoint(b)
385 b = util.sizetoint(b)
386 return lambda x: x >= a and x <= b
386 return lambda x: x >= a and x <= b
387 elif expr.startswith("<="):
387 elif expr.startswith("<="):
388 a = util.sizetoint(expr[2:])
388 a = util.sizetoint(expr[2:])
389 return lambda x: x <= a
389 return lambda x: x <= a
390 elif expr.startswith("<"):
390 elif expr.startswith("<"):
391 a = util.sizetoint(expr[1:])
391 a = util.sizetoint(expr[1:])
392 return lambda x: x < a
392 return lambda x: x < a
393 elif expr.startswith(">="):
393 elif expr.startswith(">="):
394 a = util.sizetoint(expr[2:])
394 a = util.sizetoint(expr[2:])
395 return lambda x: x >= a
395 return lambda x: x >= a
396 elif expr.startswith(">"):
396 elif expr.startswith(">"):
397 a = util.sizetoint(expr[1:])
397 a = util.sizetoint(expr[1:])
398 return lambda x: x > a
398 return lambda x: x > a
399 else:
399 else:
400 a = util.sizetoint(expr)
400 a = util.sizetoint(expr)
401 b = _sizetomax(expr)
401 b = _sizetomax(expr)
402 return lambda x: x >= a and x <= b
402 return lambda x: x >= a and x <= b
403
403
404 @predicate('size(expression)', callexisting=True)
404 @predicate('size(expression)', callexisting=True)
405 def size(mctx, x):
405 def size(mctx, x):
406 """File size matches the given expression. Examples:
406 """File size matches the given expression. Examples:
407
407
408 - size('1k') - files from 1024 to 2047 bytes
408 - size('1k') - files from 1024 to 2047 bytes
409 - size('< 20k') - files less than 20480 bytes
409 - size('< 20k') - files less than 20480 bytes
410 - size('>= .5MB') - files at least 524288 bytes
410 - size('>= .5MB') - files at least 524288 bytes
411 - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
411 - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
412 """
412 """
413 m = sizematcher(x)
413 m = sizematcher(x)
414 return [f for f in mctx.existing() if m(mctx.ctx[f].size())]
414 return [f for f in mctx.existing() if m(mctx.ctx[f].size())]
415
415
416 @predicate('encoding(name)', callexisting=True)
416 @predicate('encoding(name)', callexisting=True)
417 def encoding(mctx, x):
417 def encoding(mctx, x):
418 """File can be successfully decoded with the given character
418 """File can be successfully decoded with the given character
419 encoding. May not be useful for encodings other than ASCII and
419 encoding. May not be useful for encodings other than ASCII and
420 UTF-8.
420 UTF-8.
421 """
421 """
422
422
423 # i18n: "encoding" is a keyword
423 # i18n: "encoding" is a keyword
424 enc = getstring(x, _("encoding requires an encoding name"))
424 enc = getstring(x, _("encoding requires an encoding name"))
425
425
426 s = []
426 s = []
427 for f in mctx.existing():
427 for f in mctx.existing():
428 d = mctx.ctx[f].data()
428 d = mctx.ctx[f].data()
429 try:
429 try:
430 d.decode(pycompat.sysstr(enc))
430 d.decode(pycompat.sysstr(enc))
431 except LookupError:
431 except LookupError:
432 raise error.Abort(_("unknown encoding '%s'") % enc)
432 raise error.Abort(_("unknown encoding '%s'") % enc)
433 except UnicodeDecodeError:
433 except UnicodeDecodeError:
434 continue
434 continue
435 s.append(f)
435 s.append(f)
436
436
437 return s
437 return s
438
438
439 @predicate('eol(style)', callexisting=True)
439 @predicate('eol(style)', callexisting=True)
440 def eol(mctx, x):
440 def eol(mctx, x):
441 """File contains newlines of the given style (dos, unix, mac). Binary
441 """File contains newlines of the given style (dos, unix, mac). Binary
442 files are excluded, files with mixed line endings match multiple
442 files are excluded, files with mixed line endings match multiple
443 styles.
443 styles.
444 """
444 """
445
445
446 # i18n: "eol" is a keyword
446 # i18n: "eol" is a keyword
447 enc = getstring(x, _("eol requires a style name"))
447 enc = getstring(x, _("eol requires a style name"))
448
448
449 s = []
449 s = []
450 for f in mctx.existing():
450 for f in mctx.existing():
451 d = mctx.ctx[f].data()
451 d = mctx.ctx[f].data()
452 if stringutil.binary(d):
452 if stringutil.binary(d):
453 continue
453 continue
454 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
454 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
455 s.append(f)
455 s.append(f)
456 elif enc == 'unix' and re.search('(?<!\r)\n', d):
456 elif enc == 'unix' and re.search('(?<!\r)\n', d):
457 s.append(f)
457 s.append(f)
458 elif enc == 'mac' and re.search('\r(?!\n)', d):
458 elif enc == 'mac' and re.search('\r(?!\n)', d):
459 s.append(f)
459 s.append(f)
460 return s
460 return s
461
461
462 @predicate('copied()')
462 @predicate('copied()')
463 def copied(mctx, x):
463 def copied(mctx, x):
464 """File that is recorded as being copied.
464 """File that is recorded as being copied.
465 """
465 """
466 # i18n: "copied" is a keyword
466 # i18n: "copied" is a keyword
467 getargs(x, 0, 0, _("copied takes no arguments"))
467 getargs(x, 0, 0, _("copied takes no arguments"))
468 s = []
468 s = []
469 for f in mctx.subset:
469 for f in mctx.subset:
470 if f in mctx.ctx:
470 if f in mctx.ctx:
471 p = mctx.ctx[f].parents()
471 p = mctx.ctx[f].parents()
472 if p and p[0].path() != f:
472 if p and p[0].path() != f:
473 s.append(f)
473 s.append(f)
474 return s
474 return s
475
475
476 @predicate('revs(revs, pattern)')
476 @predicate('revs(revs, pattern)')
477 def revs(mctx, x):
477 def revs(mctx, x):
478 """Evaluate set in the specified revisions. If the revset match multiple
478 """Evaluate set in the specified revisions. If the revset match multiple
479 revs, this will return file matching pattern in any of the revision.
479 revs, this will return file matching pattern in any of the revision.
480 """
480 """
481 # i18n: "revs" is a keyword
481 # i18n: "revs" is a keyword
482 r, x = getargs(x, 2, 2, _("revs takes two arguments"))
482 r, x = getargs(x, 2, 2, _("revs takes two arguments"))
483 # i18n: "revs" is a keyword
483 # i18n: "revs" is a keyword
484 revspec = getstring(r, _("first argument to revs must be a revision"))
484 revspec = getstring(r, _("first argument to revs must be a revision"))
485 repo = mctx.ctx.repo()
485 repo = mctx.ctx.repo()
486 revs = scmutil.revrange(repo, [revspec])
486 revs = scmutil.revrange(repo, [revspec])
487
487
488 found = set()
488 found = set()
489 result = []
489 result = []
490 for r in revs:
490 for r in revs:
491 ctx = repo[r]
491 ctx = repo[r]
492 for f in getset(mctx.switch(ctx, _buildstatus(ctx, x)), x):
492 for f in getset(mctx.switch(ctx, _buildstatus(ctx, x)), x):
493 if f not in found:
493 if f not in found:
494 found.add(f)
494 found.add(f)
495 result.append(f)
495 result.append(f)
496 return result
496 return result
497
497
498 @predicate('status(base, rev, pattern)')
498 @predicate('status(base, rev, pattern)')
499 def status(mctx, x):
499 def status(mctx, x):
500 """Evaluate predicate using status change between ``base`` and
500 """Evaluate predicate using status change between ``base`` and
501 ``rev``. Examples:
501 ``rev``. Examples:
502
502
503 - ``status(3, 7, added())`` - matches files added from "3" to "7"
503 - ``status(3, 7, added())`` - matches files added from "3" to "7"
504 """
504 """
505 repo = mctx.ctx.repo()
505 repo = mctx.ctx.repo()
506 # i18n: "status" is a keyword
506 # i18n: "status" is a keyword
507 b, r, x = getargs(x, 3, 3, _("status takes three arguments"))
507 b, r, x = getargs(x, 3, 3, _("status takes three arguments"))
508 # i18n: "status" is a keyword
508 # i18n: "status" is a keyword
509 baseerr = _("first argument to status must be a revision")
509 baseerr = _("first argument to status must be a revision")
510 baserevspec = getstring(b, baseerr)
510 baserevspec = getstring(b, baseerr)
511 if not baserevspec:
511 if not baserevspec:
512 raise error.ParseError(baseerr)
512 raise error.ParseError(baseerr)
513 reverr = _("second argument to status must be a revision")
513 reverr = _("second argument to status must be a revision")
514 revspec = getstring(r, reverr)
514 revspec = getstring(r, reverr)
515 if not revspec:
515 if not revspec:
516 raise error.ParseError(reverr)
516 raise error.ParseError(reverr)
517 basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
517 basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
518 return getset(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x)
518 return getset(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x)
519
519
520 @predicate('subrepo([pattern])')
520 @predicate('subrepo([pattern])')
521 def subrepo(mctx, x):
521 def subrepo(mctx, x):
522 """Subrepositories whose paths match the given pattern.
522 """Subrepositories whose paths match the given pattern.
523 """
523 """
524 # i18n: "subrepo" is a keyword
524 # i18n: "subrepo" is a keyword
525 getargs(x, 0, 1, _("subrepo takes at most one argument"))
525 getargs(x, 0, 1, _("subrepo takes at most one argument"))
526 ctx = mctx.ctx
526 ctx = mctx.ctx
527 sstate = sorted(ctx.substate)
527 sstate = sorted(ctx.substate)
528 if x:
528 if x:
529 pat = getpattern(x, matchmod.allpatternkinds,
529 pat = getpattern(x, matchmod.allpatternkinds,
530 # i18n: "subrepo" is a keyword
530 # i18n: "subrepo" is a keyword
531 _("subrepo requires a pattern or no arguments"))
531 _("subrepo requires a pattern or no arguments"))
532 fast = not matchmod.patkind(pat)
532 fast = not matchmod.patkind(pat)
533 if fast:
533 if fast:
534 def m(s):
534 def m(s):
535 return (s == pat)
535 return (s == pat)
536 else:
536 else:
537 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
537 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
538 return [sub for sub in sstate if m(sub)]
538 return [sub for sub in sstate if m(sub)]
539 else:
539 else:
540 return [sub for sub in sstate]
540 return [sub for sub in sstate]
541
541
542 methods = {
542 methods = {
543 'string': stringset,
543 'string': stringset,
544 'symbol': stringset,
544 'symbol': stringset,
545 'kindpat': kindpatset,
545 'kindpat': kindpatset,
546 'and': andset,
546 'and': andset,
547 'or': orset,
547 'or': orset,
548 'minus': minusset,
548 'minus': minusset,
549 'negate': negateset,
549 'negate': negateset,
550 'list': listset,
550 'list': listset,
551 'group': getset,
551 'group': getset,
552 'not': notset,
552 'not': notset,
553 'func': func,
553 'func': func,
554 }
554 }
555
555
556 class matchctx(object):
556 class matchctx(object):
557 def __init__(self, ctx, subset, status=None):
557 def __init__(self, ctx, subset, status=None):
558 self.ctx = ctx
558 self.ctx = ctx
559 self.subset = subset
559 self.subset = subset
560 self._status = status
560 self._status = status
561 self._existingenabled = False
561 self._existingenabled = False
562 def status(self):
562 def status(self):
563 return self._status
563 return self._status
564 def matcher(self, patterns):
564 def matcher(self, patterns):
565 return self.ctx.match(patterns)
565 return self.ctx.match(patterns)
566 def filter(self, files):
566 def filter(self, files):
567 return [f for f in files if f in self.subset]
567 return [f for f in files if f in self.subset]
568 def existing(self):
568 def existing(self):
569 assert self._existingenabled, 'unexpected existing() invocation'
569 if not self._existingenabled:
570 raise error.ProgrammingError('unexpected existing() invocation')
570 if self._status is not None:
571 if self._status is not None:
571 removed = set(self._status[3])
572 removed = set(self._status[3])
572 unknown = set(self._status[4] + self._status[5])
573 unknown = set(self._status[4] + self._status[5])
573 else:
574 else:
574 removed = set()
575 removed = set()
575 unknown = set()
576 unknown = set()
576 return (f for f in self.subset
577 return (f for f in self.subset
577 if (f in self.ctx and f not in removed) or f in unknown)
578 if (f in self.ctx and f not in removed) or f in unknown)
578 def narrow(self, files):
579 def narrow(self, files):
579 return matchctx(self.ctx, self.filter(files), self._status)
580 return matchctx(self.ctx, self.filter(files), self._status)
580 def switch(self, ctx, status=None):
581 def switch(self, ctx, status=None):
581 subset = self.filter(_buildsubset(ctx, status))
582 subset = self.filter(_buildsubset(ctx, status))
582 return matchctx(ctx, subset, status)
583 return matchctx(ctx, subset, status)
583
584
584 class fullmatchctx(matchctx):
585 class fullmatchctx(matchctx):
585 """A match context where any files in any revisions should be valid"""
586 """A match context where any files in any revisions should be valid"""
586
587
587 def __init__(self, ctx, status=None):
588 def __init__(self, ctx, status=None):
588 subset = _buildsubset(ctx, status)
589 subset = _buildsubset(ctx, status)
589 super(fullmatchctx, self).__init__(ctx, subset, status)
590 super(fullmatchctx, self).__init__(ctx, subset, status)
590 def switch(self, ctx, status=None):
591 def switch(self, ctx, status=None):
591 return fullmatchctx(ctx, status)
592 return fullmatchctx(ctx, status)
592
593
593 # filesets using matchctx.switch()
594 # filesets using matchctx.switch()
594 _switchcallers = [
595 _switchcallers = [
595 'revs',
596 'revs',
596 'status',
597 'status',
597 ]
598 ]
598
599
599 def _intree(funcs, tree):
600 def _intree(funcs, tree):
600 if isinstance(tree, tuple):
601 if isinstance(tree, tuple):
601 if tree[0] == 'func' and tree[1][0] == 'symbol':
602 if tree[0] == 'func' and tree[1][0] == 'symbol':
602 if tree[1][1] in funcs:
603 if tree[1][1] in funcs:
603 return True
604 return True
604 if tree[1][1] in _switchcallers:
605 if tree[1][1] in _switchcallers:
605 # arguments won't be evaluated in the current context
606 # arguments won't be evaluated in the current context
606 return False
607 return False
607 for s in tree[1:]:
608 for s in tree[1:]:
608 if _intree(funcs, s):
609 if _intree(funcs, s):
609 return True
610 return True
610 return False
611 return False
611
612
612 def _buildsubset(ctx, status):
613 def _buildsubset(ctx, status):
613 if status:
614 if status:
614 subset = []
615 subset = []
615 for c in status:
616 for c in status:
616 subset.extend(c)
617 subset.extend(c)
617 return subset
618 return subset
618 else:
619 else:
619 return list(ctx.walk(ctx.match([])))
620 return list(ctx.walk(ctx.match([])))
620
621
621 def getfileset(ctx, expr):
622 def getfileset(ctx, expr):
622 tree = parse(expr)
623 tree = parse(expr)
623 return getset(fullmatchctx(ctx, _buildstatus(ctx, tree)), tree)
624 return getset(fullmatchctx(ctx, _buildstatus(ctx, tree)), tree)
624
625
625 def _buildstatus(ctx, tree, basectx=None):
626 def _buildstatus(ctx, tree, basectx=None):
626 # do we need status info?
627 # do we need status info?
627
628
628 # temporaty boolean to simplify the next conditional
629 # temporaty boolean to simplify the next conditional
629 purewdir = ctx.rev() is None and basectx is None
630 purewdir = ctx.rev() is None and basectx is None
630
631
631 if (_intree(_statuscallers, tree) or
632 if (_intree(_statuscallers, tree) or
632 # Using matchctx.existing() on a workingctx requires us to check
633 # Using matchctx.existing() on a workingctx requires us to check
633 # for deleted files.
634 # for deleted files.
634 (purewdir and _intree(_existingcallers, tree))):
635 (purewdir and _intree(_existingcallers, tree))):
635 unknown = _intree(['unknown'], tree)
636 unknown = _intree(['unknown'], tree)
636 ignored = _intree(['ignored'], tree)
637 ignored = _intree(['ignored'], tree)
637
638
638 r = ctx.repo()
639 r = ctx.repo()
639 if basectx is None:
640 if basectx is None:
640 basectx = ctx.p1()
641 basectx = ctx.p1()
641 return r.status(basectx, ctx,
642 return r.status(basectx, ctx,
642 unknown=unknown, ignored=ignored, clean=True)
643 unknown=unknown, ignored=ignored, clean=True)
643 else:
644 else:
644 return None
645 return None
645
646
646 def prettyformat(tree):
647 def prettyformat(tree):
647 return parser.prettyformat(tree, ('string', 'symbol'))
648 return parser.prettyformat(tree, ('string', 'symbol'))
648
649
649 def loadpredicate(ui, extname, registrarobj):
650 def loadpredicate(ui, extname, registrarobj):
650 """Load fileset predicates from specified registrarobj
651 """Load fileset predicates from specified registrarobj
651 """
652 """
652 for name, func in registrarobj._table.iteritems():
653 for name, func in registrarobj._table.iteritems():
653 symbols[name] = func
654 symbols[name] = func
654 if func._callstatus:
655 if func._callstatus:
655 _statuscallers.add(name)
656 _statuscallers.add(name)
656 if func._callexisting:
657 if func._callexisting:
657 _existingcallers.add(name)
658 _existingcallers.add(name)
658
659
659 # load built-in predicates explicitly to setup _statuscallers/_existingcallers
660 # load built-in predicates explicitly to setup _statuscallers/_existingcallers
660 loadpredicate(None, None, predicate)
661 loadpredicate(None, None, predicate)
661
662
662 # tell hggettext to extract docstrings from these functions:
663 # tell hggettext to extract docstrings from these functions:
663 i18nfunctions = symbols.values()
664 i18nfunctions = symbols.values()
@@ -1,676 +1,676 b''
1 $ fileset() {
1 $ fileset() {
2 > hg debugfileset "$@"
2 > hg debugfileset "$@"
3 > }
3 > }
4
4
5 $ hg init repo
5 $ hg init repo
6 $ cd repo
6 $ cd repo
7 $ echo a > a1
7 $ echo a > a1
8 $ echo a > a2
8 $ echo a > a2
9 $ echo b > b1
9 $ echo b > b1
10 $ echo b > b2
10 $ echo b > b2
11 $ hg ci -Am addfiles
11 $ hg ci -Am addfiles
12 adding a1
12 adding a1
13 adding a2
13 adding a2
14 adding b1
14 adding b1
15 adding b2
15 adding b2
16
16
17 Test operators and basic patterns
17 Test operators and basic patterns
18
18
19 $ fileset -v a1
19 $ fileset -v a1
20 (symbol 'a1')
20 (symbol 'a1')
21 a1
21 a1
22 $ fileset -v 'a*'
22 $ fileset -v 'a*'
23 (symbol 'a*')
23 (symbol 'a*')
24 a1
24 a1
25 a2
25 a2
26 $ fileset -v '"re:a\d"'
26 $ fileset -v '"re:a\d"'
27 (string 're:a\\d')
27 (string 're:a\\d')
28 a1
28 a1
29 a2
29 a2
30 $ fileset -v '!re:"a\d"'
30 $ fileset -v '!re:"a\d"'
31 (not
31 (not
32 (kindpat
32 (kindpat
33 (symbol 're')
33 (symbol 're')
34 (string 'a\\d')))
34 (string 'a\\d')))
35 b1
35 b1
36 b2
36 b2
37 $ fileset -v 'path:a1 or glob:b?'
37 $ fileset -v 'path:a1 or glob:b?'
38 (or
38 (or
39 (kindpat
39 (kindpat
40 (symbol 'path')
40 (symbol 'path')
41 (symbol 'a1'))
41 (symbol 'a1'))
42 (kindpat
42 (kindpat
43 (symbol 'glob')
43 (symbol 'glob')
44 (symbol 'b?')))
44 (symbol 'b?')))
45 a1
45 a1
46 b1
46 b1
47 b2
47 b2
48 $ fileset -v 'a1 or a2'
48 $ fileset -v 'a1 or a2'
49 (or
49 (or
50 (symbol 'a1')
50 (symbol 'a1')
51 (symbol 'a2'))
51 (symbol 'a2'))
52 a1
52 a1
53 a2
53 a2
54 $ fileset 'a1 | a2'
54 $ fileset 'a1 | a2'
55 a1
55 a1
56 a2
56 a2
57 $ fileset 'a* and "*1"'
57 $ fileset 'a* and "*1"'
58 a1
58 a1
59 $ fileset 'a* & "*1"'
59 $ fileset 'a* & "*1"'
60 a1
60 a1
61 $ fileset 'not (r"a*")'
61 $ fileset 'not (r"a*")'
62 b1
62 b1
63 b2
63 b2
64 $ fileset '! ("a*")'
64 $ fileset '! ("a*")'
65 b1
65 b1
66 b2
66 b2
67 $ fileset 'a* - a1'
67 $ fileset 'a* - a1'
68 a2
68 a2
69 $ fileset 'a_b'
69 $ fileset 'a_b'
70 $ fileset '"\xy"'
70 $ fileset '"\xy"'
71 hg: parse error: invalid \x escape
71 hg: parse error: invalid \x escape
72 [255]
72 [255]
73
73
74 Test invalid syntax
74 Test invalid syntax
75
75
76 $ fileset -v '"added"()'
76 $ fileset -v '"added"()'
77 (func
77 (func
78 (string 'added')
78 (string 'added')
79 None)
79 None)
80 hg: parse error: not a symbol
80 hg: parse error: not a symbol
81 [255]
81 [255]
82 $ fileset -v '()()'
82 $ fileset -v '()()'
83 (func
83 (func
84 (group
84 (group
85 None)
85 None)
86 None)
86 None)
87 hg: parse error: not a symbol
87 hg: parse error: not a symbol
88 [255]
88 [255]
89 $ fileset -v -- '-x'
89 $ fileset -v -- '-x'
90 (negate
90 (negate
91 (symbol 'x'))
91 (symbol 'x'))
92 hg: parse error: can't use negate operator in this context
92 hg: parse error: can't use negate operator in this context
93 [255]
93 [255]
94 $ fileset -v -- '-()'
94 $ fileset -v -- '-()'
95 (negate
95 (negate
96 (group
96 (group
97 None))
97 None))
98 hg: parse error: can't use negate operator in this context
98 hg: parse error: can't use negate operator in this context
99 [255]
99 [255]
100
100
101 $ fileset '"path":.'
101 $ fileset '"path":.'
102 hg: parse error: not a symbol
102 hg: parse error: not a symbol
103 [255]
103 [255]
104 $ fileset 'path:foo bar'
104 $ fileset 'path:foo bar'
105 hg: parse error at 9: invalid token
105 hg: parse error at 9: invalid token
106 [255]
106 [255]
107 $ fileset 'foo:bar:baz'
107 $ fileset 'foo:bar:baz'
108 hg: parse error: not a symbol
108 hg: parse error: not a symbol
109 [255]
109 [255]
110 $ fileset 'foo:bar()'
110 $ fileset 'foo:bar()'
111 hg: parse error: pattern must be a string
111 hg: parse error: pattern must be a string
112 [255]
112 [255]
113 $ fileset 'foo:bar'
113 $ fileset 'foo:bar'
114 hg: parse error: invalid pattern kind: foo
114 hg: parse error: invalid pattern kind: foo
115 [255]
115 [255]
116
116
117 Test files status
117 Test files status
118
118
119 $ rm a1
119 $ rm a1
120 $ hg rm a2
120 $ hg rm a2
121 $ echo b >> b2
121 $ echo b >> b2
122 $ hg cp b1 c1
122 $ hg cp b1 c1
123 $ echo c > c2
123 $ echo c > c2
124 $ echo c > c3
124 $ echo c > c3
125 $ cat > .hgignore <<EOF
125 $ cat > .hgignore <<EOF
126 > \.hgignore
126 > \.hgignore
127 > 2$
127 > 2$
128 > EOF
128 > EOF
129 $ fileset 'modified()'
129 $ fileset 'modified()'
130 b2
130 b2
131 $ fileset 'added()'
131 $ fileset 'added()'
132 c1
132 c1
133 $ fileset 'removed()'
133 $ fileset 'removed()'
134 a2
134 a2
135 $ fileset 'deleted()'
135 $ fileset 'deleted()'
136 a1
136 a1
137 $ fileset 'missing()'
137 $ fileset 'missing()'
138 a1
138 a1
139 $ fileset 'unknown()'
139 $ fileset 'unknown()'
140 c3
140 c3
141 $ fileset 'ignored()'
141 $ fileset 'ignored()'
142 .hgignore
142 .hgignore
143 c2
143 c2
144 $ fileset 'hgignore()'
144 $ fileset 'hgignore()'
145 a2
145 a2
146 b2
146 b2
147 $ fileset 'clean()'
147 $ fileset 'clean()'
148 b1
148 b1
149 $ fileset 'copied()'
149 $ fileset 'copied()'
150 c1
150 c1
151
151
152 Test files status in different revisions
152 Test files status in different revisions
153
153
154 $ hg status -m
154 $ hg status -m
155 M b2
155 M b2
156 $ fileset -r0 'revs("wdir()", modified())' --traceback
156 $ fileset -r0 'revs("wdir()", modified())' --traceback
157 b2
157 b2
158 $ hg status -a
158 $ hg status -a
159 A c1
159 A c1
160 $ fileset -r0 'revs("wdir()", added())'
160 $ fileset -r0 'revs("wdir()", added())'
161 c1
161 c1
162 $ hg status --change 0 -a
162 $ hg status --change 0 -a
163 A a1
163 A a1
164 A a2
164 A a2
165 A b1
165 A b1
166 A b2
166 A b2
167 $ hg status -mru
167 $ hg status -mru
168 M b2
168 M b2
169 R a2
169 R a2
170 ? c3
170 ? c3
171 $ fileset -r0 'added() and revs("wdir()", modified() or removed() or unknown())'
171 $ fileset -r0 'added() and revs("wdir()", modified() or removed() or unknown())'
172 b2
172 b2
173 a2
173 a2
174 $ fileset -r0 'added() or revs("wdir()", added())'
174 $ fileset -r0 'added() or revs("wdir()", added())'
175 a1
175 a1
176 a2
176 a2
177 b1
177 b1
178 b2
178 b2
179 c1
179 c1
180
180
181 Test files properties
181 Test files properties
182
182
183 >>> open('bin', 'wb').write(b'\0a') and None
183 >>> open('bin', 'wb').write(b'\0a') and None
184 $ fileset 'binary()'
184 $ fileset 'binary()'
185 $ fileset 'binary() and unknown()'
185 $ fileset 'binary() and unknown()'
186 bin
186 bin
187 $ echo '^bin$' >> .hgignore
187 $ echo '^bin$' >> .hgignore
188 $ fileset 'binary() and ignored()'
188 $ fileset 'binary() and ignored()'
189 bin
189 bin
190 $ hg add bin
190 $ hg add bin
191 $ fileset 'binary()'
191 $ fileset 'binary()'
192 bin
192 bin
193
193
194 $ fileset 'grep("b{1}")'
194 $ fileset 'grep("b{1}")'
195 b2
195 b2
196 c1
196 c1
197 b1
197 b1
198 $ fileset 'grep("missingparens(")'
198 $ fileset 'grep("missingparens(")'
199 hg: parse error: invalid match pattern: unbalanced parenthesis
199 hg: parse error: invalid match pattern: unbalanced parenthesis
200 [255]
200 [255]
201
201
202 #if execbit
202 #if execbit
203 $ chmod +x b2
203 $ chmod +x b2
204 $ fileset 'exec()'
204 $ fileset 'exec()'
205 b2
205 b2
206 #endif
206 #endif
207
207
208 #if symlink
208 #if symlink
209 $ ln -s b2 b2link
209 $ ln -s b2 b2link
210 $ fileset 'symlink() and unknown()'
210 $ fileset 'symlink() and unknown()'
211 b2link
211 b2link
212 $ hg add b2link
212 $ hg add b2link
213 #endif
213 #endif
214
214
215 #if no-windows
215 #if no-windows
216 $ echo foo > con.xml
216 $ echo foo > con.xml
217 $ fileset 'not portable()'
217 $ fileset 'not portable()'
218 con.xml
218 con.xml
219 $ hg --config ui.portablefilenames=ignore add con.xml
219 $ hg --config ui.portablefilenames=ignore add con.xml
220 #endif
220 #endif
221
221
222 >>> open('1k', 'wb').write(b' '*1024) and None
222 >>> open('1k', 'wb').write(b' '*1024) and None
223 >>> open('2k', 'wb').write(b' '*2048) and None
223 >>> open('2k', 'wb').write(b' '*2048) and None
224 $ hg add 1k 2k
224 $ hg add 1k 2k
225 $ fileset 'size("bar")'
225 $ fileset 'size("bar")'
226 hg: parse error: couldn't parse size: bar
226 hg: parse error: couldn't parse size: bar
227 [255]
227 [255]
228 $ fileset '(1k, 2k)'
228 $ fileset '(1k, 2k)'
229 hg: parse error: can't use a list in this context
229 hg: parse error: can't use a list in this context
230 (see hg help "filesets.x or y")
230 (see hg help "filesets.x or y")
231 [255]
231 [255]
232 $ fileset 'size(1k)'
232 $ fileset 'size(1k)'
233 1k
233 1k
234 $ fileset '(1k or 2k) and size("< 2k")'
234 $ fileset '(1k or 2k) and size("< 2k")'
235 1k
235 1k
236 $ fileset '(1k or 2k) and size("<=2k")'
236 $ fileset '(1k or 2k) and size("<=2k")'
237 1k
237 1k
238 2k
238 2k
239 $ fileset '(1k or 2k) and size("> 1k")'
239 $ fileset '(1k or 2k) and size("> 1k")'
240 2k
240 2k
241 $ fileset '(1k or 2k) and size(">=1K")'
241 $ fileset '(1k or 2k) and size(">=1K")'
242 1k
242 1k
243 2k
243 2k
244 $ fileset '(1k or 2k) and size(".5KB - 1.5kB")'
244 $ fileset '(1k or 2k) and size(".5KB - 1.5kB")'
245 1k
245 1k
246 $ fileset 'size("1M")'
246 $ fileset 'size("1M")'
247 $ fileset 'size("1 GB")'
247 $ fileset 'size("1 GB")'
248
248
249 Test merge states
249 Test merge states
250
250
251 $ hg ci -m manychanges
251 $ hg ci -m manychanges
252 $ hg file -r . 'set:copied() & modified()'
252 $ hg file -r . 'set:copied() & modified()'
253 [1]
253 [1]
254 $ hg up -C 0
254 $ hg up -C 0
255 * files updated, 0 files merged, * files removed, 0 files unresolved (glob)
255 * files updated, 0 files merged, * files removed, 0 files unresolved (glob)
256 $ echo c >> b2
256 $ echo c >> b2
257 $ hg ci -m diverging b2
257 $ hg ci -m diverging b2
258 created new head
258 created new head
259 $ fileset 'resolved()'
259 $ fileset 'resolved()'
260 $ fileset 'unresolved()'
260 $ fileset 'unresolved()'
261 $ hg merge
261 $ hg merge
262 merging b2
262 merging b2
263 warning: conflicts while merging b2! (edit, then use 'hg resolve --mark')
263 warning: conflicts while merging b2! (edit, then use 'hg resolve --mark')
264 * files updated, 0 files merged, 1 files removed, 1 files unresolved (glob)
264 * files updated, 0 files merged, 1 files removed, 1 files unresolved (glob)
265 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
265 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
266 [1]
266 [1]
267 $ fileset 'resolved()'
267 $ fileset 'resolved()'
268 $ fileset 'unresolved()'
268 $ fileset 'unresolved()'
269 b2
269 b2
270 $ echo e > b2
270 $ echo e > b2
271 $ hg resolve -m b2
271 $ hg resolve -m b2
272 (no more unresolved files)
272 (no more unresolved files)
273 $ fileset 'resolved()'
273 $ fileset 'resolved()'
274 b2
274 b2
275 $ fileset 'unresolved()'
275 $ fileset 'unresolved()'
276 $ hg ci -m merge
276 $ hg ci -m merge
277
277
278 Test subrepo predicate
278 Test subrepo predicate
279
279
280 $ hg init sub
280 $ hg init sub
281 $ echo a > sub/suba
281 $ echo a > sub/suba
282 $ hg -R sub add sub/suba
282 $ hg -R sub add sub/suba
283 $ hg -R sub ci -m sub
283 $ hg -R sub ci -m sub
284 $ echo 'sub = sub' > .hgsub
284 $ echo 'sub = sub' > .hgsub
285 $ hg init sub2
285 $ hg init sub2
286 $ echo b > sub2/b
286 $ echo b > sub2/b
287 $ hg -R sub2 ci -Am sub2
287 $ hg -R sub2 ci -Am sub2
288 adding b
288 adding b
289 $ echo 'sub2 = sub2' >> .hgsub
289 $ echo 'sub2 = sub2' >> .hgsub
290 $ fileset 'subrepo()'
290 $ fileset 'subrepo()'
291 $ hg add .hgsub
291 $ hg add .hgsub
292 $ fileset 'subrepo()'
292 $ fileset 'subrepo()'
293 sub
293 sub
294 sub2
294 sub2
295 $ fileset 'subrepo("sub")'
295 $ fileset 'subrepo("sub")'
296 sub
296 sub
297 $ fileset 'subrepo("glob:*")'
297 $ fileset 'subrepo("glob:*")'
298 sub
298 sub
299 sub2
299 sub2
300 $ hg ci -m subrepo
300 $ hg ci -m subrepo
301
301
302 Test that .hgsubstate is updated as appropriate during a conversion. The
302 Test that .hgsubstate is updated as appropriate during a conversion. The
303 saverev property is enough to alter the hashes of the subrepo.
303 saverev property is enough to alter the hashes of the subrepo.
304
304
305 $ hg init ../converted
305 $ hg init ../converted
306 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
306 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
307 > sub ../converted/sub
307 > sub ../converted/sub
308 initializing destination ../converted/sub repository
308 initializing destination ../converted/sub repository
309 scanning source...
309 scanning source...
310 sorting...
310 sorting...
311 converting...
311 converting...
312 0 sub
312 0 sub
313 $ hg clone -U sub2 ../converted/sub2
313 $ hg clone -U sub2 ../converted/sub2
314 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
314 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
315 > . ../converted
315 > . ../converted
316 scanning source...
316 scanning source...
317 sorting...
317 sorting...
318 converting...
318 converting...
319 4 addfiles
319 4 addfiles
320 3 manychanges
320 3 manychanges
321 2 diverging
321 2 diverging
322 1 merge
322 1 merge
323 0 subrepo
323 0 subrepo
324 no ".hgsubstate" updates will be made for "sub2"
324 no ".hgsubstate" updates will be made for "sub2"
325 $ hg up -q -R ../converted -r tip
325 $ hg up -q -R ../converted -r tip
326 $ hg --cwd ../converted cat sub/suba sub2/b -r tip
326 $ hg --cwd ../converted cat sub/suba sub2/b -r tip
327 a
327 a
328 b
328 b
329 $ oldnode=`hg log -r tip -T "{node}\n"`
329 $ oldnode=`hg log -r tip -T "{node}\n"`
330 $ newnode=`hg log -R ../converted -r tip -T "{node}\n"`
330 $ newnode=`hg log -R ../converted -r tip -T "{node}\n"`
331 $ [ "$oldnode" != "$newnode" ] || echo "nothing changed"
331 $ [ "$oldnode" != "$newnode" ] || echo "nothing changed"
332
332
333 Test with a revision
333 Test with a revision
334
334
335 $ hg log -G --template '{rev} {desc}\n'
335 $ hg log -G --template '{rev} {desc}\n'
336 @ 4 subrepo
336 @ 4 subrepo
337 |
337 |
338 o 3 merge
338 o 3 merge
339 |\
339 |\
340 | o 2 diverging
340 | o 2 diverging
341 | |
341 | |
342 o | 1 manychanges
342 o | 1 manychanges
343 |/
343 |/
344 o 0 addfiles
344 o 0 addfiles
345
345
346 $ echo unknown > unknown
346 $ echo unknown > unknown
347 $ fileset -r1 'modified()'
347 $ fileset -r1 'modified()'
348 b2
348 b2
349 $ fileset -r1 'added() and c1'
349 $ fileset -r1 'added() and c1'
350 c1
350 c1
351 $ fileset -r1 'removed()'
351 $ fileset -r1 'removed()'
352 a2
352 a2
353 $ fileset -r1 'deleted()'
353 $ fileset -r1 'deleted()'
354 $ fileset -r1 'unknown()'
354 $ fileset -r1 'unknown()'
355 $ fileset -r1 'ignored()'
355 $ fileset -r1 'ignored()'
356 $ fileset -r1 'hgignore()'
356 $ fileset -r1 'hgignore()'
357 b2
357 b2
358 bin
358 bin
359 $ fileset -r1 'binary()'
359 $ fileset -r1 'binary()'
360 bin
360 bin
361 $ fileset -r1 'size(1k)'
361 $ fileset -r1 'size(1k)'
362 1k
362 1k
363 $ fileset -r3 'resolved()'
363 $ fileset -r3 'resolved()'
364 $ fileset -r3 'unresolved()'
364 $ fileset -r3 'unresolved()'
365
365
366 #if execbit
366 #if execbit
367 $ fileset -r1 'exec()'
367 $ fileset -r1 'exec()'
368 b2
368 b2
369 #endif
369 #endif
370
370
371 #if symlink
371 #if symlink
372 $ fileset -r1 'symlink()'
372 $ fileset -r1 'symlink()'
373 b2link
373 b2link
374 #endif
374 #endif
375
375
376 #if no-windows
376 #if no-windows
377 $ fileset -r1 'not portable()'
377 $ fileset -r1 'not portable()'
378 con.xml
378 con.xml
379 $ hg forget 'con.xml'
379 $ hg forget 'con.xml'
380 #endif
380 #endif
381
381
382 $ fileset -r4 'subrepo("re:su.*")'
382 $ fileset -r4 'subrepo("re:su.*")'
383 sub
383 sub
384 sub2
384 sub2
385 $ fileset -r4 'subrepo(re:su.*)'
385 $ fileset -r4 'subrepo(re:su.*)'
386 sub
386 sub
387 sub2
387 sub2
388 $ fileset -r4 'subrepo("sub")'
388 $ fileset -r4 'subrepo("sub")'
389 sub
389 sub
390 $ fileset -r4 'b2 or c1'
390 $ fileset -r4 'b2 or c1'
391 b2
391 b2
392 c1
392 c1
393
393
394 >>> open('dos', 'wb').write(b"dos\r\n") and None
394 >>> open('dos', 'wb').write(b"dos\r\n") and None
395 >>> open('mixed', 'wb').write(b"dos\r\nunix\n") and None
395 >>> open('mixed', 'wb').write(b"dos\r\nunix\n") and None
396 >>> open('mac', 'wb').write(b"mac\r") and None
396 >>> open('mac', 'wb').write(b"mac\r") and None
397 $ hg add dos mixed mac
397 $ hg add dos mixed mac
398
398
399 (remove a1, to examine safety of 'eol' on removed files)
399 (remove a1, to examine safety of 'eol' on removed files)
400 $ rm a1
400 $ rm a1
401
401
402 $ fileset 'eol(dos)'
402 $ fileset 'eol(dos)'
403 dos
403 dos
404 mixed
404 mixed
405 $ fileset 'eol(unix)'
405 $ fileset 'eol(unix)'
406 mixed
406 mixed
407 .hgsub
407 .hgsub
408 .hgsubstate
408 .hgsubstate
409 b1
409 b1
410 b2
410 b2
411 c1
411 c1
412 $ fileset 'eol(mac)'
412 $ fileset 'eol(mac)'
413 mac
413 mac
414
414
415 Test safety of 'encoding' on removed files
415 Test safety of 'encoding' on removed files
416
416
417 $ fileset 'encoding("ascii")'
417 $ fileset 'encoding("ascii")'
418 dos
418 dos
419 mac
419 mac
420 mixed
420 mixed
421 .hgsub
421 .hgsub
422 .hgsubstate
422 .hgsubstate
423 1k
423 1k
424 2k
424 2k
425 b1
425 b1
426 b2
426 b2
427 b2link (symlink !)
427 b2link (symlink !)
428 bin
428 bin
429 c1
429 c1
430
430
431 Test detection of unintentional 'matchctx.existing()' invocation
431 Test detection of unintentional 'matchctx.existing()' invocation
432
432
433 $ cat > $TESTTMP/existingcaller.py <<EOF
433 $ cat > $TESTTMP/existingcaller.py <<EOF
434 > from mercurial import registrar
434 > from mercurial import registrar
435 >
435 >
436 > filesetpredicate = registrar.filesetpredicate()
436 > filesetpredicate = registrar.filesetpredicate()
437 > @filesetpredicate(b'existingcaller()', callexisting=False)
437 > @filesetpredicate(b'existingcaller()', callexisting=False)
438 > def existingcaller(mctx, x):
438 > def existingcaller(mctx, x):
439 > # this 'mctx.existing()' invocation is unintentional
439 > # this 'mctx.existing()' invocation is unintentional
440 > return [f for f in mctx.existing()]
440 > return [f for f in mctx.existing()]
441 > EOF
441 > EOF
442
442
443 $ cat >> .hg/hgrc <<EOF
443 $ cat >> .hg/hgrc <<EOF
444 > [extensions]
444 > [extensions]
445 > existingcaller = $TESTTMP/existingcaller.py
445 > existingcaller = $TESTTMP/existingcaller.py
446 > EOF
446 > EOF
447
447
448 $ fileset 'existingcaller()' 2>&1 | tail -1
448 $ fileset 'existingcaller()' 2>&1 | tail -1
449 AssertionError: unexpected existing() invocation
449 *ProgrammingError: *unexpected existing() invocation* (glob)
450
450
451 Test 'revs(...)'
451 Test 'revs(...)'
452 ================
452 ================
453
453
454 small reminder of the repository state
454 small reminder of the repository state
455
455
456 $ hg log -G
456 $ hg log -G
457 @ changeset: 4:* (glob)
457 @ changeset: 4:* (glob)
458 | tag: tip
458 | tag: tip
459 | user: test
459 | user: test
460 | date: Thu Jan 01 00:00:00 1970 +0000
460 | date: Thu Jan 01 00:00:00 1970 +0000
461 | summary: subrepo
461 | summary: subrepo
462 |
462 |
463 o changeset: 3:* (glob)
463 o changeset: 3:* (glob)
464 |\ parent: 2:55b05bdebf36
464 |\ parent: 2:55b05bdebf36
465 | | parent: 1:* (glob)
465 | | parent: 1:* (glob)
466 | | user: test
466 | | user: test
467 | | date: Thu Jan 01 00:00:00 1970 +0000
467 | | date: Thu Jan 01 00:00:00 1970 +0000
468 | | summary: merge
468 | | summary: merge
469 | |
469 | |
470 | o changeset: 2:55b05bdebf36
470 | o changeset: 2:55b05bdebf36
471 | | parent: 0:8a9576c51c1f
471 | | parent: 0:8a9576c51c1f
472 | | user: test
472 | | user: test
473 | | date: Thu Jan 01 00:00:00 1970 +0000
473 | | date: Thu Jan 01 00:00:00 1970 +0000
474 | | summary: diverging
474 | | summary: diverging
475 | |
475 | |
476 o | changeset: 1:* (glob)
476 o | changeset: 1:* (glob)
477 |/ user: test
477 |/ user: test
478 | date: Thu Jan 01 00:00:00 1970 +0000
478 | date: Thu Jan 01 00:00:00 1970 +0000
479 | summary: manychanges
479 | summary: manychanges
480 |
480 |
481 o changeset: 0:8a9576c51c1f
481 o changeset: 0:8a9576c51c1f
482 user: test
482 user: test
483 date: Thu Jan 01 00:00:00 1970 +0000
483 date: Thu Jan 01 00:00:00 1970 +0000
484 summary: addfiles
484 summary: addfiles
485
485
486 $ hg status --change 0
486 $ hg status --change 0
487 A a1
487 A a1
488 A a2
488 A a2
489 A b1
489 A b1
490 A b2
490 A b2
491 $ hg status --change 1
491 $ hg status --change 1
492 M b2
492 M b2
493 A 1k
493 A 1k
494 A 2k
494 A 2k
495 A b2link (no-windows !)
495 A b2link (no-windows !)
496 A bin
496 A bin
497 A c1
497 A c1
498 A con.xml (no-windows !)
498 A con.xml (no-windows !)
499 R a2
499 R a2
500 $ hg status --change 2
500 $ hg status --change 2
501 M b2
501 M b2
502 $ hg status --change 3
502 $ hg status --change 3
503 M b2
503 M b2
504 A 1k
504 A 1k
505 A 2k
505 A 2k
506 A b2link (no-windows !)
506 A b2link (no-windows !)
507 A bin
507 A bin
508 A c1
508 A c1
509 A con.xml (no-windows !)
509 A con.xml (no-windows !)
510 R a2
510 R a2
511 $ hg status --change 4
511 $ hg status --change 4
512 A .hgsub
512 A .hgsub
513 A .hgsubstate
513 A .hgsubstate
514 $ hg status
514 $ hg status
515 A dos
515 A dos
516 A mac
516 A mac
517 A mixed
517 A mixed
518 R con.xml (no-windows !)
518 R con.xml (no-windows !)
519 ! a1
519 ! a1
520 ? b2.orig
520 ? b2.orig
521 ? c3
521 ? c3
522 ? unknown
522 ? unknown
523
523
524 Test files at -r0 should be filtered by files at wdir
524 Test files at -r0 should be filtered by files at wdir
525 -----------------------------------------------------
525 -----------------------------------------------------
526
526
527 $ fileset -r0 '* and revs("wdir()", *)'
527 $ fileset -r0 '* and revs("wdir()", *)'
528 a1
528 a1
529 b1
529 b1
530 b2
530 b2
531
531
532 Test that "revs()" work at all
532 Test that "revs()" work at all
533 ------------------------------
533 ------------------------------
534
534
535 $ fileset "revs('2', modified())"
535 $ fileset "revs('2', modified())"
536 b2
536 b2
537
537
538 Test that "revs()" work for file missing in the working copy/current context
538 Test that "revs()" work for file missing in the working copy/current context
539 ----------------------------------------------------------------------------
539 ----------------------------------------------------------------------------
540
540
541 (a2 not in working copy)
541 (a2 not in working copy)
542
542
543 $ fileset "revs('0', added())"
543 $ fileset "revs('0', added())"
544 a1
544 a1
545 a2
545 a2
546 b1
546 b1
547 b2
547 b2
548
548
549 (none of the file exist in "0")
549 (none of the file exist in "0")
550
550
551 $ fileset -r 0 "revs('4', added())"
551 $ fileset -r 0 "revs('4', added())"
552 .hgsub
552 .hgsub
553 .hgsubstate
553 .hgsubstate
554
554
555 Call with empty revset
555 Call with empty revset
556 --------------------------
556 --------------------------
557
557
558 $ fileset "revs('2-2', modified())"
558 $ fileset "revs('2-2', modified())"
559
559
560 Call with revset matching multiple revs
560 Call with revset matching multiple revs
561 ---------------------------------------
561 ---------------------------------------
562
562
563 $ fileset "revs('0+4', added())"
563 $ fileset "revs('0+4', added())"
564 a1
564 a1
565 a2
565 a2
566 b1
566 b1
567 b2
567 b2
568 .hgsub
568 .hgsub
569 .hgsubstate
569 .hgsubstate
570
570
571 overlapping set
571 overlapping set
572
572
573 $ fileset "revs('1+2', modified())"
573 $ fileset "revs('1+2', modified())"
574 b2
574 b2
575
575
576 test 'status(...)'
576 test 'status(...)'
577 =================
577 =================
578
578
579 Simple case
579 Simple case
580 -----------
580 -----------
581
581
582 $ fileset "status(3, 4, added())"
582 $ fileset "status(3, 4, added())"
583 .hgsub
583 .hgsub
584 .hgsubstate
584 .hgsubstate
585
585
586 use rev to restrict matched file
586 use rev to restrict matched file
587 -----------------------------------------
587 -----------------------------------------
588
588
589 $ hg status --removed --rev 0 --rev 1
589 $ hg status --removed --rev 0 --rev 1
590 R a2
590 R a2
591 $ fileset "status(0, 1, removed())"
591 $ fileset "status(0, 1, removed())"
592 a2
592 a2
593 $ fileset "* and status(0, 1, removed())"
593 $ fileset "* and status(0, 1, removed())"
594 $ fileset -r 4 "status(0, 1, removed())"
594 $ fileset -r 4 "status(0, 1, removed())"
595 a2
595 a2
596 $ fileset -r 4 "* and status(0, 1, removed())"
596 $ fileset -r 4 "* and status(0, 1, removed())"
597 $ fileset "revs('4', * and status(0, 1, removed()))"
597 $ fileset "revs('4', * and status(0, 1, removed()))"
598 $ fileset "revs('0', * and status(0, 1, removed()))"
598 $ fileset "revs('0', * and status(0, 1, removed()))"
599 a2
599 a2
600
600
601 check wdir()
601 check wdir()
602 ------------
602 ------------
603
603
604 $ hg status --removed --rev 4
604 $ hg status --removed --rev 4
605 R con.xml (no-windows !)
605 R con.xml (no-windows !)
606 $ fileset "status(4, 'wdir()', removed())"
606 $ fileset "status(4, 'wdir()', removed())"
607 con.xml (no-windows !)
607 con.xml (no-windows !)
608
608
609 $ hg status --removed --rev 2
609 $ hg status --removed --rev 2
610 R a2
610 R a2
611 $ fileset "status('2', 'wdir()', removed())"
611 $ fileset "status('2', 'wdir()', removed())"
612 a2
612 a2
613
613
614 test backward status
614 test backward status
615 --------------------
615 --------------------
616
616
617 $ hg status --removed --rev 0 --rev 4
617 $ hg status --removed --rev 0 --rev 4
618 R a2
618 R a2
619 $ hg status --added --rev 4 --rev 0
619 $ hg status --added --rev 4 --rev 0
620 A a2
620 A a2
621 $ fileset "status(4, 0, added())"
621 $ fileset "status(4, 0, added())"
622 a2
622 a2
623
623
624 test cross branch status
624 test cross branch status
625 ------------------------
625 ------------------------
626
626
627 $ hg status --added --rev 1 --rev 2
627 $ hg status --added --rev 1 --rev 2
628 A a2
628 A a2
629 $ fileset "status(1, 2, added())"
629 $ fileset "status(1, 2, added())"
630 a2
630 a2
631
631
632 test with multi revs revset
632 test with multi revs revset
633 ---------------------------
633 ---------------------------
634 $ hg status --added --rev 0:1 --rev 3:4
634 $ hg status --added --rev 0:1 --rev 3:4
635 A .hgsub
635 A .hgsub
636 A .hgsubstate
636 A .hgsubstate
637 A 1k
637 A 1k
638 A 2k
638 A 2k
639 A b2link (no-windows !)
639 A b2link (no-windows !)
640 A bin
640 A bin
641 A c1
641 A c1
642 A con.xml (no-windows !)
642 A con.xml (no-windows !)
643 $ fileset "status('0:1', '3:4', added())"
643 $ fileset "status('0:1', '3:4', added())"
644 .hgsub
644 .hgsub
645 .hgsubstate
645 .hgsubstate
646 1k
646 1k
647 2k
647 2k
648 b2link (no-windows !)
648 b2link (no-windows !)
649 bin
649 bin
650 c1
650 c1
651 con.xml (no-windows !)
651 con.xml (no-windows !)
652
652
653 tests with empty value
653 tests with empty value
654 ----------------------
654 ----------------------
655
655
656 Fully empty revset
656 Fully empty revset
657
657
658 $ fileset "status('', '4', added())"
658 $ fileset "status('', '4', added())"
659 hg: parse error: first argument to status must be a revision
659 hg: parse error: first argument to status must be a revision
660 [255]
660 [255]
661 $ fileset "status('2', '', added())"
661 $ fileset "status('2', '', added())"
662 hg: parse error: second argument to status must be a revision
662 hg: parse error: second argument to status must be a revision
663 [255]
663 [255]
664
664
665 Empty revset will error at the revset layer
665 Empty revset will error at the revset layer
666
666
667 $ fileset "status(' ', '4', added())"
667 $ fileset "status(' ', '4', added())"
668 hg: parse error at 1: not a prefix: end
668 hg: parse error at 1: not a prefix: end
669 (
669 (
670 ^ here)
670 ^ here)
671 [255]
671 [255]
672 $ fileset "status('2', ' ', added())"
672 $ fileset "status('2', ' ', added())"
673 hg: parse error at 1: not a prefix: end
673 hg: parse error at 1: not a prefix: end
674 (
674 (
675 ^ here)
675 ^ here)
676 [255]
676 [255]
General Comments 0
You need to be logged in to leave comments. Login now