##// END OF EJS Templates
revset: expand help for contains predicate
Martin Geisler -
r14353:afb2ed5b stable
parent child Browse files
Show More
@@ -1,833 +1,834 b''
1 1 # revset.py - revision 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 import re
9 9 import parser, util, error, discovery
10 10 import bookmarks as bookmarksmod
11 11 import match as matchmod
12 12 from i18n import _, gettext
13 13
14 14 elements = {
15 15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
16 16 "-": (5, ("negate", 19), ("minus", 5)),
17 17 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
18 18 ("dagrangepost", 17)),
19 19 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
20 20 ("dagrangepost", 17)),
21 21 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
22 22 "not": (10, ("not", 10)),
23 23 "!": (10, ("not", 10)),
24 24 "and": (5, None, ("and", 5)),
25 25 "&": (5, None, ("and", 5)),
26 26 "or": (4, None, ("or", 4)),
27 27 "|": (4, None, ("or", 4)),
28 28 "+": (4, None, ("or", 4)),
29 29 ",": (2, None, ("list", 2)),
30 30 ")": (0, None, None),
31 31 "symbol": (0, ("symbol",), None),
32 32 "string": (0, ("string",), None),
33 33 "end": (0, None, None),
34 34 }
35 35
36 36 keywords = set(['and', 'or', 'not'])
37 37
38 38 def tokenize(program):
39 39 pos, l = 0, len(program)
40 40 while pos < l:
41 41 c = program[pos]
42 42 if c.isspace(): # skip inter-token whitespace
43 43 pass
44 44 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
45 45 yield ('::', None, pos)
46 46 pos += 1 # skip ahead
47 47 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
48 48 yield ('..', None, pos)
49 49 pos += 1 # skip ahead
50 50 elif c in "():,-|&+!": # handle simple operators
51 51 yield (c, None, pos)
52 52 elif (c in '"\'' or c == 'r' and
53 53 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
54 54 if c == 'r':
55 55 pos += 1
56 56 c = program[pos]
57 57 decode = lambda x: x
58 58 else:
59 59 decode = lambda x: x.decode('string-escape')
60 60 pos += 1
61 61 s = pos
62 62 while pos < l: # find closing quote
63 63 d = program[pos]
64 64 if d == '\\': # skip over escaped characters
65 65 pos += 2
66 66 continue
67 67 if d == c:
68 68 yield ('string', decode(program[s:pos]), s)
69 69 break
70 70 pos += 1
71 71 else:
72 72 raise error.ParseError(_("unterminated string"), s)
73 73 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
74 74 s = pos
75 75 pos += 1
76 76 while pos < l: # find end of symbol
77 77 d = program[pos]
78 78 if not (d.isalnum() or d in "._" or ord(d) > 127):
79 79 break
80 80 if d == '.' and program[pos - 1] == '.': # special case for ..
81 81 pos -= 1
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 # helpers
96 96
97 97 def getstring(x, err):
98 98 if x and (x[0] == 'string' or x[0] == 'symbol'):
99 99 return x[1]
100 100 raise error.ParseError(err)
101 101
102 102 def getlist(x):
103 103 if not x:
104 104 return []
105 105 if x[0] == 'list':
106 106 return getlist(x[1]) + [x[2]]
107 107 return [x]
108 108
109 109 def getargs(x, min, max, err):
110 110 l = getlist(x)
111 111 if len(l) < min or len(l) > max:
112 112 raise error.ParseError(err)
113 113 return l
114 114
115 115 def getset(repo, subset, x):
116 116 if not x:
117 117 raise error.ParseError(_("missing argument"))
118 118 return methods[x[0]](repo, subset, *x[1:])
119 119
120 120 # operator methods
121 121
122 122 def stringset(repo, subset, x):
123 123 x = repo[x].rev()
124 124 if x == -1 and len(subset) == len(repo):
125 125 return [-1]
126 126 if x in subset:
127 127 return [x]
128 128 return []
129 129
130 130 def symbolset(repo, subset, x):
131 131 if x in symbols:
132 132 raise error.ParseError(_("can't use %s here") % x)
133 133 return stringset(repo, subset, x)
134 134
135 135 def rangeset(repo, subset, x, y):
136 136 m = getset(repo, subset, x)
137 137 if not m:
138 138 m = getset(repo, range(len(repo)), x)
139 139
140 140 n = getset(repo, subset, y)
141 141 if not n:
142 142 n = getset(repo, range(len(repo)), y)
143 143
144 144 if not m or not n:
145 145 return []
146 146 m, n = m[0], n[-1]
147 147
148 148 if m < n:
149 149 r = range(m, n + 1)
150 150 else:
151 151 r = range(m, n - 1, -1)
152 152 s = set(subset)
153 153 return [x for x in r if x in s]
154 154
155 155 def andset(repo, subset, x, y):
156 156 return getset(repo, getset(repo, subset, x), y)
157 157
158 158 def orset(repo, subset, x, y):
159 159 s = set(getset(repo, subset, x))
160 160 s |= set(getset(repo, [r for r in subset if r not in s], y))
161 161 return [r for r in subset if r in s]
162 162
163 163 def notset(repo, subset, x):
164 164 s = set(getset(repo, subset, x))
165 165 return [r for r in subset if r not in s]
166 166
167 167 def listset(repo, subset, a, b):
168 168 raise error.ParseError(_("can't use a list in this context"))
169 169
170 170 def func(repo, subset, a, b):
171 171 if a[0] == 'symbol' and a[1] in symbols:
172 172 return symbols[a[1]](repo, subset, b)
173 173 raise error.ParseError(_("not a function: %s") % a[1])
174 174
175 175 # functions
176 176
177 177 def node(repo, subset, x):
178 178 """``id(string)``
179 179 Revision non-ambiguously specified by the given hex string prefix.
180 180 """
181 181 # i18n: "id" is a keyword
182 182 l = getargs(x, 1, 1, _("id requires one argument"))
183 183 # i18n: "id" is a keyword
184 184 n = getstring(l[0], _("id requires a string"))
185 185 if len(n) == 40:
186 186 rn = repo[n].rev()
187 187 else:
188 188 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
189 189 return [r for r in subset if r == rn]
190 190
191 191 def rev(repo, subset, x):
192 192 """``rev(number)``
193 193 Revision with the given numeric identifier.
194 194 """
195 195 # i18n: "rev" is a keyword
196 196 l = getargs(x, 1, 1, _("rev requires one argument"))
197 197 try:
198 198 # i18n: "rev" is a keyword
199 199 l = int(getstring(l[0], _("rev requires a number")))
200 200 except ValueError:
201 201 # i18n: "rev" is a keyword
202 202 raise error.ParseError(_("rev expects a number"))
203 203 return [r for r in subset if r == l]
204 204
205 205 def p1(repo, subset, x):
206 206 """``p1([set])``
207 207 First parent of changesets in set, or the working directory.
208 208 """
209 209 if x is None:
210 210 p = repo[x].parents()[0].rev()
211 211 return [r for r in subset if r == p]
212 212
213 213 ps = set()
214 214 cl = repo.changelog
215 215 for r in getset(repo, range(len(repo)), x):
216 216 ps.add(cl.parentrevs(r)[0])
217 217 return [r for r in subset if r in ps]
218 218
219 219 def p2(repo, subset, x):
220 220 """``p2([set])``
221 221 Second parent of changesets in set, or the working directory.
222 222 """
223 223 if x is None:
224 224 ps = repo[x].parents()
225 225 try:
226 226 p = ps[1].rev()
227 227 return [r for r in subset if r == p]
228 228 except IndexError:
229 229 return []
230 230
231 231 ps = set()
232 232 cl = repo.changelog
233 233 for r in getset(repo, range(len(repo)), x):
234 234 ps.add(cl.parentrevs(r)[1])
235 235 return [r for r in subset if r in ps]
236 236
237 237 def parents(repo, subset, x):
238 238 """``parents([set])``
239 239 The set of all parents for all changesets in set, or the working directory.
240 240 """
241 241 if x is None:
242 242 ps = tuple(p.rev() for p in repo[x].parents())
243 243 return [r for r in subset if r in ps]
244 244
245 245 ps = set()
246 246 cl = repo.changelog
247 247 for r in getset(repo, range(len(repo)), x):
248 248 ps.update(cl.parentrevs(r))
249 249 return [r for r in subset if r in ps]
250 250
251 251 def maxrev(repo, subset, x):
252 252 """``max(set)``
253 253 Changeset with highest revision number in set.
254 254 """
255 255 s = getset(repo, subset, x)
256 256 if s:
257 257 m = max(s)
258 258 if m in subset:
259 259 return [m]
260 260 return []
261 261
262 262 def minrev(repo, subset, x):
263 263 """``min(set)``
264 264 Changeset with lowest revision number in set.
265 265 """
266 266 s = getset(repo, subset, x)
267 267 if s:
268 268 m = min(s)
269 269 if m in subset:
270 270 return [m]
271 271 return []
272 272
273 273 def limit(repo, subset, x):
274 274 """``limit(set, n)``
275 275 First n members of set.
276 276 """
277 277 # i18n: "limit" is a keyword
278 278 l = getargs(x, 2, 2, _("limit requires two arguments"))
279 279 try:
280 280 # i18n: "limit" is a keyword
281 281 lim = int(getstring(l[1], _("limit requires a number")))
282 282 except ValueError:
283 283 # i18n: "limit" is a keyword
284 284 raise error.ParseError(_("limit expects a number"))
285 285 return getset(repo, subset, l[0])[:lim]
286 286
287 287 def children(repo, subset, x):
288 288 """``children(set)``
289 289 Child changesets of changesets in set.
290 290 """
291 291 cs = set()
292 292 cl = repo.changelog
293 293 s = set(getset(repo, range(len(repo)), x))
294 294 for r in xrange(0, len(repo)):
295 295 for p in cl.parentrevs(r):
296 296 if p in s:
297 297 cs.add(r)
298 298 return [r for r in subset if r in cs]
299 299
300 300 def branch(repo, subset, x):
301 301 """``branch(set)``
302 302 All changesets belonging to the branches of changesets in set.
303 303 """
304 304 s = getset(repo, range(len(repo)), x)
305 305 b = set()
306 306 for r in s:
307 307 b.add(repo[r].branch())
308 308 s = set(s)
309 309 return [r for r in subset if r in s or repo[r].branch() in b]
310 310
311 311 def ancestor(repo, subset, x):
312 312 """``ancestor(single, single)``
313 313 Greatest common ancestor of the two changesets.
314 314 """
315 315 # i18n: "ancestor" is a keyword
316 316 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
317 317 r = range(len(repo))
318 318 a = getset(repo, r, l[0])
319 319 b = getset(repo, r, l[1])
320 320 if len(a) != 1 or len(b) != 1:
321 321 # i18n: "ancestor" is a keyword
322 322 raise error.ParseError(_("ancestor arguments must be single revisions"))
323 323 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
324 324
325 325 return [r for r in an if r in subset]
326 326
327 327 def ancestors(repo, subset, x):
328 328 """``ancestors(set)``
329 329 Changesets that are ancestors of a changeset in set.
330 330 """
331 331 args = getset(repo, range(len(repo)), x)
332 332 if not args:
333 333 return []
334 334 s = set(repo.changelog.ancestors(*args)) | set(args)
335 335 return [r for r in subset if r in s]
336 336
337 337 def descendants(repo, subset, x):
338 338 """``descendants(set)``
339 339 Changesets which are descendants of changesets in set.
340 340 """
341 341 args = getset(repo, range(len(repo)), x)
342 342 if not args:
343 343 return []
344 344 s = set(repo.changelog.descendants(*args)) | set(args)
345 345 return [r for r in subset if r in s]
346 346
347 347 def follow(repo, subset, x):
348 348 """``follow()``
349 349 An alias for ``::.`` (ancestors of the working copy's first parent).
350 350 """
351 351 # i18n: "follow" is a keyword
352 352 getargs(x, 0, 0, _("follow takes no arguments"))
353 353 p = repo['.'].rev()
354 354 s = set(repo.changelog.ancestors(p)) | set([p])
355 355 return [r for r in subset if r in s]
356 356
357 357 def date(repo, subset, x):
358 358 """``date(interval)``
359 359 Changesets within the interval, see :hg:`help dates`.
360 360 """
361 361 # i18n: "date" is a keyword
362 362 ds = getstring(x, _("date requires a string"))
363 363 dm = util.matchdate(ds)
364 364 return [r for r in subset if dm(repo[r].date()[0])]
365 365
366 366 def keyword(repo, subset, x):
367 367 """``keyword(string)``
368 368 Search commit message, user name, and names of changed files for
369 369 string.
370 370 """
371 371 # i18n: "keyword" is a keyword
372 372 kw = getstring(x, _("keyword requires a string")).lower()
373 373 l = []
374 374 for r in subset:
375 375 c = repo[r]
376 376 t = " ".join(c.files() + [c.user(), c.description()])
377 377 if kw in t.lower():
378 378 l.append(r)
379 379 return l
380 380
381 381 def grep(repo, subset, x):
382 382 """``grep(regex)``
383 383 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
384 384 to ensure special escape characters are handled correctly.
385 385 """
386 386 try:
387 387 # i18n: "grep" is a keyword
388 388 gr = re.compile(getstring(x, _("grep requires a string")))
389 389 except re.error, e:
390 390 raise error.ParseError(_('invalid match pattern: %s') % e)
391 391 l = []
392 392 for r in subset:
393 393 c = repo[r]
394 394 for e in c.files() + [c.user(), c.description()]:
395 395 if gr.search(e):
396 396 l.append(r)
397 397 continue
398 398 return l
399 399
400 400 def author(repo, subset, x):
401 401 """``author(string)``
402 402 Alias for ``user(string)``.
403 403 """
404 404 # i18n: "author" is a keyword
405 405 n = getstring(x, _("author requires a string")).lower()
406 406 return [r for r in subset if n in repo[r].user().lower()]
407 407
408 408 def user(repo, subset, x):
409 409 """``user(string)``
410 410 User name is string.
411 411 """
412 412 return author(repo, subset, x)
413 413
414 414 def hasfile(repo, subset, x):
415 415 """``file(pattern)``
416 416 Changesets affecting files matched by pattern.
417 417 """
418 418 # i18n: "file" is a keyword
419 419 pat = getstring(x, _("file requires a pattern"))
420 420 m = matchmod.match(repo.root, repo.getcwd(), [pat])
421 421 s = []
422 422 for r in subset:
423 423 for f in repo[r].files():
424 424 if m(f):
425 425 s.append(r)
426 426 continue
427 427 return s
428 428
429 429 def contains(repo, subset, x):
430 430 """``contains(pattern)``
431 Revision contains pattern.
431 Revision contains a file matching pattern. See :hg:`help patterns`
432 for information about file patterns.
432 433 """
433 434 # i18n: "contains" is a keyword
434 435 pat = getstring(x, _("contains requires a pattern"))
435 436 m = matchmod.match(repo.root, repo.getcwd(), [pat])
436 437 s = []
437 438 if m.files() == [pat]:
438 439 for r in subset:
439 440 if pat in repo[r]:
440 441 s.append(r)
441 442 continue
442 443 else:
443 444 for r in subset:
444 445 for f in repo[r].manifest():
445 446 if m(f):
446 447 s.append(r)
447 448 continue
448 449 return s
449 450
450 451 def checkstatus(repo, subset, pat, field):
451 452 m = matchmod.match(repo.root, repo.getcwd(), [pat])
452 453 s = []
453 454 fast = (m.files() == [pat])
454 455 for r in subset:
455 456 c = repo[r]
456 457 if fast:
457 458 if pat not in c.files():
458 459 continue
459 460 else:
460 461 for f in c.files():
461 462 if m(f):
462 463 break
463 464 else:
464 465 continue
465 466 files = repo.status(c.p1().node(), c.node())[field]
466 467 if fast:
467 468 if pat in files:
468 469 s.append(r)
469 470 continue
470 471 else:
471 472 for f in files:
472 473 if m(f):
473 474 s.append(r)
474 475 continue
475 476 return s
476 477
477 478 def modifies(repo, subset, x):
478 479 """``modifies(pattern)``
479 480 Changesets modifying files matched by pattern.
480 481 """
481 482 # i18n: "modifies" is a keyword
482 483 pat = getstring(x, _("modifies requires a pattern"))
483 484 return checkstatus(repo, subset, pat, 0)
484 485
485 486 def adds(repo, subset, x):
486 487 """``adds(pattern)``
487 488 Changesets that add a file matching pattern.
488 489 """
489 490 # i18n: "adds" is a keyword
490 491 pat = getstring(x, _("adds requires a pattern"))
491 492 return checkstatus(repo, subset, pat, 1)
492 493
493 494 def removes(repo, subset, x):
494 495 """``removes(pattern)``
495 496 Changesets which remove files matching pattern.
496 497 """
497 498 # i18n: "removes" is a keyword
498 499 pat = getstring(x, _("removes requires a pattern"))
499 500 return checkstatus(repo, subset, pat, 2)
500 501
501 502 def merge(repo, subset, x):
502 503 """``merge()``
503 504 Changeset is a merge changeset.
504 505 """
505 506 # i18n: "merge" is a keyword
506 507 getargs(x, 0, 0, _("merge takes no arguments"))
507 508 cl = repo.changelog
508 509 return [r for r in subset if cl.parentrevs(r)[1] != -1]
509 510
510 511 def closed(repo, subset, x):
511 512 """``closed()``
512 513 Changeset is closed.
513 514 """
514 515 # i18n: "closed" is a keyword
515 516 getargs(x, 0, 0, _("closed takes no arguments"))
516 517 return [r for r in subset if repo[r].extra().get('close')]
517 518
518 519 def head(repo, subset, x):
519 520 """``head()``
520 521 Changeset is a named branch head.
521 522 """
522 523 # i18n: "head" is a keyword
523 524 getargs(x, 0, 0, _("head takes no arguments"))
524 525 hs = set()
525 526 for b, ls in repo.branchmap().iteritems():
526 527 hs.update(repo[h].rev() for h in ls)
527 528 return [r for r in subset if r in hs]
528 529
529 530 def reverse(repo, subset, x):
530 531 """``reverse(set)``
531 532 Reverse order of set.
532 533 """
533 534 l = getset(repo, subset, x)
534 535 l.reverse()
535 536 return l
536 537
537 538 def present(repo, subset, x):
538 539 """``present(set)``
539 540 An empty set, if any revision in set isn't found; otherwise,
540 541 all revisions in set.
541 542 """
542 543 try:
543 544 return getset(repo, subset, x)
544 545 except error.RepoLookupError:
545 546 return []
546 547
547 548 def sort(repo, subset, x):
548 549 """``sort(set[, [-]key...])``
549 550 Sort set by keys. The default sort order is ascending, specify a key
550 551 as ``-key`` to sort in descending order.
551 552
552 553 The keys can be:
553 554
554 555 - ``rev`` for the revision number,
555 556 - ``branch`` for the branch name,
556 557 - ``desc`` for the commit message (description),
557 558 - ``user`` for user name (``author`` can be used as an alias),
558 559 - ``date`` for the commit date
559 560 """
560 561 # i18n: "sort" is a keyword
561 562 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
562 563 keys = "rev"
563 564 if len(l) == 2:
564 565 keys = getstring(l[1], _("sort spec must be a string"))
565 566
566 567 s = l[0]
567 568 keys = keys.split()
568 569 l = []
569 570 def invert(s):
570 571 return "".join(chr(255 - ord(c)) for c in s)
571 572 for r in getset(repo, subset, s):
572 573 c = repo[r]
573 574 e = []
574 575 for k in keys:
575 576 if k == 'rev':
576 577 e.append(r)
577 578 elif k == '-rev':
578 579 e.append(-r)
579 580 elif k == 'branch':
580 581 e.append(c.branch())
581 582 elif k == '-branch':
582 583 e.append(invert(c.branch()))
583 584 elif k == 'desc':
584 585 e.append(c.description())
585 586 elif k == '-desc':
586 587 e.append(invert(c.description()))
587 588 elif k in 'user author':
588 589 e.append(c.user())
589 590 elif k in '-user -author':
590 591 e.append(invert(c.user()))
591 592 elif k == 'date':
592 593 e.append(c.date()[0])
593 594 elif k == '-date':
594 595 e.append(-c.date()[0])
595 596 else:
596 597 raise error.ParseError(_("unknown sort key %r") % k)
597 598 e.append(r)
598 599 l.append(e)
599 600 l.sort()
600 601 return [e[-1] for e in l]
601 602
602 603 def getall(repo, subset, x):
603 604 """``all()``
604 605 All changesets, the same as ``0:tip``.
605 606 """
606 607 # i18n: "all" is a keyword
607 608 getargs(x, 0, 0, _("all takes no arguments"))
608 609 return subset
609 610
610 611 def heads(repo, subset, x):
611 612 """``heads(set)``
612 613 Members of set with no children in set.
613 614 """
614 615 s = getset(repo, subset, x)
615 616 ps = set(parents(repo, subset, x))
616 617 return [r for r in s if r not in ps]
617 618
618 619 def roots(repo, subset, x):
619 620 """``roots(set)``
620 621 Changesets with no parent changeset in set.
621 622 """
622 623 s = getset(repo, subset, x)
623 624 cs = set(children(repo, subset, x))
624 625 return [r for r in s if r not in cs]
625 626
626 627 def outgoing(repo, subset, x):
627 628 """``outgoing([path])``
628 629 Changesets not found in the specified destination repository, or the
629 630 default push location.
630 631 """
631 632 import hg # avoid start-up nasties
632 633 # i18n: "outgoing" is a keyword
633 634 l = getargs(x, 0, 1, _("outgoing requires a repository path"))
634 635 # i18n: "outgoing" is a keyword
635 636 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
636 637 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
637 638 dest, branches = hg.parseurl(dest)
638 639 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
639 640 if revs:
640 641 revs = [repo.lookup(rev) for rev in revs]
641 642 other = hg.repository(hg.remoteui(repo, {}), dest)
642 643 repo.ui.pushbuffer()
643 644 o = discovery.findoutgoing(repo, other)
644 645 repo.ui.popbuffer()
645 646 cl = repo.changelog
646 647 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
647 648 return [r for r in subset if r in o]
648 649
649 650 def tag(repo, subset, x):
650 651 """``tag(name)``
651 652 The specified tag by name, or all tagged revisions if no name is given.
652 653 """
653 654 # i18n: "tag" is a keyword
654 655 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
655 656 cl = repo.changelog
656 657 if args:
657 658 tn = getstring(args[0],
658 659 # i18n: "tag" is a keyword
659 660 _('the argument to tag must be a string'))
660 661 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
661 662 else:
662 663 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
663 664 return [r for r in subset if r in s]
664 665
665 666 def tagged(repo, subset, x):
666 667 return tag(repo, subset, x)
667 668
668 669 def bookmark(repo, subset, x):
669 670 """``bookmark([name])``
670 671 The named bookmark or all bookmarks.
671 672 """
672 673 # i18n: "bookmark" is a keyword
673 674 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
674 675 if args:
675 676 bm = getstring(args[0],
676 677 # i18n: "bookmark" is a keyword
677 678 _('the argument to bookmark must be a string'))
678 679 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
679 680 if bmrev:
680 681 bmrev = repo[bmrev].rev()
681 682 return [r for r in subset if r == bmrev]
682 683 bms = set([repo[r].rev()
683 684 for r in bookmarksmod.listbookmarks(repo).values()])
684 685 return [r for r in subset if r in bms]
685 686
686 687 symbols = {
687 688 "adds": adds,
688 689 "all": getall,
689 690 "ancestor": ancestor,
690 691 "ancestors": ancestors,
691 692 "author": author,
692 693 "bookmark": bookmark,
693 694 "branch": branch,
694 695 "children": children,
695 696 "closed": closed,
696 697 "contains": contains,
697 698 "date": date,
698 699 "descendants": descendants,
699 700 "file": hasfile,
700 701 "follow": follow,
701 702 "grep": grep,
702 703 "head": head,
703 704 "heads": heads,
704 705 "keyword": keyword,
705 706 "limit": limit,
706 707 "max": maxrev,
707 708 "min": minrev,
708 709 "merge": merge,
709 710 "modifies": modifies,
710 711 "id": node,
711 712 "outgoing": outgoing,
712 713 "p1": p1,
713 714 "p2": p2,
714 715 "parents": parents,
715 716 "present": present,
716 717 "removes": removes,
717 718 "reverse": reverse,
718 719 "rev": rev,
719 720 "roots": roots,
720 721 "sort": sort,
721 722 "tag": tag,
722 723 "tagged": tagged,
723 724 "user": user,
724 725 }
725 726
726 727 methods = {
727 728 "range": rangeset,
728 729 "string": stringset,
729 730 "symbol": symbolset,
730 731 "and": andset,
731 732 "or": orset,
732 733 "not": notset,
733 734 "list": listset,
734 735 "func": func,
735 736 }
736 737
737 738 def optimize(x, small):
738 739 if x is None:
739 740 return 0, x
740 741
741 742 smallbonus = 1
742 743 if small:
743 744 smallbonus = .5
744 745
745 746 op = x[0]
746 747 if op == 'minus':
747 748 return optimize(('and', x[1], ('not', x[2])), small)
748 749 elif op == 'dagrange':
749 750 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
750 751 ('func', ('symbol', 'ancestors'), x[2])), small)
751 752 elif op == 'dagrangepre':
752 753 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
753 754 elif op == 'dagrangepost':
754 755 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
755 756 elif op == 'rangepre':
756 757 return optimize(('range', ('string', '0'), x[1]), small)
757 758 elif op == 'rangepost':
758 759 return optimize(('range', x[1], ('string', 'tip')), small)
759 760 elif op == 'negate':
760 761 return optimize(('string',
761 762 '-' + getstring(x[1], _("can't negate that"))), small)
762 763 elif op in 'string symbol negate':
763 764 return smallbonus, x # single revisions are small
764 765 elif op == 'and' or op == 'dagrange':
765 766 wa, ta = optimize(x[1], True)
766 767 wb, tb = optimize(x[2], True)
767 768 w = min(wa, wb)
768 769 if wa > wb:
769 770 return w, (op, tb, ta)
770 771 return w, (op, ta, tb)
771 772 elif op == 'or':
772 773 wa, ta = optimize(x[1], False)
773 774 wb, tb = optimize(x[2], False)
774 775 if wb < wa:
775 776 wb, wa = wa, wb
776 777 return max(wa, wb), (op, ta, tb)
777 778 elif op == 'not':
778 779 o = optimize(x[1], not small)
779 780 return o[0], (op, o[1])
780 781 elif op == 'group':
781 782 return optimize(x[1], small)
782 783 elif op in 'range list':
783 784 wa, ta = optimize(x[1], small)
784 785 wb, tb = optimize(x[2], small)
785 786 return wa + wb, (op, ta, tb)
786 787 elif op == 'func':
787 788 f = getstring(x[1], _("not a symbol"))
788 789 wa, ta = optimize(x[2], small)
789 790 if f in "grep date user author keyword branch file outgoing":
790 791 w = 10 # slow
791 792 elif f in "modifies adds removes":
792 793 w = 30 # slower
793 794 elif f == "contains":
794 795 w = 100 # very slow
795 796 elif f == "ancestor":
796 797 w = 1 * smallbonus
797 798 elif f in "reverse limit":
798 799 w = 0
799 800 elif f in "sort":
800 801 w = 10 # assume most sorts look at changelog
801 802 else:
802 803 w = 1
803 804 return w + wa, (op, x[1], ta)
804 805 return 1, x
805 806
806 807 parse = parser.parser(tokenize, elements).parse
807 808
808 809 def match(spec):
809 810 if not spec:
810 811 raise error.ParseError(_("empty query"))
811 812 tree = parse(spec)
812 813 weight, tree = optimize(tree, True)
813 814 def mfunc(repo, subset):
814 815 return getset(repo, subset, tree)
815 816 return mfunc
816 817
817 818 def makedoc(topic, doc):
818 819 """Generate and include predicates help in revsets topic."""
819 820 predicates = []
820 821 for name in sorted(symbols):
821 822 text = symbols[name].__doc__
822 823 if not text:
823 824 continue
824 825 text = gettext(text.rstrip())
825 826 lines = text.splitlines()
826 827 lines[1:] = [(' ' + l.strip()) for l in lines[1:]]
827 828 predicates.append('\n'.join(lines))
828 829 predicates = '\n\n'.join(predicates)
829 830 doc = doc.replace('.. predicatesmarker', predicates)
830 831 return doc
831 832
832 833 # tell hggettext to extract docstrings from these functions:
833 834 i18nfunctions = symbols.values()
General Comments 0
You need to be logged in to leave comments. Login now