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