diff --git a/mercurial/revset.py b/mercurial/revset.py --- a/mercurial/revset.py +++ b/mercurial/revset.py @@ -57,6 +57,37 @@ raise error.ParseError(_("missing argument")) return methods[x[0]](repo, subset, *x[1:], order=order) +def intersect(subset, newset, order): + """intersect two revsets with order preserved as requested + + If order is "followorder", preserve subset's order. If order is + "defineorder", preserve newset's order. + + A typical use of this function is:: + + @revsetpredicate('myrevset', takeorder=True) + def myrevset(repo, subset, x, order): + newset = ... # a smartset that implements "myrevset" + return revset.intersect(subset, newset, order) + """ + # ordering does not matter if there are at most 1 revision (test quickly) + if isinstance(newset, baseset) and len(newset) <= 1: + return newset & subset + + # "defineorder" by an unordered set is bad, enforce "followorder" + if not util.safehasattr(newset, 'isascending'): + return subset & newset + + if order == followorder: + return subset & newset + elif order == defineorder: + return newset & subset + + assert order == anyorder + + # newset is usually smaller, prefer it on the left + return newset & subset + def _getrevsource(repo, r): extra = repo[r].extra() for label in ('source', 'transplant_source', 'rebase_source'): @@ -113,17 +144,13 @@ else: r = spanset(repo, m, n - 1) - if order == defineorder: - return r & subset - else: - # carrying the sorting over when possible would be more efficient - return subset & r + return intersect(subset, r, order) def dagrange(repo, subset, x, y, order): r = fullreposet(repo) xs = dagop.reachableroots(repo, getset(repo, r, x), getset(repo, r, y), includepath=True) - return subset & xs + return intersect(subset, xs, order) def andset(repo, subset, x, y, order): if order == anyorder: @@ -1143,9 +1170,7 @@ raise error.ParseError(_("negative offset")) os = getset(repo, fullreposet(repo), args['set'], defineorder) ls = os.slice(ofs, ofs + lim) - if order == followorder and lim > 1: - return subset & ls - return ls & subset + return intersect(subset, ls, order) @predicate('last(set, [n])', safe=True, takeorder=True) def last(repo, subset, x, order): @@ -1162,10 +1187,8 @@ os = getset(repo, fullreposet(repo), l[0], defineorder) os.reverse() ls = os.slice(0, lim) - if order == followorder and lim > 1: - return subset & ls ls.reverse() - return ls & subset + return intersect(subset, ls, order) @predicate('max(set)', safe=True) def maxrev(repo, subset, x): @@ -1523,7 +1546,7 @@ parents = repo[r].parents() if len(parents) == 2: ps.add(parents[1].rev()) - return subset & ps + return intersect(subset, ps, order) @predicate('present(set)', safe=True, takeorder=True) def present(repo, subset, x, order): diff --git a/tests/test-revset.t b/tests/test-revset.t --- a/tests/test-revset.t +++ b/tests/test-revset.t @@ -1477,7 +1477,7 @@ * set: , - > + > 0 1 @@ -1978,7 +1978,7 @@ , >, >> + >> 3 0 @@ -2403,8 +2403,8 @@ ('string', '0\x002\x001')))) * set: , - > + , + > 1 'A & B' can be rewritten as 'B & A' by weight. When ordering needs to be @@ -2521,13 +2521,11 @@ ('symbol', '2')))))) * set: , - > + , + > 0 + 1 2 - 1 - - WRONG: should take dagrange order (0, 1, 2) 'A + B' can be rewritten to 'B + A' by weight only when the order doesn't matter (e.g. 'X & (A + B)' can be 'X & (B + A)', but '(A + B) & X' can't):