mercurial/revset.py
changeset 16409 2cbd7dd0cc1f
parent 16402 1fb2f1400ea8
child 16411 4c2edcd84175
--- a/mercurial/revset.py	Wed Apr 11 11:22:40 2012 +0200
+++ b/mercurial/revset.py	Wed Apr 11 11:25:34 2012 +0200
@@ -13,6 +13,39 @@
 from i18n import _
 import encoding
 
+def _revancestors(repo, revs, followfirst):
+    """Like revlog.ancestors(), but supports followfirst."""
+    cut = followfirst and 1 or None
+    cl = repo.changelog
+    visit = list(revs)
+    seen = set([nodemod.nullrev])
+    while visit:
+        for parent in cl.parentrevs(visit.pop(0))[:cut]:
+            if parent not in seen:
+                visit.append(parent)
+                seen.add(parent)
+                yield parent
+
+def _revdescendants(repo, revs, followfirst):
+    """Like revlog.descendants() but supports followfirst."""
+    cut = followfirst and 1 or None
+    cl = repo.changelog
+    first = min(revs)
+    if first == nodemod.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 xrange(first + 1, len(cl)):
+        for x in cl.parentrevs(i)[:cut]:
+            if x != nodemod.nullrev and x in seen:
+                seen.add(i)
+                yield i
+                break
+
 elements = {
     "(": (20, ("group", 1, ")"), ("func", 1, ")")),
     "~": (18, None, ("ancestor", 18)),
@@ -203,15 +236,23 @@
 
     return [r for r in an if r in subset]
 
+def _ancestors(repo, subset, x, followfirst=False):
+    args = getset(repo, range(len(repo)), x)
+    if not args:
+        return []
+    s = set(_revancestors(repo, args, followfirst)) | set(args)
+    return [r for r in subset if r in s]
+
 def ancestors(repo, subset, x):
     """``ancestors(set)``
     Changesets that are ancestors of a changeset in set.
     """
-    args = getset(repo, range(len(repo)), x)
-    if not args:
-        return []
-    s = set(repo.changelog.ancestors(*args)) | set(args)
-    return [r for r in subset if r in s]
+    return _ancestors(repo, subset, x)
+
+def _firstancestors(repo, subset, x):
+    # ``_firstancestors(set)``
+    # Like ``ancestors(set)`` but follows only the first parents.
+    return _ancestors(repo, subset, x, followfirst=True)
 
 def ancestorspec(repo, subset, x, n):
     """``set~n``
@@ -395,15 +436,23 @@
             l.append(r)
     return l
 
+def _descendants(repo, subset, x, followfirst=False):
+    args = getset(repo, range(len(repo)), x)
+    if not args:
+        return []
+    s = set(_revdescendants(repo, args, followfirst)) | set(args)
+    return [r for r in subset if r in s]
+
 def descendants(repo, subset, x):
     """``descendants(set)``
     Changesets which are descendants of changesets in set.
     """
-    args = getset(repo, range(len(repo)), x)
-    if not args:
-        return []
-    s = set(repo.changelog.descendants(*args)) | set(args)
-    return [r for r in subset if r in s]
+    return _descendants(repo, subset, x)
+
+def _firstdescendants(repo, subset, x):
+    # ``_firstdescendants(set)``
+    # Like ``descendants(set)`` but follows only the first parents.
+    return _descendants(repo, subset, x, followfirst=True)
 
 def draft(repo, subset, x):
     """``draft()``
@@ -454,16 +503,7 @@
         else:
             return []
     else:
-        cut = followfirst and 1 or None
-        cl = repo.changelog
-        s = set()
-        visit = [c.rev()]
-        while visit:
-            for prev in cl.parentrevs(visit.pop(0))[:cut]:
-                if prev not in s and prev != nodemod.nullrev:
-                    visit.append(prev)
-                    s.add(prev)
-        s.add(c.rev())
+        s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
 
     return [r for r in subset if r in s]
 
@@ -1055,6 +1095,7 @@
     "all": getall,
     "ancestor": ancestor,
     "ancestors": ancestors,
+    "_firstancestors": _firstancestors,
     "author": author,
     "bisect": bisect,
     "bisected": bisected,
@@ -1066,6 +1107,7 @@
     "date": date,
     "desc": desc,
     "descendants": descendants,
+    "_firstdescendants": _firstdescendants,
     "draft": draft,
     "file": hasfile,
     "filelog": filelog,