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