[PATCH] revset: add exclusive revset
Durham Goode
durham at fb.com
Fri Nov 15 22:36:10 CST 2013
# HG changeset patch
# User Durham Goode <durham at fb.com>
# Date 1384575318 28800
# Fri Nov 15 20:15:18 2013 -0800
# Node ID 029191a9eb775c586ed02c201d0c555053ea44da
# Parent aa80446aacc3b1574211649cd8f190250b6b04b3
revset: add exclusive revset
Adds a exclusive revset that has two forms:
exclusive(<set>) is equivalent to "::<set> - ::(heads() - <set>)"
exclusive(<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>".
diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -5,7 +5,7 @@
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
-import re
+import re, heapq
import parser, util, error, discovery, hbisect, phases
import node
import match as matchmod
@@ -346,6 +346,60 @@
kind, pattern, matcher = _substringmatcher(n)
return [r for r in subset if matcher(encoding.lower(repo[r].user()))]
+def exclusive(repo, subset, x):
+ """``exclusive(set, [set])
+ Changesets that are ancestors of the first set, but not the second.
+ If no second set is specified, it is assumed to be the heads not
+ in the first set.
+ """
+ cl = repo.changelog
+ args = getargs(x, 1, 2, _('exclusive takes one or two arguments'))
+ include = set(getset(repo, list(repo), args[0]))
+ if len(args) == 1:
+ exclude = [rev for rev in cl.headrevs() if not rev in include]
+ else:
+ exclude = getset(repo, list(repo), args[1])
+
+ def buildheap(revs):
+ # heapq is only a min heap, so use negative revs
+ heap = list([-rev for rev in revs])
+ heapq.heapify(heap)
+ return heap
+
+ def pushparents(heap, rev):
+ for parentrev in cl.parentrevs(rev):
+ if parentrev != node.nullrev:
+ heapq.heappush(heap, -parentrev)
+
+ def pop(heap):
+ rev = heapq.heappop(heap)
+ # the heap can contain duplicates, so skip them
+ while heap and heap[0] == rev:
+ heapq.heappop(heap)
+
+ includeheap = buildheap(include)
+ excludeheap = buildheap(exclude)
+
+ # Walk down the include/exclude tree, highest rev first.
+ # Accept included revs, as long as they aren't in exclude.
+ results = []
+ while includeheap:
+ maxinclude = -includeheap[0]
+ maxexclude = -excludeheap[0] if excludeheap else nullrev
+ if maxexclude == maxinclude:
+ pop(includeheap)
+ pop(excludeheap)
+ pushparents(excludeheap, maxexclude)
+ elif maxinclude > maxexclude:
+ results.append(maxinclude)
+ pop(includeheap)
+ pushparents(includeheap, maxinclude)
+ else:
+ pop(excludeheap)
+ pushparents(excludeheap, maxexclude)
+
+ return results
+
def bisect(repo, subset, x):
"""``bisect(string)``
Changesets marked in the specified bisect status:
@@ -1538,6 +1592,7 @@
"ancestors": ancestors,
"_firstancestors": _firstancestors,
"author": author,
+ "exclusive": exclusive,
"bisect": bisect,
"bisected": bisected,
"bookmark": bookmark,
diff --git a/tests/test-revset.t b/tests/test-revset.t
--- a/tests/test-revset.t
+++ b/tests/test-revset.t
@@ -284,6 +284,20 @@
7
8
9
+ $ log 'exclusive(9)'
+ 9
+ 8
+ $ log 'exclusive(9, 5)'
+ 9
+ 8
+ 4
+ 2
+ $ log 'exclusive(7 + 9, 5 + 2)'
+ 9
+ 8
+ 7
+ 6
+ 4
$ log 'file("b*")'
1
4
More information about the Mercurial-devel
mailing list