comparison mercurial/revset.py @ 14070:305c97670d7a

revset: add ^ and ~ operators from parentrevspec extension ^ (Nth parent) and ~ (Nth first ancestor) are infix operators that match certain ancestors of the set: set^0 the set set^1 (also available as set^) the first parent of every changeset in set set^2 the second parent of every changeset in set set~0 the set set~1 the first ancestor (i.e. the first parent) of every changeset in set set~2 the second ancestor (i.e. first parent of first parent) of every changeset in set set~N the Nth ancestor (following first parents only) of every changeset in set; set~N is equivalent to set^1^1..., with ^1 repeated N times.
author Kevin Gessner <kevin@kevingessner.com>
date Sat, 30 Apr 2011 17:43:04 +0200
parents 611d2f8a4ba2
children 2e4d79dcc0a0
comparison
equal deleted inserted replaced
14069:e38846a79a23 14070:305c97670d7a
11 import match as matchmod 11 import match as matchmod
12 from i18n import _ 12 from i18n import _
13 13
14 elements = { 14 elements = {
15 "(": (20, ("group", 1, ")"), ("func", 1, ")")), 15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
16 "~": (18, None, ("ancestor", 18)),
17 "^": (18, None, ("parent", 18), ("parentpost", 18)),
16 "-": (5, ("negate", 19), ("minus", 5)), 18 "-": (5, ("negate", 19), ("minus", 5)),
17 "::": (17, ("dagrangepre", 17), ("dagrange", 17), 19 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
18 ("dagrangepost", 17)), 20 ("dagrangepost", 17)),
19 "..": (17, ("dagrangepre", 17), ("dagrange", 17), 21 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
20 ("dagrangepost", 17)), 22 ("dagrangepost", 17)),
45 yield ('::', None, pos) 47 yield ('::', None, pos)
46 pos += 1 # skip ahead 48 pos += 1 # skip ahead
47 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully 49 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
48 yield ('..', None, pos) 50 yield ('..', None, pos)
49 pos += 1 # skip ahead 51 pos += 1 # skip ahead
50 elif c in "():,-|&+!": # handle simple operators 52 elif c in "():,-|&+!~^": # handle simple operators
51 yield (c, None, pos) 53 yield (c, None, pos)
52 elif (c in '"\'' or c == 'r' and 54 elif (c in '"\'' or c == 'r' and
53 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings 55 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
54 if c == 'r': 56 if c == 'r':
55 pos += 1 57 pos += 1
207 if not args: 209 if not args:
208 return [] 210 return []
209 s = set(repo.changelog.ancestors(*args)) | set(args) 211 s = set(repo.changelog.ancestors(*args)) | set(args)
210 return [r for r in subset if r in s] 212 return [r for r in subset if r in s]
211 213
214 def ancestorspec(repo, subset, x, n):
215 """``set~n``
216 Changesets that are the Nth ancestor (first parents only) of a changeset in set.
217 """
218 try:
219 n = int(n[1])
220 except ValueError:
221 raise error.ParseError(_("~ expects a number"))
222 ps = set()
223 cl = repo.changelog
224 for r in getset(repo, subset, x):
225 for i in range(n):
226 r = cl.parentrevs(r)[0]
227 ps.add(r)
228 return [r for r in subset if r in ps]
229
212 def author(repo, subset, x): 230 def author(repo, subset, x):
213 """``author(string)`` 231 """``author(string)``
214 Alias for ``user(string)``. 232 Alias for ``user(string)``.
215 """ 233 """
216 # i18n: "author" is a keyword 234 # i18n: "author" is a keyword
584 602
585 ps = set() 603 ps = set()
586 cl = repo.changelog 604 cl = repo.changelog
587 for r in getset(repo, range(len(repo)), x): 605 for r in getset(repo, range(len(repo)), x):
588 ps.update(cl.parentrevs(r)) 606 ps.update(cl.parentrevs(r))
607 return [r for r in subset if r in ps]
608
609 def parentspec(repo, subset, x, n):
610 """``set^0``
611 The set.
612 ``set^1`` (or ``set^``), ``set^2``
613 First or second parent, respectively, of all changesets in set.
614 """
615 try:
616 n = int(n[1])
617 if n not in (0,1,2):
618 raise ValueError
619 except ValueError:
620 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
621 ps = set()
622 cl = repo.changelog
623 for r in getset(repo, subset, x):
624 if n == 0:
625 ps.add(r)
626 elif n == 1:
627 ps.add(cl.parentrevs(r)[0])
628 elif n == 2:
629 parents = cl.parentrevs(r)
630 if len(parents) > 1:
631 ps.add(parents[1])
589 return [r for r in subset if r in ps] 632 return [r for r in subset if r in ps]
590 633
591 def present(repo, subset, x): 634 def present(repo, subset, x):
592 """``present(set)`` 635 """``present(set)``
593 An empty set, if any revision in set isn't found; otherwise, 636 An empty set, if any revision in set isn't found; otherwise,
767 "and": andset, 810 "and": andset,
768 "or": orset, 811 "or": orset,
769 "not": notset, 812 "not": notset,
770 "list": listset, 813 "list": listset,
771 "func": func, 814 "func": func,
815 "ancestor": ancestorspec,
816 "parent": parentspec,
817 "parentpost": p1,
772 } 818 }
773 819
774 def optimize(x, small): 820 def optimize(x, small):
775 if x is None: 821 if x is None:
776 return 0, x 822 return 0, x
812 wb, wa = wa, wb 858 wb, wa = wa, wb
813 return max(wa, wb), (op, ta, tb) 859 return max(wa, wb), (op, ta, tb)
814 elif op == 'not': 860 elif op == 'not':
815 o = optimize(x[1], not small) 861 o = optimize(x[1], not small)
816 return o[0], (op, o[1]) 862 return o[0], (op, o[1])
863 elif op == 'parentpost':
864 o = optimize(x[1], small)
865 return o[0], (op, o[1])
817 elif op == 'group': 866 elif op == 'group':
818 return optimize(x[1], small) 867 return optimize(x[1], small)
819 elif op in 'range list': 868 elif op in 'range list parent ancestorspec':
820 wa, ta = optimize(x[1], small) 869 wa, ta = optimize(x[1], small)
821 wb, tb = optimize(x[2], small) 870 wb, tb = optimize(x[2], small)
822 return wa + wb, (op, ta, tb) 871 return wa + wb, (op, ta, tb)
823 elif op == 'func': 872 elif op == 'func':
824 f = getstring(x[1], _("not a symbol")) 873 f = getstring(x[1], _("not a symbol"))