revset: add 'only' revset
authorDurham Goode <durham@fb.com>
Sat, 16 Nov 2013 08:57:08 -0800
changeset 20613 10433163bf57
parent 20612 60c308b932eb
child 20614 1bc68ff4e0a2
revset: add 'only' revset Adds a only() revset that has two forms: only(<set>) is equivalent to "::<set> - ::(heads() - heads(<set>::))" only(<include>,<exclude>) is equivalent to "::<include> - ::<exclude>" On a large repo, this implementation can process/traverse 50,000 revs in 0.7 seconds, versus 4.2 seconds using "::<include> - ::<exclude>". This is useful for performing histedits on your branch: hg histedit -r 'first(only(.))' Or lifting branch foo off of branch bar: hg rebase -d @ -s 'only(foo, bar)' Or a variety of other uses.
mercurial/revset.py
tests/test-revset.t
--- a/mercurial/revset.py	Thu Feb 06 17:42:08 2014 -0800
+++ b/mercurial/revset.py	Sat Nov 16 08:57:08 2013 -0800
@@ -9,6 +9,7 @@
 import parser, util, error, discovery, hbisect, phases
 import node
 import match as matchmod
+import ancestor as ancestormod
 from i18n import _
 import encoding
 import obsolete as obsmod
@@ -351,6 +352,26 @@
     kind, pattern, matcher = _substringmatcher(n)
     return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())))
 
+def only(repo, subset, x):
+    """``only(set, [set])``
+    Changesets that are ancestors of the first set that are not ancestors
+    of any other head in the repo. If a second set is specified, the result
+    is ancestors of the first set that are not ancestors of the second set
+    (i.e. ::<set1> - ::<set2>).
+    """
+    cl = repo.changelog
+    args = getargs(x, 1, 2, _('only takes one or two arguments'))
+    include = getset(repo, spanset(repo), args[0]).set()
+    if len(args) == 1:
+        descendants = set(_revdescendants(repo, include, False))
+        exclude = [rev for rev in cl.headrevs()
+            if not rev in descendants and not rev in include]
+    else:
+        exclude = getset(repo, spanset(repo), args[1])
+
+    results = set(ancestormod.missingancestors(include, exclude, cl.parentrevs))
+    return lazyset(subset, lambda x: x in results)
+
 def bisect(repo, subset, x):
     """``bisect(string)``
     Changesets marked in the specified bisect status:
@@ -1606,6 +1627,7 @@
     "ancestors": ancestors,
     "_firstancestors": _firstancestors,
     "author": author,
+    "only": only,
     "bisect": bisect,
     "bisected": bisected,
     "bookmark": bookmark,
--- a/tests/test-revset.t	Thu Feb 06 17:42:08 2014 -0800
+++ b/tests/test-revset.t	Sat Nov 16 08:57:08 2013 -0800
@@ -367,6 +367,22 @@
   4
   $ log 'id(5)'
   2
+  $ log 'only(9)'
+  8
+  9
+  $ log 'only(8)'
+  8
+  $ log 'only(9, 5)'
+  2
+  4
+  8
+  9
+  $ log 'only(7 + 9, 5 + 2)'
+  4
+  6
+  7
+  8
+  9
   $ log 'outgoing()'
   8
   9