lookup: add option to disambiguate prefix within revset
authorMartin von Zweigbergk <martinvonz@google.com>
Wed, 28 Mar 2018 09:36:02 -0700
changeset 38842 503f936489dd
parent 38841 df0873ab5c14
child 38843 6f7c9527030b
lookup: add option to disambiguate prefix within revset When resolving a nodeid prefix that is not unique within the repo and the user has configured a revset that they want to disambiguate within, we now try to look up within that revset before we fail. If there is a unique match within the revset, we use that. This is of course most effective at allowing a short prefix if the revset contains few nodes. For most of our internal users at Google, "not public()" is sufficiently small that a hex digit or two is enough. The implementation is currently pretty slow, but good enough for small revsets (which is the expected use case). The scan in the revset is linear. We may want to use a prefix tree if we want to allow users to use a larger revset. Credit for the idea goes to Kyle Lippincott. Differential Revision: https://phab.mercurial-scm.org/D4037
mercurial/configitems.py
mercurial/scmutil.py
tests/test-revisions.t
--- a/mercurial/configitems.py	Fri Apr 13 23:37:53 2018 -0700
+++ b/mercurial/configitems.py	Wed Mar 28 09:36:02 2018 -0700
@@ -590,6 +590,9 @@
 coreconfigitem('experimental', 'revlogv2',
     default=None,
 )
+coreconfigitem('experimental', 'revisions.disambiguatewithin',
+    default=None,
+)
 coreconfigitem('experimental', 'single-head-per-branch',
     default=False,
 )
--- a/mercurial/scmutil.py	Fri Apr 13 23:37:53 2018 -0700
+++ b/mercurial/scmutil.py	Wed Mar 28 09:36:02 2018 -0700
@@ -437,9 +437,26 @@
     return '%d:%s' % (rev, hexfunc(node))
 
 def resolvehexnodeidprefix(repo, prefix):
-    # Uses unfiltered repo because it's faster when prefix is ambiguous/
-    # This matches the shortesthexnodeidprefix() function below.
-    node = repo.unfiltered().changelog._partialmatch(prefix)
+    try:
+        # Uses unfiltered repo because it's faster when prefix is ambiguous/
+        # This matches the shortesthexnodeidprefix() function below.
+        node = repo.unfiltered().changelog._partialmatch(prefix)
+    except error.AmbiguousPrefixLookupError:
+        revset = repo.ui.config('experimental', 'revisions.disambiguatewithin')
+        if revset:
+            # Clear config to avoid infinite recursion
+            configoverrides = {('experimental',
+                                'revisions.disambiguatewithin'): None}
+            with repo.ui.configoverride(configoverrides):
+                revs = repo.anyrevs([revset], user=True)
+                matches = []
+                for rev in revs:
+                    node = repo.changelog.node(rev)
+                    if hex(node).startswith(prefix):
+                        matches.append(node)
+                if len(matches) == 1:
+                    return matches[0]
+        raise
     if node is None:
         return
     repo.changelog.rev(node)  # make sure node isn't filtered
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-revisions.t	Wed Mar 28 09:36:02 2018 -0700
@@ -0,0 +1,37 @@
+  $ hg init repo
+  $ cd repo
+
+  $ echo 0 > a
+  $ hg ci -qAm 0
+  $ for i in 5 8 14 43; do
+  >   hg up -q 0
+  >   echo $i > a
+  >   hg ci -qm $i
+  > done
+  $ cat <<EOF >> .hg/hgrc
+  > [alias]
+  > l = log -T '{rev}:{shortest(node,1)}\n'
+  > EOF
+
+  $ hg l
+  4:7ba5d
+  3:7ba57
+  2:72
+  1:9
+  0:b
+  $ cat <<EOF >> .hg/hgrc
+  > [experimental]
+  > revisions.disambiguatewithin=:3
+  > EOF
+9 was unambiguous and still is
+  $ hg l -r 9
+  1:9
+7 was ambiguous and still is
+  $ hg l -r 7
+  abort: 00changelog.i@7: ambiguous identifier!
+  [255]
+7b is no longer ambiguous
+  $ hg l -r 7b
+  3:7ba57
+
+  $ cd ..