diff --git a/hgext/sqlitestore.py b/hgext/sqlitestore.py --- a/hgext/sqlitestore.py +++ b/hgext/sqlitestore.py @@ -381,6 +381,12 @@ class sqlitefilestore(object): def __iter__(self): return iter(pycompat.xrange(len(self._revisions))) + def hasnode(self, node): + if node == nullid: + return False + + return node in self._nodetorev + def revs(self, start=0, stop=None): return storageutil.iterrevs(len(self._revisions), start=start, stop=stop) diff --git a/mercurial/filelog.py b/mercurial/filelog.py --- a/mercurial/filelog.py +++ b/mercurial/filelog.py @@ -7,6 +7,10 @@ from __future__ import absolute_import +from .node import ( + nullid, + nullrev, +) from . import ( error, repository, @@ -33,6 +37,16 @@ class filelog(object): def __iter__(self): return self._revlog.__iter__() + def hasnode(self, node): + if node in (nullid, nullrev): + return False + + try: + self._revlog.rev(node) + return True + except (TypeError, ValueError, IndexError, error.LookupError): + return False + def revs(self, start=0, stop=None): return self._revlog.revs(start=start, stop=stop) diff --git a/mercurial/repository.py b/mercurial/repository.py --- a/mercurial/repository.py +++ b/mercurial/repository.py @@ -484,6 +484,16 @@ class ifileindex(interfaceutil.Interface def __iter__(): """Iterate over revision numbers for this file.""" + def hasnode(node): + """Returns a bool indicating if a node is known to this store. + + Implementations must only return True for full, binary node values: + hex nodes, revision numbers, and partial node matches must be + rejected. + + The null node is never present. + """ + def revs(start=0, stop=None): """Iterate over revision numbers for this file, with control.""" diff --git a/mercurial/testing/storage.py b/mercurial/testing/storage.py --- a/mercurial/testing/storage.py +++ b/mercurial/testing/storage.py @@ -45,6 +45,13 @@ class ifileindextests(basetestcase): with self.assertRaises(StopIteration): next(gen) + self.assertFalse(f.hasnode(None)) + self.assertFalse(f.hasnode(0)) + self.assertFalse(f.hasnode(nullrev)) + self.assertFalse(f.hasnode(nullid)) + self.assertFalse(f.hasnode(b'0')) + self.assertFalse(f.hasnode(b'a' * 20)) + # revs() should evaluate to an empty list. self.assertEqual(list(f.revs()), []) @@ -161,6 +168,13 @@ class ifileindextests(basetestcase): with self.assertRaises(StopIteration): next(gen) + self.assertTrue(f.hasnode(node)) + self.assertFalse(f.hasnode(hex(node))) + self.assertFalse(f.hasnode(nullrev)) + self.assertFalse(f.hasnode(nullid)) + self.assertFalse(f.hasnode(node[0:12])) + self.assertFalse(f.hasnode(hex(node)[0:20])) + self.assertEqual(list(f.revs()), [0]) self.assertEqual(list(f.revs(start=1)), []) self.assertEqual(list(f.revs(start=0)), [0])