comparison mercurial/revset.py @ 12821:165079e564f0 stable

revsets: generate predicate help dynamically
author Patrick Mezard <pmezard@gmail.com>
date Sat, 23 Oct 2010 19:21:51 +0200
parents 079a618ea89d
children 80deae3bc5ea
comparison
equal deleted inserted replaced
12820:0edc0aa7432d 12821:165079e564f0
172 raise error.ParseError(_("not a function: %s") % a[1]) 172 raise error.ParseError(_("not a function: %s") % a[1])
173 173
174 # functions 174 # functions
175 175
176 def node(repo, subset, x): 176 def node(repo, subset, x):
177 """``id(string)``
178 Revision non-ambiguously specified by the given hex string prefix
179 """
177 # i18n: "id" is a keyword 180 # i18n: "id" is a keyword
178 l = getargs(x, 1, 1, _("id requires one argument")) 181 l = getargs(x, 1, 1, _("id requires one argument"))
179 # i18n: "id" is a keyword 182 # i18n: "id" is a keyword
180 n = getstring(l[0], _("id requires a string")) 183 n = getstring(l[0], _("id requires a string"))
181 if len(n) == 40: 184 if len(n) == 40:
183 else: 186 else:
184 rn = repo.changelog.rev(repo.changelog._partialmatch(n)) 187 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
185 return [r for r in subset if r == rn] 188 return [r for r in subset if r == rn]
186 189
187 def rev(repo, subset, x): 190 def rev(repo, subset, x):
191 """``rev(number)``
192 Revision with the given numeric identifier.
193 """
188 # i18n: "rev" is a keyword 194 # i18n: "rev" is a keyword
189 l = getargs(x, 1, 1, _("rev requires one argument")) 195 l = getargs(x, 1, 1, _("rev requires one argument"))
190 try: 196 try:
191 # i18n: "rev" is a keyword 197 # i18n: "rev" is a keyword
192 l = int(getstring(l[0], _("rev requires a number"))) 198 l = int(getstring(l[0], _("rev requires a number")))
194 # i18n: "rev" is a keyword 200 # i18n: "rev" is a keyword
195 raise error.ParseError(_("rev expects a number")) 201 raise error.ParseError(_("rev expects a number"))
196 return [r for r in subset if r == l] 202 return [r for r in subset if r == l]
197 203
198 def p1(repo, subset, x): 204 def p1(repo, subset, x):
205 """``p1(set)``
206 First parent of changesets in set.
207 """
199 ps = set() 208 ps = set()
200 cl = repo.changelog 209 cl = repo.changelog
201 for r in getset(repo, range(len(repo)), x): 210 for r in getset(repo, range(len(repo)), x):
202 ps.add(cl.parentrevs(r)[0]) 211 ps.add(cl.parentrevs(r)[0])
203 return [r for r in subset if r in ps] 212 return [r for r in subset if r in ps]
204 213
205 def p2(repo, subset, x): 214 def p2(repo, subset, x):
215 """``p2(set)``
216 Second parent of changesets in set.
217 """
206 ps = set() 218 ps = set()
207 cl = repo.changelog 219 cl = repo.changelog
208 for r in getset(repo, range(len(repo)), x): 220 for r in getset(repo, range(len(repo)), x):
209 ps.add(cl.parentrevs(r)[1]) 221 ps.add(cl.parentrevs(r)[1])
210 return [r for r in subset if r in ps] 222 return [r for r in subset if r in ps]
211 223
212 def parents(repo, subset, x): 224 def parents(repo, subset, x):
225 """``parents(set)``
226 The set of all parents for all changesets in set.
227 """
213 ps = set() 228 ps = set()
214 cl = repo.changelog 229 cl = repo.changelog
215 for r in getset(repo, range(len(repo)), x): 230 for r in getset(repo, range(len(repo)), x):
216 ps.update(cl.parentrevs(r)) 231 ps.update(cl.parentrevs(r))
217 return [r for r in subset if r in ps] 232 return [r for r in subset if r in ps]
218 233
219 def maxrev(repo, subset, x): 234 def maxrev(repo, subset, x):
235 """``max(set)``
236 Changeset with highest revision number in set.
237 """
220 s = getset(repo, subset, x) 238 s = getset(repo, subset, x)
221 if s: 239 if s:
222 m = max(s) 240 m = max(s)
223 if m in subset: 241 if m in subset:
224 return [m] 242 return [m]
225 return [] 243 return []
226 244
227 def minrev(repo, subset, x): 245 def minrev(repo, subset, x):
246 """``min(set)``
247 Changeset with lowest revision number in set.
248 """
228 s = getset(repo, subset, x) 249 s = getset(repo, subset, x)
229 if s: 250 if s:
230 m = min(s) 251 m = min(s)
231 if m in subset: 252 if m in subset:
232 return [m] 253 return [m]
233 return [] 254 return []
234 255
235 def limit(repo, subset, x): 256 def limit(repo, subset, x):
257 """``limit(set, n)``
258 First n members of set.
259 """
236 # i18n: "limit" is a keyword 260 # i18n: "limit" is a keyword
237 l = getargs(x, 2, 2, _("limit requires two arguments")) 261 l = getargs(x, 2, 2, _("limit requires two arguments"))
238 try: 262 try:
239 # i18n: "limit" is a keyword 263 # i18n: "limit" is a keyword
240 lim = int(getstring(l[1], _("limit requires a number"))) 264 lim = int(getstring(l[1], _("limit requires a number")))
242 # i18n: "limit" is a keyword 266 # i18n: "limit" is a keyword
243 raise error.ParseError(_("limit expects a number")) 267 raise error.ParseError(_("limit expects a number"))
244 return getset(repo, subset, l[0])[:lim] 268 return getset(repo, subset, l[0])[:lim]
245 269
246 def children(repo, subset, x): 270 def children(repo, subset, x):
271 """``children(set)``
272 Child changesets of changesets in set.
273 """
247 cs = set() 274 cs = set()
248 cl = repo.changelog 275 cl = repo.changelog
249 s = set(getset(repo, range(len(repo)), x)) 276 s = set(getset(repo, range(len(repo)), x))
250 for r in xrange(0, len(repo)): 277 for r in xrange(0, len(repo)):
251 for p in cl.parentrevs(r): 278 for p in cl.parentrevs(r):
252 if p in s: 279 if p in s:
253 cs.add(r) 280 cs.add(r)
254 return [r for r in subset if r in cs] 281 return [r for r in subset if r in cs]
255 282
256 def branch(repo, subset, x): 283 def branch(repo, subset, x):
284 """``branch(set)``
285 All changesets belonging to the branches of changesets in set.
286 """
257 s = getset(repo, range(len(repo)), x) 287 s = getset(repo, range(len(repo)), x)
258 b = set() 288 b = set()
259 for r in s: 289 for r in s:
260 b.add(repo[r].branch()) 290 b.add(repo[r].branch())
261 s = set(s) 291 s = set(s)
262 return [r for r in subset if r in s or repo[r].branch() in b] 292 return [r for r in subset if r in s or repo[r].branch() in b]
263 293
264 def ancestor(repo, subset, x): 294 def ancestor(repo, subset, x):
295 """``ancestor(single, single)``
296 Greatest common ancestor of the two changesets.
297 """
265 # i18n: "ancestor" is a keyword 298 # i18n: "ancestor" is a keyword
266 l = getargs(x, 2, 2, _("ancestor requires two arguments")) 299 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
267 r = range(len(repo)) 300 r = range(len(repo))
268 a = getset(repo, r, l[0]) 301 a = getset(repo, r, l[0])
269 b = getset(repo, r, l[1]) 302 b = getset(repo, r, l[1])
273 an = [repo[a[0]].ancestor(repo[b[0]]).rev()] 306 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
274 307
275 return [r for r in an if r in subset] 308 return [r for r in an if r in subset]
276 309
277 def ancestors(repo, subset, x): 310 def ancestors(repo, subset, x):
311 """``ancestors(set)``
312 Changesets that are ancestors of a changeset in set.
313 """
278 args = getset(repo, range(len(repo)), x) 314 args = getset(repo, range(len(repo)), x)
279 if not args: 315 if not args:
280 return [] 316 return []
281 s = set(repo.changelog.ancestors(*args)) | set(args) 317 s = set(repo.changelog.ancestors(*args)) | set(args)
282 return [r for r in subset if r in s] 318 return [r for r in subset if r in s]
283 319
284 def descendants(repo, subset, x): 320 def descendants(repo, subset, x):
321 """``descendants(set)``
322 Changesets which are descendants of changesets in set.
323 """
285 args = getset(repo, range(len(repo)), x) 324 args = getset(repo, range(len(repo)), x)
286 if not args: 325 if not args:
287 return [] 326 return []
288 s = set(repo.changelog.descendants(*args)) | set(args) 327 s = set(repo.changelog.descendants(*args)) | set(args)
289 return [r for r in subset if r in s] 328 return [r for r in subset if r in s]
290 329
291 def follow(repo, subset, x): 330 def follow(repo, subset, x):
331 """``follow()``
332 An alias for ``::.`` (ancestors of the working copy's first parent).
333 """
292 # i18n: "follow" is a keyword 334 # i18n: "follow" is a keyword
293 getargs(x, 0, 0, _("follow takes no arguments")) 335 getargs(x, 0, 0, _("follow takes no arguments"))
294 p = repo['.'].rev() 336 p = repo['.'].rev()
295 s = set(repo.changelog.ancestors(p)) | set([p]) 337 s = set(repo.changelog.ancestors(p)) | set([p])
296 return [r for r in subset if r in s] 338 return [r for r in subset if r in s]
297 339
298 def date(repo, subset, x): 340 def date(repo, subset, x):
341 """``date(interval)``
342 Changesets within the interval, see :hg:`help dates`.
343 """
299 # i18n: "date" is a keyword 344 # i18n: "date" is a keyword
300 ds = getstring(x, _("date requires a string")) 345 ds = getstring(x, _("date requires a string"))
301 dm = util.matchdate(ds) 346 dm = util.matchdate(ds)
302 return [r for r in subset if dm(repo[r].date()[0])] 347 return [r for r in subset if dm(repo[r].date()[0])]
303 348
304 def keyword(repo, subset, x): 349 def keyword(repo, subset, x):
350 """``keyword(string)``
351 Search commit message, user name, and names of changed files for
352 string.
353 """
305 # i18n: "keyword" is a keyword 354 # i18n: "keyword" is a keyword
306 kw = getstring(x, _("keyword requires a string")).lower() 355 kw = getstring(x, _("keyword requires a string")).lower()
307 l = [] 356 l = []
308 for r in subset: 357 for r in subset:
309 c = repo[r] 358 c = repo[r]
311 if kw in t.lower(): 360 if kw in t.lower():
312 l.append(r) 361 l.append(r)
313 return l 362 return l
314 363
315 def grep(repo, subset, x): 364 def grep(repo, subset, x):
365 """``grep(regex)``
366 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
367 to ensure special escape characters are handled correctly.
368 """
316 try: 369 try:
317 # i18n: "grep" is a keyword 370 # i18n: "grep" is a keyword
318 gr = re.compile(getstring(x, _("grep requires a string"))) 371 gr = re.compile(getstring(x, _("grep requires a string")))
319 except re.error, e: 372 except re.error, e:
320 raise error.ParseError(_('invalid match pattern: %s') % e) 373 raise error.ParseError(_('invalid match pattern: %s') % e)
326 l.append(r) 379 l.append(r)
327 continue 380 continue
328 return l 381 return l
329 382
330 def author(repo, subset, x): 383 def author(repo, subset, x):
384 """``author(string)``
385 Alias for ``user(string)``.
386 """
331 # i18n: "author" is a keyword 387 # i18n: "author" is a keyword
332 n = getstring(x, _("author requires a string")).lower() 388 n = getstring(x, _("author requires a string")).lower()
333 return [r for r in subset if n in repo[r].user().lower()] 389 return [r for r in subset if n in repo[r].user().lower()]
334 390
391 def user(repo, subset, x):
392 """``user(string)``
393 User name is string.
394 """
395 return author(repo, subset, x)
396
335 def hasfile(repo, subset, x): 397 def hasfile(repo, subset, x):
398 """``file(pattern)``
399 Changesets affecting files matched by pattern.
400 """
336 # i18n: "file" is a keyword 401 # i18n: "file" is a keyword
337 pat = getstring(x, _("file requires a pattern")) 402 pat = getstring(x, _("file requires a pattern"))
338 m = matchmod.match(repo.root, repo.getcwd(), [pat]) 403 m = matchmod.match(repo.root, repo.getcwd(), [pat])
339 s = [] 404 s = []
340 for r in subset: 405 for r in subset:
343 s.append(r) 408 s.append(r)
344 continue 409 continue
345 return s 410 return s
346 411
347 def contains(repo, subset, x): 412 def contains(repo, subset, x):
413 """``contains(pattern)``
414 Revision contains pattern.
415 """
348 # i18n: "contains" is a keyword 416 # i18n: "contains" is a keyword
349 pat = getstring(x, _("contains requires a pattern")) 417 pat = getstring(x, _("contains requires a pattern"))
350 m = matchmod.match(repo.root, repo.getcwd(), [pat]) 418 m = matchmod.match(repo.root, repo.getcwd(), [pat])
351 s = [] 419 s = []
352 if m.files() == [pat]: 420 if m.files() == [pat]:
388 s.append(r) 456 s.append(r)
389 continue 457 continue
390 return s 458 return s
391 459
392 def modifies(repo, subset, x): 460 def modifies(repo, subset, x):
461 """``modifies(pattern)``
462 Changesets modifying files matched by pattern.
463 """
393 # i18n: "modifies" is a keyword 464 # i18n: "modifies" is a keyword
394 pat = getstring(x, _("modifies requires a pattern")) 465 pat = getstring(x, _("modifies requires a pattern"))
395 return checkstatus(repo, subset, pat, 0) 466 return checkstatus(repo, subset, pat, 0)
396 467
397 def adds(repo, subset, x): 468 def adds(repo, subset, x):
469 """``adds(pattern)``
470 Changesets that add a file matching pattern.
471 """
398 # i18n: "adds" is a keyword 472 # i18n: "adds" is a keyword
399 pat = getstring(x, _("adds requires a pattern")) 473 pat = getstring(x, _("adds requires a pattern"))
400 return checkstatus(repo, subset, pat, 1) 474 return checkstatus(repo, subset, pat, 1)
401 475
402 def removes(repo, subset, x): 476 def removes(repo, subset, x):
477 """``removes(pattern)``
478 Changesets which remove files matching pattern.
479 """
403 # i18n: "removes" is a keyword 480 # i18n: "removes" is a keyword
404 pat = getstring(x, _("removes requires a pattern")) 481 pat = getstring(x, _("removes requires a pattern"))
405 return checkstatus(repo, subset, pat, 2) 482 return checkstatus(repo, subset, pat, 2)
406 483
407 def merge(repo, subset, x): 484 def merge(repo, subset, x):
485 """``merge()``
486 Changeset is a merge changeset.
487 """
408 # i18n: "merge" is a keyword 488 # i18n: "merge" is a keyword
409 getargs(x, 0, 0, _("merge takes no arguments")) 489 getargs(x, 0, 0, _("merge takes no arguments"))
410 cl = repo.changelog 490 cl = repo.changelog
411 return [r for r in subset if cl.parentrevs(r)[1] != -1] 491 return [r for r in subset if cl.parentrevs(r)[1] != -1]
412 492
413 def closed(repo, subset, x): 493 def closed(repo, subset, x):
494 """``closed()``
495 Changeset is closed.
496 """
414 # i18n: "closed" is a keyword 497 # i18n: "closed" is a keyword
415 getargs(x, 0, 0, _("closed takes no arguments")) 498 getargs(x, 0, 0, _("closed takes no arguments"))
416 return [r for r in subset if repo[r].extra().get('close')] 499 return [r for r in subset if repo[r].extra().get('close')]
417 500
418 def head(repo, subset, x): 501 def head(repo, subset, x):
502 """``head()``
503 Changeset is a named branch head.
504 """
419 # i18n: "head" is a keyword 505 # i18n: "head" is a keyword
420 getargs(x, 0, 0, _("head takes no arguments")) 506 getargs(x, 0, 0, _("head takes no arguments"))
421 hs = set() 507 hs = set()
422 for b, ls in repo.branchmap().iteritems(): 508 for b, ls in repo.branchmap().iteritems():
423 hs.update(repo[h].rev() for h in ls) 509 hs.update(repo[h].rev() for h in ls)
424 return [r for r in subset if r in hs] 510 return [r for r in subset if r in hs]
425 511
426 def reverse(repo, subset, x): 512 def reverse(repo, subset, x):
513 """``reverse(set)``
514 Reverse order of set.
515 """
427 l = getset(repo, subset, x) 516 l = getset(repo, subset, x)
428 l.reverse() 517 l.reverse()
429 return l 518 return l
430 519
431 def present(repo, subset, x): 520 def present(repo, subset, x):
521 """``present(set)``
522 An empty set, if any revision in set isn't found; otherwise,
523 all revisions in set.
524 """
432 try: 525 try:
433 return getset(repo, subset, x) 526 return getset(repo, subset, x)
434 except error.RepoLookupError: 527 except error.RepoLookupError:
435 return [] 528 return []
436 529
437 def sort(repo, subset, x): 530 def sort(repo, subset, x):
531 """``sort(set[, [-]key...])``
532 Sort set by keys. The default sort order is ascending, specify a key
533 as ``-key`` to sort in descending order.
534
535 The keys can be:
536
537 - ``rev`` for the revision number,
538 - ``branch`` for the branch name,
539 - ``desc`` for the commit message (description),
540 - ``user`` for user name (``author`` can be used as an alias),
541 - ``date`` for the commit date
542 """
438 # i18n: "sort" is a keyword 543 # i18n: "sort" is a keyword
439 l = getargs(x, 1, 2, _("sort requires one or two arguments")) 544 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
440 keys = "rev" 545 keys = "rev"
441 if len(l) == 2: 546 if len(l) == 2:
442 keys = getstring(l[1], _("sort spec must be a string")) 547 keys = getstring(l[1], _("sort spec must be a string"))
476 l.append(e) 581 l.append(e)
477 l.sort() 582 l.sort()
478 return [e[-1] for e in l] 583 return [e[-1] for e in l]
479 584
480 def getall(repo, subset, x): 585 def getall(repo, subset, x):
586 """``all()``
587 All changesets, the same as ``0:tip``.
588 """
481 # i18n: "all" is a keyword 589 # i18n: "all" is a keyword
482 getargs(x, 0, 0, _("all takes no arguments")) 590 getargs(x, 0, 0, _("all takes no arguments"))
483 return subset 591 return subset
484 592
485 def heads(repo, subset, x): 593 def heads(repo, subset, x):
594 """``heads(set)``
595 Members of set with no children in set.
596 """
486 s = getset(repo, subset, x) 597 s = getset(repo, subset, x)
487 ps = set(parents(repo, subset, x)) 598 ps = set(parents(repo, subset, x))
488 return [r for r in s if r not in ps] 599 return [r for r in s if r not in ps]
489 600
490 def roots(repo, subset, x): 601 def roots(repo, subset, x):
602 """``roots(set)``
603 Changesets with no parent changeset in set.
604 """
491 s = getset(repo, subset, x) 605 s = getset(repo, subset, x)
492 cs = set(children(repo, subset, x)) 606 cs = set(children(repo, subset, x))
493 return [r for r in s if r not in cs] 607 return [r for r in s if r not in cs]
494 608
495 def outgoing(repo, subset, x): 609 def outgoing(repo, subset, x):
610 """``outgoing([path])``
611 Changesets not found in the specified destination repository, or the
612 default push location.
613 """
496 import hg # avoid start-up nasties 614 import hg # avoid start-up nasties
497 # i18n: "outgoing" is a keyword 615 # i18n: "outgoing" is a keyword
498 l = getargs(x, 0, 1, _("outgoing requires a repository path")) 616 l = getargs(x, 0, 1, _("outgoing requires a repository path"))
499 # i18n: "outgoing" is a keyword 617 # i18n: "outgoing" is a keyword
500 dest = l and getstring(l[0], _("outgoing requires a repository path")) or '' 618 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
510 cl = repo.changelog 628 cl = repo.changelog
511 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]]) 629 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
512 return [r for r in subset if r in o] 630 return [r for r in subset if r in o]
513 631
514 def tag(repo, subset, x): 632 def tag(repo, subset, x):
633 """``tag(name)``
634 The specified tag by name, or all tagged revisions if no name is given.
635 """
515 # i18n: "tag" is a keyword 636 # i18n: "tag" is a keyword
516 args = getargs(x, 0, 1, _("tag takes one or no arguments")) 637 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
517 cl = repo.changelog 638 cl = repo.changelog
518 if args: 639 if args:
519 tn = getstring(args[0], 640 tn = getstring(args[0],
521 _('the argument to tag must be a string')) 642 _('the argument to tag must be a string'))
522 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn]) 643 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
523 else: 644 else:
524 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip']) 645 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
525 return [r for r in subset if r in s] 646 return [r for r in subset if r in s]
647
648 def tagged(repo, subset, x):
649 return tag(repo, subset, x)
526 650
527 symbols = { 651 symbols = {
528 "adds": adds, 652 "adds": adds,
529 "all": getall, 653 "all": getall,
530 "ancestor": ancestor, 654 "ancestor": ancestor,
557 "reverse": reverse, 681 "reverse": reverse,
558 "rev": rev, 682 "rev": rev,
559 "roots": roots, 683 "roots": roots,
560 "sort": sort, 684 "sort": sort,
561 "tag": tag, 685 "tag": tag,
562 "tagged": tag, 686 "tagged": tagged,
563 "user": author, 687 "user": user,
564 } 688 }
565 689
566 methods = { 690 methods = {
567 "range": rangeset, 691 "range": rangeset,
568 "string": stringset, 692 "string": stringset,
651 tree = parse(spec) 775 tree = parse(spec)
652 weight, tree = optimize(tree, True) 776 weight, tree = optimize(tree, True)
653 def mfunc(repo, subset): 777 def mfunc(repo, subset):
654 return getset(repo, subset, tree) 778 return getset(repo, subset, tree)
655 return mfunc 779 return mfunc
780
781 def makedoc(topic, doc):
782 """Generate and include predicates help in revsets topic."""
783 predicates = []
784 for name in sorted(symbols):
785 text = symbols[name].__doc__
786 if not text:
787 continue
788 lines = text.splitlines()
789 lines[1:] = [(' ' + l.strip()) for l in lines[1:]]
790 predicates.append('\n'.join(lines))
791 predicates = '\n'.join(predicates)
792 doc = doc.replace('.. predicatesmarker', predicates)
793 return doc