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