diff --git a/mercurial/help/revisions.txt b/mercurial/help/revisions.txt --- a/mercurial/help/revisions.txt +++ b/mercurial/help/revisions.txt @@ -97,6 +97,7 @@ These are the supported infix operators: ``x~n`` The nth first ancestor of x; ``x~0`` is x; ``x~3`` is ``x^^^``. + For n < 0, the nth unambiguous descendent of x. ``x ## y`` Concatenate strings and identifiers into one string. diff --git a/mercurial/revset.py b/mercurial/revset.py --- a/mercurial/revset.py +++ b/mercurial/revset.py @@ -379,12 +379,33 @@ def _firstancestors(repo, subset, x): # Like ``ancestors(set)`` but follows only the first parents. return _ancestors(repo, subset, x, followfirst=True) +def _childrenspec(repo, subset, x, n, order): + """Changesets that are the Nth child of a changeset + in set. + """ + cs = set() + for r in getset(repo, fullreposet(repo), x): + for i in range(n): + c = repo[r].children() + if len(c) == 0: + break + if len(c) > 1: + raise error.RepoLookupError( + _("revision in set has more than one child")) + r = c[0] + else: + cs.add(r) + return subset & cs + def ancestorspec(repo, subset, x, n, order): """``set~n`` Changesets that are the Nth ancestor (first parents only) of a changeset in set. """ n = getinteger(n, _("~ expects a number")) + if n < 0: + # children lookup + return _childrenspec(repo, subset, x, -n, order) ps = set() cl = repo.changelog for r in getset(repo, fullreposet(repo), x): diff --git a/tests/test-revset.t b/tests/test-revset.t --- a/tests/test-revset.t +++ b/tests/test-revset.t @@ -2975,6 +2975,14 @@ parentrevspec $ log 'merge()^^^' 1 + $ log '(merge() | 0)~-1' + 7 + 1 + $ log 'merge()~-1' + 7 + $ log 'tip~-1' + $ log '(tip | merge())~-1' + 7 $ log 'merge()~0' 6 $ log 'merge()~1' @@ -2995,6 +3003,10 @@ parentrevspec hg: parse error: ^ expects a number 0, 1, or 2 [255] + $ log 'branchpoint()~-1' + abort: revision in set has more than one child! + [255] + Bogus function gets suggestions $ log 'add()' hg: parse error: unknown identifier: add