diff --git a/mercurial/dagop.py b/mercurial/dagop.py --- a/mercurial/dagop.py +++ b/mercurial/dagop.py @@ -28,7 +28,7 @@ baseset = smartset.baseset generatorset = smartset.generatorset # possible maximum depth between null and wdir() -_maxlogdepth = 0x80000000 +maxlogdepth = 0x80000000 def _walkrevtree(pfunc, revs, startdepth, stopdepth, reverse): """Walk DAG using 'pfunc' from the given 'revs' nodes @@ -42,7 +42,7 @@ def _walkrevtree(pfunc, revs, startdepth if startdepth is None: startdepth = 0 if stopdepth is None: - stopdepth = _maxlogdepth + stopdepth = maxlogdepth if stopdepth == 0: return if stopdepth < 0: @@ -221,7 +221,7 @@ def revdescendants(repo, revs, followfir Scan ends at the stopdepth (exlusive) if specified. Revisions found earlier than the startdepth are omitted. """ - if startdepth is None and stopdepth is None: + if startdepth is None and (stopdepth is None or stopdepth == maxlogdepth): gen = _genrevdescendants(repo, revs, followfirst) else: gen = _genrevdescendantsofdepth(repo, revs, followfirst, diff --git a/mercurial/revset.py b/mercurial/revset.py --- a/mercurial/revset.py +++ b/mercurial/revset.py @@ -225,24 +225,82 @@ def notset(repo, subset, x, order): def relationset(repo, subset, x, y, order): raise error.ParseError(_("can't use a relation in this context")) -def generationsrel(repo, subset, x, rel, n, order): - # TODO: support range, rewrite tests, and drop startdepth argument - # from ancestors() and descendants() predicates - if n <= 0: - n = -n - return _ancestors(repo, subset, x, startdepth=n, stopdepth=n + 1) - else: - return _descendants(repo, subset, x, startdepth=n, stopdepth=n + 1) +def _splitrange(a, b): + """Split range with bounds a and b into two ranges at 0 and return two + tuples of numbers for use as startdepth and stopdepth arguments of + revancestors and revdescendants. + + >>> _splitrange(-10, -5) # [-10:-5] + ((5, 11), (None, None)) + >>> _splitrange(5, 10) # [5:10] + ((None, None), (5, 11)) + >>> _splitrange(-10, 10) # [-10:10] + ((0, 11), (0, 11)) + >>> _splitrange(-10, 0) # [-10:0] + ((0, 11), (None, None)) + >>> _splitrange(0, 10) # [0:10] + ((None, None), (0, 11)) + >>> _splitrange(0, 0) # [0:0] + ((0, 1), (None, None)) + >>> _splitrange(1, -1) # [1:-1] + ((None, None), (None, None)) + """ + ancdepths = (None, None) + descdepths = (None, None) + if a == b == 0: + ancdepths = (0, 1) + if a < 0: + ancdepths = (-min(b, 0), -a + 1) + if b > 0: + descdepths = (max(a, 0), b + 1) + return ancdepths, descdepths + +def generationsrel(repo, subset, x, rel, a, b, order): + # TODO: rewrite tests, and drop startdepth argument from ancestors() and + # descendants() predicates + (ancstart, ancstop), (descstart, descstop) = _splitrange(a, b) + + if ancstart is None and descstart is None: + return baseset() + + revs = getset(repo, fullreposet(repo), x) + if not revs: + return baseset() + + if ancstart is not None and descstart is not None: + s = dagop.revancestors(repo, revs, False, ancstart, ancstop) + s += dagop.revdescendants(repo, revs, False, descstart, descstop) + elif ancstart is not None: + s = dagop.revancestors(repo, revs, False, ancstart, ancstop) + elif descstart is not None: + s = dagop.revdescendants(repo, revs, False, descstart, descstop) + + return subset & s def relsubscriptset(repo, subset, x, y, z, order): # this is pretty basic implementation of 'x#y[z]' operator, still # experimental so undocumented. see the wiki for further ideas. # https://www.mercurial-scm.org/wiki/RevsetOperatorPlan rel = getsymbol(y) - n = getinteger(z, _("relation subscript must be an integer")) + try: + a, b = getrange(z, '') + except error.ParseError: + a = getinteger(z, _("relation subscript must be an integer")) + b = a + else: + def getbound(i): + if i is None: + return None + msg = _("relation subscript bounds must be integers") + return getinteger(i, msg) + a, b = [getbound(i) for i in (a, b)] + if a is None: + a = -(dagop.maxlogdepth - 1) + if b is None: + b = +(dagop.maxlogdepth - 1) if rel in subscriptrelations: - return subscriptrelations[rel](repo, subset, x, rel, n, order) + return subscriptrelations[rel](repo, subset, x, rel, a, b, order) relnames = [r for r in subscriptrelations.keys() if len(r) > 1] raise error.UnknownIdentifier(rel, relnames) diff --git a/tests/test-doctest.py b/tests/test-doctest.py --- a/tests/test-doctest.py +++ b/tests/test-doctest.py @@ -62,6 +62,7 @@ testmod('mercurial.parser') testmod('mercurial.pycompat') testmod('mercurial.revlog') testmod('mercurial.revlogutils.deltas') +testmod('mercurial.revset') testmod('mercurial.revsetlang') testmod('mercurial.smartset') testmod('mercurial.store') diff --git a/tests/test-revset.t b/tests/test-revset.t --- a/tests/test-revset.t +++ b/tests/test-revset.t @@ -648,6 +648,9 @@ parse errors of relation, subscript and $ hg debugrevspec '.#generations[1-2]' hg: parse error: relation subscript must be an integer [255] + $ hg debugrevspec '.#generations[foo:bar]' + hg: parse error: relation subscript bounds must be integers + [255] suggested relations @@ -1274,6 +1277,30 @@ test ancestors/descendants relation subs $ log '.#g[(-1)]' 8 + $ log '6#generations[0:1]' + 6 + 7 + $ log '6#generations[-1:1]' + 4 + 5 + 6 + 7 + $ log '6#generations[0:]' + 6 + 7 + $ log '5#generations[:0]' + 0 + 1 + 3 + 5 + $ log '3#generations[:]' + 0 + 1 + 3 + 5 + 6 + 7 + $ hg debugrevspec -p parsed 'roots(:)#g[2]' * parsed: (relsubscript