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