comparison mercurial/revset.py @ 20365:bc770ee6a351

revset: implemented set caching for revset evaluation Added set caching to the baseset class. It lazily builds the set whenever it's needed and keeps a reference which is returned when the set is requested instead of being built again.
author Lucas Moscovicz <lmoscovicz@fb.com>
date Wed, 22 Jan 2014 10:46:02 -0800
parents a6cf48b2880d
children 5ec6321f49a9
comparison
equal deleted inserted replaced
20364:a6cf48b2880d 20365:bc770ee6a351
233 233
234 if m < n: 234 if m < n:
235 r = range(m, n + 1) 235 r = range(m, n + 1)
236 else: 236 else:
237 r = range(m, n - 1, -1) 237 r = range(m, n - 1, -1)
238 s = set(subset) 238 s = subset.set()
239 return baseset([x for x in r if x in s]) 239 return baseset([x for x in r if x in s])
240 240
241 def dagrange(repo, subset, x, y): 241 def dagrange(repo, subset, x, y):
242 r = baseset(repo) 242 r = baseset(repo)
243 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y)) 243 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
244 s = set(subset) 244 s = subset.set()
245 return baseset([r for r in xs if r in s]) 245 return baseset([r for r in xs if r in s])
246 246
247 def andset(repo, subset, x, y): 247 def andset(repo, subset, x, y):
248 return getset(repo, getset(repo, subset, x), y) 248 return getset(repo, getset(repo, subset, x), y)
249 249
250 def orset(repo, subset, x, y): 250 def orset(repo, subset, x, y):
251 xl = getset(repo, subset, x) 251 xl = getset(repo, subset, x)
252 s = set(xl) 252 s = xl.set()
253 yl = getset(repo, [r for r in subset if r not in s], y) 253 yl = getset(repo, baseset([r for r in subset if r not in s]), y)
254 return baseset(xl + yl) 254 return baseset(xl + yl)
255 255
256 def notset(repo, subset, x): 256 def notset(repo, subset, x):
257 s = set(getset(repo, subset, x)) 257 s = getset(repo, subset, x).set()
258 return baseset([r for r in subset if r not in s]) 258 return baseset([r for r in subset if r not in s])
259 259
260 def listset(repo, subset, a, b): 260 def listset(repo, subset, a, b):
261 raise error.ParseError(_("can't use a list in this context")) 261 raise error.ParseError(_("can't use a list in this context"))
262 262
310 def _ancestors(repo, subset, x, followfirst=False): 310 def _ancestors(repo, subset, x, followfirst=False):
311 args = getset(repo, baseset(repo), x) 311 args = getset(repo, baseset(repo), x)
312 if not args: 312 if not args:
313 return baseset([]) 313 return baseset([])
314 s = set(_revancestors(repo, args, followfirst)) | set(args) 314 s = set(_revancestors(repo, args, followfirst)) | set(args)
315 return baseset([r for r in subset if r in s]) 315 ss = subset.set()
316 return baseset([r for r in ss if r in s])
316 317
317 def ancestors(repo, subset, x): 318 def ancestors(repo, subset, x):
318 """``ancestors(set)`` 319 """``ancestors(set)``
319 Changesets that are ancestors of a changeset in set. 320 Changesets that are ancestors of a changeset in set.
320 """ 321 """
338 cl = repo.changelog 339 cl = repo.changelog
339 for r in getset(repo, cl, x): 340 for r in getset(repo, cl, x):
340 for i in range(n): 341 for i in range(n):
341 r = cl.parentrevs(r)[0] 342 r = cl.parentrevs(r)[0]
342 ps.add(r) 343 ps.add(r)
343 return baseset([r for r in subset if r in ps]) 344 s = subset.set()
345 return baseset([r for r in s if r in ps])
344 346
345 def author(repo, subset, x): 347 def author(repo, subset, x):
346 """``author(string)`` 348 """``author(string)``
347 Alias for ``user(string)``. 349 Alias for ``user(string)``.
348 """ 350 """
365 - ``current`` : the cset currently being bisected 367 - ``current`` : the cset currently being bisected
366 """ 368 """
367 # i18n: "bisect" is a keyword 369 # i18n: "bisect" is a keyword
368 status = getstring(x, _("bisect requires a string")).lower() 370 status = getstring(x, _("bisect requires a string")).lower()
369 state = set(hbisect.get(repo, status)) 371 state = set(hbisect.get(repo, status))
370 return baseset([r for r in subset if r in state]) 372 s = subset.set()
373 return baseset([r for r in s if r in state])
371 374
372 # Backward-compatibility 375 # Backward-compatibility
373 # - no help entry so that we do not advertise it any more 376 # - no help entry so that we do not advertise it any more
374 def bisected(repo, subset, x): 377 def bisected(repo, subset, x):
375 return bisect(repo, subset, x) 378 return bisect(repo, subset, x)
404 raise util.Abort(_("no bookmarks exist that match '%s'") 407 raise util.Abort(_("no bookmarks exist that match '%s'")
405 % pattern) 408 % pattern)
406 bmrevs = set() 409 bmrevs = set()
407 for bmrev in matchrevs: 410 for bmrev in matchrevs:
408 bmrevs.add(repo[bmrev].rev()) 411 bmrevs.add(repo[bmrev].rev())
409 return baseset([r for r in subset if r in bmrevs]) 412 s = subset.set()
413 return baseset([r for r in s if r in bmrevs])
410 414
411 bms = set([repo[r].rev() 415 bms = set([repo[r].rev()
412 for r in repo._bookmarks.values()]) 416 for r in repo._bookmarks.values()])
413 return baseset([r for r in subset if r in bms]) 417 s = subset.set()
418 return baseset([r for r in s if r in bms])
414 419
415 def branch(repo, subset, x): 420 def branch(repo, subset, x):
416 """``branch(string or set)`` 421 """``branch(string or set)``
417 All changesets belonging to the given branch or the branches of the given 422 All changesets belonging to the given branch or the branches of the given
418 changesets. 423 changesets.
438 443
439 s = getset(repo, baseset(repo), x) 444 s = getset(repo, baseset(repo), x)
440 b = set() 445 b = set()
441 for r in s: 446 for r in s:
442 b.add(repo[r].branch()) 447 b.add(repo[r].branch())
443 s = set(s) 448 s = s.set()
444 return baseset([r for r in subset if r in s or repo[r].branch() in b]) 449 return baseset([r for r in subset if r in s or repo[r].branch() in b])
445 450
446 def bumped(repo, subset, x): 451 def bumped(repo, subset, x):
447 """``bumped()`` 452 """``bumped()``
448 Mutable changesets marked as successors of public changesets. 453 Mutable changesets marked as successors of public changesets.
513 518
514 def children(repo, subset, x): 519 def children(repo, subset, x):
515 """``children(set)`` 520 """``children(set)``
516 Child changesets of changesets in set. 521 Child changesets of changesets in set.
517 """ 522 """
518 s = set(getset(repo, baseset(repo), x)) 523 s = getset(repo, baseset(repo), x).set()
519 cs = _children(repo, subset, s) 524 cs = _children(repo, subset, s)
520 return baseset([r for r in subset if r in cs]) 525 return baseset([r for r in subset if r in cs])
521 526
522 def closed(repo, subset, x): 527 def closed(repo, subset, x):
523 """``closed()`` 528 """``closed()``
603 def _descendants(repo, subset, x, followfirst=False): 608 def _descendants(repo, subset, x, followfirst=False):
604 args = getset(repo, baseset(repo), x) 609 args = getset(repo, baseset(repo), x)
605 if not args: 610 if not args:
606 return baseset([]) 611 return baseset([])
607 s = set(_revdescendants(repo, args, followfirst)) | set(args) 612 s = set(_revdescendants(repo, args, followfirst)) | set(args)
608 return baseset([r for r in subset if r in s]) 613 ss = subset.set()
614 return baseset([r for r in ss if r in s])
609 615
610 def descendants(repo, subset, x): 616 def descendants(repo, subset, x):
611 """``descendants(set)`` 617 """``descendants(set)``
612 Changesets which are descendants of changesets in set. 618 Changesets which are descendants of changesets in set.
613 """ 619 """
623 Changesets that were created by a graft, transplant or rebase operation, 629 Changesets that were created by a graft, transplant or rebase operation,
624 with the given revisions specified as the source. Omitting the optional set 630 with the given revisions specified as the source. Omitting the optional set
625 is the same as passing all(). 631 is the same as passing all().
626 """ 632 """
627 if x is not None: 633 if x is not None:
628 args = set(getset(repo, baseset(repo), x)) 634 args = getset(repo, baseset(repo), x).set()
629 else: 635 else:
630 args = set(getall(repo, baseset(repo), x)) 636 args = getall(repo, baseset(repo), x).set()
631 637
632 dests = set() 638 dests = set()
633 639
634 # subset contains all of the possible destinations that can be returned, so 640 # subset contains all of the possible destinations that can be returned, so
635 # iterate over them and see if their source(s) were provided in the args. 641 # iterate over them and see if their source(s) were provided in the args.
743 if m(f): 749 if m(f):
744 fl = repo.file(f) 750 fl = repo.file(f)
745 for fr in fl: 751 for fr in fl:
746 s.add(fl.linkrev(fr)) 752 s.add(fl.linkrev(fr))
747 753
748 return baseset([r for r in subset if r in s]) 754 ss = subset.set()
755 return baseset([r for r in ss if r in s])
749 756
750 def first(repo, subset, x): 757 def first(repo, subset, x):
751 """``first(set, [n])`` 758 """``first(set, [n])``
752 An alias for limit(). 759 An alias for limit().
753 """ 760 """
766 else: 773 else:
767 return baseset([]) 774 return baseset([])
768 else: 775 else:
769 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()]) 776 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
770 777
771 return baseset([r for r in subset if r in s]) 778 ss = subset.set()
779 return baseset([r for r in ss if r in s])
772 780
773 def follow(repo, subset, x): 781 def follow(repo, subset, x):
774 """``follow([file])`` 782 """``follow([file])``
775 An alias for ``::.`` (ancestors of the working copy's first parent). 783 An alias for ``::.`` (ancestors of the working copy's first parent).
776 If a filename is specified, the history of the given file is followed, 784 If a filename is specified, the history of the given file is followed,
895 # i18n: "head" is a keyword 903 # i18n: "head" is a keyword
896 getargs(x, 0, 0, _("head takes no arguments")) 904 getargs(x, 0, 0, _("head takes no arguments"))
897 hs = set() 905 hs = set()
898 for b, ls in repo.branchmap().iteritems(): 906 for b, ls in repo.branchmap().iteritems():
899 hs.update(repo[h].rev() for h in ls) 907 hs.update(repo[h].rev() for h in ls)
900 return baseset([r for r in subset if r in hs]) 908 s = subset.set()
909 return baseset([r for r in s if r in hs])
901 910
902 def heads(repo, subset, x): 911 def heads(repo, subset, x):
903 """``heads(set)`` 912 """``heads(set)``
904 Members of set with no children in set. 913 Members of set with no children in set.
905 """ 914 """
906 s = getset(repo, subset, x) 915 s = getset(repo, subset, x).set()
907 ps = set(parents(repo, subset, x)) 916 ps = parents(repo, subset, x).set()
908 return baseset([r for r in s if r not in ps]) 917 return baseset([r for r in s if r not in ps])
909 918
910 def hidden(repo, subset, x): 919 def hidden(repo, subset, x):
911 """``hidden()`` 920 """``hidden()``
912 Hidden changesets. 921 Hidden changesets.
943 # i18n: "limit" is a keyword 952 # i18n: "limit" is a keyword
944 lim = int(getstring(l[1], _("limit requires a number"))) 953 lim = int(getstring(l[1], _("limit requires a number")))
945 except (TypeError, ValueError): 954 except (TypeError, ValueError):
946 # i18n: "limit" is a keyword 955 # i18n: "limit" is a keyword
947 raise error.ParseError(_("limit expects a number")) 956 raise error.ParseError(_("limit expects a number"))
948 ss = set(subset) 957 ss = subset.set()
949 os = getset(repo, baseset(repo), l[0])[:lim] 958 os = getset(repo, baseset(repo), l[0])[:lim]
950 return baseset([r for r in os if r in ss]) 959 return baseset([r for r in os if r in ss])
951 960
952 def last(repo, subset, x): 961 def last(repo, subset, x):
953 """``last(set, [n])`` 962 """``last(set, [n])``
961 # i18n: "last" is a keyword 970 # i18n: "last" is a keyword
962 lim = int(getstring(l[1], _("last requires a number"))) 971 lim = int(getstring(l[1], _("last requires a number")))
963 except (TypeError, ValueError): 972 except (TypeError, ValueError):
964 # i18n: "last" is a keyword 973 # i18n: "last" is a keyword
965 raise error.ParseError(_("last expects a number")) 974 raise error.ParseError(_("last expects a number"))
966 ss = set(subset) 975 ss = subset.set()
967 os = getset(repo, baseset(repo), l[0])[-lim:] 976 os = getset(repo, baseset(repo), l[0])[-lim:]
968 return baseset([r for r in os if r in ss]) 977 return baseset([r for r in os if r in ss])
969 978
970 def maxrev(repo, subset, x): 979 def maxrev(repo, subset, x):
971 """``max(set)`` 980 """``max(set)``
1060 same as passing all(). If a changeset created by these operations is itself 1069 same as passing all(). If a changeset created by these operations is itself
1061 specified as a source for one of these operations, only the source changeset 1070 specified as a source for one of these operations, only the source changeset
1062 for the first operation is selected. 1071 for the first operation is selected.
1063 """ 1072 """
1064 if x is not None: 1073 if x is not None:
1065 args = set(getset(repo, baseset(repo), x)) 1074 args = getset(repo, baseset(repo), x).set()
1066 else: 1075 else:
1067 args = set(getall(repo, baseset(repo), x)) 1076 args = getall(repo, baseset(repo), x).set()
1068 1077
1069 def _firstsrc(rev): 1078 def _firstsrc(rev):
1070 src = _getrevsource(repo, rev) 1079 src = _getrevsource(repo, rev)
1071 if src is None: 1080 if src is None:
1072 return None 1081 return None
1077 if prev is None: 1086 if prev is None:
1078 return src 1087 return src
1079 src = prev 1088 src = prev
1080 1089
1081 o = set([_firstsrc(r) for r in args]) 1090 o = set([_firstsrc(r) for r in args])
1082 return baseset([r for r in subset if r in o]) 1091 s = subset.set()
1092 return baseset([r for r in s if r in o])
1083 1093
1084 def outgoing(repo, subset, x): 1094 def outgoing(repo, subset, x):
1085 """``outgoing([path])`` 1095 """``outgoing([path])``
1086 Changesets not found in the specified destination repository, or the 1096 Changesets not found in the specified destination repository, or the
1087 default push location. 1097 default push location.
1100 repo.ui.pushbuffer() 1110 repo.ui.pushbuffer()
1101 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs) 1111 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1102 repo.ui.popbuffer() 1112 repo.ui.popbuffer()
1103 cl = repo.changelog 1113 cl = repo.changelog
1104 o = set([cl.rev(r) for r in outgoing.missing]) 1114 o = set([cl.rev(r) for r in outgoing.missing])
1105 return baseset([r for r in subset if r in o]) 1115 s = subset.set()
1116 return baseset([r for r in s if r in o])
1106 1117
1107 def p1(repo, subset, x): 1118 def p1(repo, subset, x):
1108 """``p1([set])`` 1119 """``p1([set])``
1109 First parent of changesets in set, or the working directory. 1120 First parent of changesets in set, or the working directory.
1110 """ 1121 """
1114 1125
1115 ps = set() 1126 ps = set()
1116 cl = repo.changelog 1127 cl = repo.changelog
1117 for r in getset(repo, baseset(repo), x): 1128 for r in getset(repo, baseset(repo), x):
1118 ps.add(cl.parentrevs(r)[0]) 1129 ps.add(cl.parentrevs(r)[0])
1119 return baseset([r for r in subset if r in ps]) 1130 s = subset.set()
1131 return baseset([r for r in s if r in ps])
1120 1132
1121 def p2(repo, subset, x): 1133 def p2(repo, subset, x):
1122 """``p2([set])`` 1134 """``p2([set])``
1123 Second parent of changesets in set, or the working directory. 1135 Second parent of changesets in set, or the working directory.
1124 """ 1136 """
1132 1144
1133 ps = set() 1145 ps = set()
1134 cl = repo.changelog 1146 cl = repo.changelog
1135 for r in getset(repo, baseset(repo), x): 1147 for r in getset(repo, baseset(repo), x):
1136 ps.add(cl.parentrevs(r)[1]) 1148 ps.add(cl.parentrevs(r)[1])
1137 return baseset([r for r in subset if r in ps]) 1149 s = subset.set()
1150 return baseset([r for r in s if r in ps])
1138 1151
1139 def parents(repo, subset, x): 1152 def parents(repo, subset, x):
1140 """``parents([set])`` 1153 """``parents([set])``
1141 The set of all parents for all changesets in set, or the working directory. 1154 The set of all parents for all changesets in set, or the working directory.
1142 """ 1155 """
1146 1159
1147 ps = set() 1160 ps = set()
1148 cl = repo.changelog 1161 cl = repo.changelog
1149 for r in getset(repo, baseset(repo), x): 1162 for r in getset(repo, baseset(repo), x):
1150 ps.update(cl.parentrevs(r)) 1163 ps.update(cl.parentrevs(r))
1151 return baseset([r for r in subset if r in ps]) 1164 s = subset.set()
1165 return baseset([r for r in s if r in ps])
1152 1166
1153 def parentspec(repo, subset, x, n): 1167 def parentspec(repo, subset, x, n):
1154 """``set^0`` 1168 """``set^0``
1155 The set. 1169 The set.
1156 ``set^1`` (or ``set^``), ``set^2`` 1170 ``set^1`` (or ``set^``), ``set^2``
1171 ps.add(cl.parentrevs(r)[0]) 1185 ps.add(cl.parentrevs(r)[0])
1172 elif n == 2: 1186 elif n == 2:
1173 parents = cl.parentrevs(r) 1187 parents = cl.parentrevs(r)
1174 if len(parents) > 1: 1188 if len(parents) > 1:
1175 ps.add(parents[1]) 1189 ps.add(parents[1])
1176 return baseset([r for r in subset if r in ps]) 1190 s = subset.set()
1191 return baseset([r for r in s if r in ps])
1177 1192
1178 def present(repo, subset, x): 1193 def present(repo, subset, x):
1179 """``present(set)`` 1194 """``present(set)``
1180 An empty set, if any revision in set isn't found; otherwise, 1195 An empty set, if any revision in set isn't found; otherwise,
1181 all revisions in set. 1196 all revisions in set.
1373 def reverse(repo, subset, x): 1388 def reverse(repo, subset, x):
1374 """``reverse(set)`` 1389 """``reverse(set)``
1375 Reverse order of set. 1390 Reverse order of set.
1376 """ 1391 """
1377 l = getset(repo, subset, x) 1392 l = getset(repo, subset, x)
1378 if not isinstance(l, list):
1379 l = baseset(l)
1380 l.reverse() 1393 l.reverse()
1381 return l 1394 return l
1382 1395
1383 def roots(repo, subset, x): 1396 def roots(repo, subset, x):
1384 """``roots(set)`` 1397 """``roots(set)``
1385 Changesets in set with no parent changeset in set. 1398 Changesets in set with no parent changeset in set.
1386 """ 1399 """
1387 s = set(getset(repo, baseset(repo.changelog), x)) 1400 s = getset(repo, baseset(repo.changelog), x).set()
1388 subset = baseset([r for r in subset if r in s]) 1401 subset = baseset([r for r in subset if r in s])
1389 cs = _children(repo, subset, s) 1402 cs = _children(repo, subset, s)
1390 return baseset([r for r in subset if r not in cs]) 1403 return baseset([r for r in subset if r not in cs])
1391 1404
1392 def secret(repo, subset, x): 1405 def secret(repo, subset, x):
1548 # for internal use 1561 # for internal use
1549 def _list(repo, subset, x): 1562 def _list(repo, subset, x):
1550 s = getstring(x, "internal error") 1563 s = getstring(x, "internal error")
1551 if not s: 1564 if not s:
1552 return baseset([]) 1565 return baseset([])
1553 if not isinstance(subset, set):
1554 subset = set(subset)
1555 ls = [repo[r].rev() for r in s.split('\0')] 1566 ls = [repo[r].rev() for r in s.split('\0')]
1556 return baseset([r for r in ls if r in subset]) 1567 s = subset.set()
1568 return baseset([r for r in ls if r in s])
1557 1569
1558 symbols = { 1570 symbols = {
1559 "adds": adds, 1571 "adds": adds,
1560 "all": getall, 1572 "all": getall,
1561 "ancestor": ancestor, 1573 "ancestor": ancestor,
2046 if tree[0] == 'func': 2058 if tree[0] == 'func':
2047 funcs.add(tree[1][1]) 2059 funcs.add(tree[1][1])
2048 return funcs 2060 return funcs
2049 2061
2050 class baseset(list): 2062 class baseset(list):
2051 pass 2063 def __init__(self, data):
2064 super(baseset, self).__init__(data)
2065 self._set = None
2066
2067 def set(self):
2068 if not self._set:
2069 self._set = set(self)
2070 return self._set
2052 2071
2053 # tell hggettext to extract docstrings from these functions: 2072 # tell hggettext to extract docstrings from these functions:
2054 i18nfunctions = symbols.values() 2073 i18nfunctions = symbols.values()