revset: changed descendants revset to use lazy generators
Performance Benchmarking:
$ time hg log -qr "0:: and 0:5"
...
real 0m3.665s
user 0m3.364s
sys 0m0.289s
$ time ./hg log -qr "0:: and 0:5"
...
real 0m0.492s
user 0m0.394s
sys 0m0.097s
--- a/mercurial/revset.py Fri Feb 07 13:44:57 2014 -0800
+++ b/mercurial/revset.py Mon Feb 10 12:26:45 2014 -0800
@@ -51,23 +51,26 @@
def _revdescendants(repo, revs, followfirst):
"""Like revlog.descendants() but supports followfirst."""
cut = followfirst and 1 or None
- cl = repo.changelog
- first = min(revs)
- nullrev = node.nullrev
- if first == nullrev:
- # Are there nodes with a null first parent and a non-null
- # second one? Maybe. Do we care? Probably not.
- for i in cl:
- yield i
- return
- seen = set(revs)
- for i in cl.revs(first + 1):
- for x in cl.parentrevs(i)[:cut]:
- if x != nullrev and x in seen:
- seen.add(i)
+ def iterate():
+ cl = repo.changelog
+ first = min(revs)
+ nullrev = node.nullrev
+ if first == nullrev:
+ # Are there nodes with a null first parent and a non-null
+ # second one? Maybe. Do we care? Probably not.
+ for i in cl:
yield i
- break
+ else:
+ seen = set(revs)
+ for i in cl.revs(first + 1):
+ for x in cl.parentrevs(i)[:cut]:
+ if x != nullrev and x in seen:
+ seen.add(i)
+ yield i
+ break
+
+ return ascgeneratorset(iterate())
def _revsbetween(repo, roots, heads):
"""Return all paths between roots and heads, inclusive of both endpoint
@@ -641,8 +644,9 @@
args = getset(repo, spanset(repo), x)
if not args:
return baseset([])
- s = set(_revdescendants(repo, args, followfirst)) | set(args)
- return subset & s
+ s = _revdescendants(repo, args, followfirst)
+ a = set(args)
+ return subset.filter(lambda r: r in s or r in a)
def descendants(repo, subset, x):
"""``descendants(set)``