scmutil: make shortesthexnodeidprefix() take a full binary nodeid
The shortest() template function depended on the behavior of
revlog._partialmatch() for these types of inputs:
* non-hex strings
* ambiguous strings
* too long strings
revlog._partialmatch() seems to return the input unchanged in these
cases, but we shouldn't depend on such a low-level function to match
the behavior we want in the user-facing template function. Instead,
let's handle these cases in the template function and always pass a
binary nodeid to _partialmatch().
Differential Revision: https://phab.mercurial-scm.org/D3371
--- a/hgext/show.py Fri Apr 13 10:36:03 2018 -0700
+++ b/hgext/show.py Sat Apr 14 00:13:08 2018 -0700
@@ -29,7 +29,6 @@
from mercurial.i18n import _
from mercurial.node import (
- hex,
nullrev,
)
from mercurial import (
@@ -448,8 +447,8 @@
if not revs:
return minlen
cl = repo.changelog
- return max(len(scmutil.shortesthexnodeidprefix(repo, hex(cl.node(r)),
- minlen)) for r in revs)
+ return max(len(scmutil.shortesthexnodeidprefix(repo, cl.node(r), minlen))
+ for r in revs)
# Adjust the docstring of the show command so it shows all registered views.
# This is a bit hacky because it runs at the end of module load. When moved
--- a/mercurial/scmutil.py Fri Apr 13 10:36:03 2018 -0700
+++ b/mercurial/scmutil.py Sat Apr 14 00:13:08 2018 -0700
@@ -443,12 +443,12 @@
repo.changelog.rev(node) # make sure node isn't filtered
return node
-def shortesthexnodeidprefix(repo, hexnode, minlength=1):
+def shortesthexnodeidprefix(repo, node, minlength=1):
"""Find the shortest unambiguous prefix that matches hexnode."""
# _partialmatch() of filtered changelog could take O(len(repo)) time,
# which would be unacceptably slow. so we look for hash collision in
# unfiltered space, which means some hashes may be slightly longer.
- return repo.unfiltered().changelog.shortest(hexnode, minlength)
+ return repo.unfiltered().changelog.shortest(hex(node), minlength)
def isrevsymbol(repo, symbol):
"""Checks if a symbol exists in the repo.
--- a/mercurial/templatefuncs.py Fri Apr 13 10:36:03 2018 -0700
+++ b/mercurial/templatefuncs.py Sat Apr 14 00:13:08 2018 -0700
@@ -10,6 +10,9 @@
import re
from .i18n import _
+from .node import (
+ bin,
+)
from . import (
color,
encoding,
@@ -579,7 +582,7 @@
# i18n: "shortest" is a keyword
raise error.ParseError(_("shortest() expects one or two arguments"))
- node = evalstring(context, mapping, args[0])
+ hexnode = evalstring(context, mapping, args[0])
minlength = 4
if len(args) > 1:
@@ -588,6 +591,20 @@
_("shortest() expects an integer minlength"))
repo = context.resource(mapping, 'ctx')._repo
+ if len(hexnode) > 40:
+ return hexnode
+ elif len(hexnode) == 40:
+ try:
+ node = bin(hexnode)
+ except TypeError:
+ return hexnode
+ else:
+ try:
+ node = scmutil.resolvehexnodeidprefix(repo, hexnode)
+ except (error.LookupError, error.WdirUnsupported):
+ return hexnode
+ if not node:
+ return hexnode
return scmutil.shortesthexnodeidprefix(repo, node, minlength)
@templatefunc('strip(text[, chars])')
--- a/tests/test-command-template.t Fri Apr 13 10:36:03 2018 -0700
+++ b/tests/test-command-template.t Sat Apr 14 00:13:08 2018 -0700
@@ -3900,6 +3900,21 @@
$ hg log -r 'wdir()' -T '{node|shortest}\n'
ffff
+ $ hg log --template '{shortest("f")}\n' -l1
+ f
+
+ $ hg log --template '{shortest("0123456789012345678901234567890123456789")}\n' -l1
+ 0123456789012345678901234567890123456789
+
+ $ hg log --template '{shortest("01234567890123456789012345678901234567890123456789")}\n' -l1
+ 01234567890123456789012345678901234567890123456789
+
+ $ hg log --template '{shortest("not a hex string")}\n' -l1
+ not a hex string
+
+ $ hg log --template '{shortest("not a hex string, but it'\''s 40 bytes long")}\n' -l1
+ not a hex string, but it's 40 bytes long
+
$ cd ..
Test shortest(node) with the repo having short hash collision: