[PATCH RFC] revset: lookup descendents for negative arguments to ancestor operator

David Soria Parra davidsp at fb.com
Sat May 27 17:26:09 UTC 2017


# HG changeset patch
# User David Soria Parra <davidsp at fb.com>
# Date 1495905909 25200
#      Sat May 27 10:25:09 2017 -0700
# Node ID 47c710c797624b6d5c84c493a873ea8beb563a74
# Parent  b647b923486f38d83b92089eafa9faafaa79785d
revset: lookup descendents for negative arguments to ancestor operator

Negative offsets to the `~` operator now search for descendents. The search is
aborted when a node has more than one child as we do not have a definition for
'nth child'. Optionally we can introduce such a notion and take the nth child
ordered by rev number.

The current revset language does provides a short operator for ancestor lookup
but not for descendents. This gives user a simple revset to move to the previous
changeset, e.g. `hg up '.~1'` but not to the 'next' changeset. With this change
userse can now use `.~-1` as a shortcut to move to the next changeset.
This fits better into allowing users to specify revisions via revsets and
avoiding the need for special `hg next` and `hg prev` operations.

The alternative to negative offsets is adding a new operator. We do not have
many operators in ascii left that do not require bash escaping (',', '_', and
'/' come to mind). If we decide that we should add a more convenient short
operator such as ('/', e.g. './1') we can later add it and allow ascendents
lookup via negative numbers.

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 @@
 
 ``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
@@ -378,12 +378,33 @@
     # 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
@@ -2881,6 +2881,14 @@
   $ 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'
@@ -2901,6 +2909,10 @@
   hg: parse error: ^ expects a number 0, 1, or 2
   [255]
 
+  $ log 'branchpoint()~-1'
+  abort: revset has more than one child!
+  [255]
+
 Bogus function gets suggestions
   $ log 'add()'
   hg: parse error: unknown identifier: add


More information about the Mercurial-devel mailing list