storageutil: implement file identifier resolution method (BC)
revlog.lookup() has a number of advanced features, including partial
node matching.
These advanced features aren't needed for file id lookup because file
identifiers are almost always from internal sources. (An exception to
this is hgweb, which appears to have some code paths that attempt
to resolve a user-supplied string to a node.)
This commit implements a new function for resolving file identifiers
to nodes. It behaves like revlog.lookup() but without the
advanced features.
Tests reveal behavior changes:
* Partial hex nodes are no longer resolved to nodes.
* "-1" now returns nullid instead of raising LookupError.
* "0" on an empty store now raises LookupError instead of returning
nullid.
I'm not sure why "-1" wasn't recognized before. But it seems reasonable
to accept it as a value since integer -1 resolves to nullid.
These changes all seem reasonable to me. And with the exception of
partial hex node matching, we may want to consider changing
revlog.lookup() as well.
Differential Revision: https://phab.mercurial-scm.org/D4797
--- a/mercurial/filelog.py Fri Sep 28 11:00:20 2018 -0700
+++ b/mercurial/filelog.py Fri Sep 28 11:03:17 2018 -0700
@@ -49,7 +49,8 @@
return self._revlog.node(rev)
def lookup(self, node):
- return self._revlog.lookup(node)
+ return storageutil.fileidlookup(self._revlog, node,
+ self._revlog.indexfile)
def linkrev(self, rev):
return self._revlog.linkrev(rev)
--- a/mercurial/repository.py Fri Sep 28 11:00:20 2018 -0700
+++ b/mercurial/repository.py Fri Sep 28 11:03:17 2018 -0700
@@ -1099,9 +1099,6 @@
that can be converted to an integer.
Raises ``error.LookupError`` if a ndoe could not be resolved.
-
- TODO this is only used by debug* commands and can probably be deleted
- easily.
"""
def parents(node):
--- a/mercurial/testing/storage.py Fri Sep 28 11:00:20 2018 -0700
+++ b/mercurial/testing/storage.py Fri Sep 28 11:03:17 2018 -0700
@@ -85,21 +85,19 @@
self.assertEqual(f.lookup(nullid), nullid)
self.assertEqual(f.lookup(nullrev), nullid)
self.assertEqual(f.lookup(hex(nullid)), nullid)
-
- # String converted to integer doesn't work for nullrev.
- with self.assertRaises(error.LookupError):
- f.lookup(b'%d' % nullrev)
+ self.assertEqual(f.lookup(b'%d' % nullrev), nullid)
with self.assertRaises(error.LookupError):
f.lookup(b'badvalue')
- self.assertEqual(f.lookup(hex(nullid)[0:12]), nullid)
+ with self.assertRaises(error.LookupError):
+ f.lookup(hex(nullid)[0:12])
with self.assertRaises(error.LookupError):
f.lookup(b'-2')
- # TODO this is wonky.
- self.assertEqual(f.lookup(b'0'), nullid)
+ with self.assertRaises(error.LookupError):
+ f.lookup(b'0')
with self.assertRaises(error.LookupError):
f.lookup(b'1')
@@ -197,7 +195,9 @@
self.assertEqual(f.lookup(-1), nullid)
self.assertEqual(f.lookup(b'0'), node)
self.assertEqual(f.lookup(hex(node)), node)
- self.assertEqual(f.lookup(hex(node)[0:12]), node)
+
+ with self.assertRaises(error.LookupError):
+ f.lookup(hex(node)[0:12])
with self.assertRaises(IndexError):
f.lookup(-2)
--- a/mercurial/utils/storageutil.py Fri Sep 28 11:00:20 2018 -0700
+++ b/mercurial/utils/storageutil.py Fri Sep 28 11:03:17 2018 -0700
@@ -10,10 +10,13 @@
import hashlib
import re
+from ..i18n import _
from ..node import (
+ bin,
nullid,
)
from .. import (
+ error,
pycompat,
)
@@ -99,3 +102,53 @@
stop = storelen
return pycompat.xrange(start, stop, step)
+
+def fileidlookup(store, fileid, identifier):
+ """Resolve the file node for a value.
+
+ ``store`` is an object implementing the ``ifileindex`` interface.
+
+ ``fileid`` can be:
+
+ * A 20 byte binary node.
+ * An integer revision number
+ * A 40 byte hex node.
+ * A bytes that can be parsed as an integer representing a revision number.
+
+ ``identifier`` is used to populate ``error.LookupError`` with an identifier
+ for the store.
+
+ Raises ``error.LookupError`` on failure.
+ """
+ if isinstance(fileid, int):
+ return store.node(fileid)
+
+ if len(fileid) == 20:
+ try:
+ store.rev(fileid)
+ return fileid
+ except error.LookupError:
+ pass
+
+ if len(fileid) == 40:
+ try:
+ rawnode = bin(fileid)
+ store.rev(rawnode)
+ return rawnode
+ except TypeError:
+ pass
+
+ try:
+ rev = int(fileid)
+
+ if b'%d' % rev != fileid:
+ raise ValueError
+
+ try:
+ return store.node(rev)
+ except (IndexError, TypeError):
+ pass
+ except (ValueError, OverflowError):
+ pass
+
+ raise error.LookupError(fileid, identifier, _('no match found'))