diff --git a/mercurial/manifest.py b/mercurial/manifest.py --- a/mercurial/manifest.py +++ b/mercurial/manifest.py @@ -1246,7 +1246,8 @@ self.write() self._read = False -class manifestrevlog(revlog.revlog): +@interfaceutil.implementer(repository.imanifeststorage) +class manifestrevlog(object): '''A revlog that stores manifest texts. This is responsible for caching the full-text manifest contents. ''' @@ -1291,10 +1292,14 @@ else: self._dirlogcache = {'': self} - super(manifestrevlog, self).__init__(opener, indexfile, - # only root indexfile is cached - checkambig=not bool(tree), - mmaplargeindex=True) + self._revlog = revlog.revlog(opener, indexfile, + # only root indexfile is cached + checkambig=not bool(tree), + mmaplargeindex=True) + + self.index = self._revlog.index + self.version = self._revlog.version + self._generaldelta = self._revlog._generaldelta def _setupmanifestcachehooks(self, repo): """Persist the manifestfulltextcache on lock release""" @@ -1323,7 +1328,7 @@ return self._fulltextcache def clearcaches(self, clear_persisted_data=False): - super(manifestrevlog, self).clearcaches() + self._revlog.clearcaches() self._fulltextcache.clear(clear_persisted_data=clear_persisted_data) self._dirlogcache = {self._tree: self} @@ -1350,9 +1355,10 @@ [(x, True) for x in removed]) arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work) - cachedelta = self.rev(p1), deltatext + cachedelta = self._revlog.rev(p1), deltatext text = util.buffer(arraytext) - n = self.addrevision(text, transaction, link, p1, p2, cachedelta) + n = self._revlog.addrevision(text, transaction, link, p1, p2, + cachedelta) else: # The first parent manifest isn't already loaded, so we'll # just encode a fulltext of the manifest and pass that @@ -1366,7 +1372,7 @@ arraytext = None else: text = m.text() - n = self.addrevision(text, transaction, link, p1, p2) + n = self._revlog.addrevision(text, transaction, link, p1, p2) arraytext = bytearray(text) if arraytext is not None: @@ -1395,12 +1401,90 @@ n = m2.node() if not n: - n = self.addrevision(text, transaction, link, m1.node(), m2.node()) + n = self._revlog.addrevision(text, transaction, link, m1.node(), + m2.node()) # Save nodeid so parent manifest can calculate its nodeid m.setnode(n) return n + def __len__(self): + return len(self._revlog) + + def __iter__(self): + return self._revlog.__iter__() + + def rev(self, node): + return self._revlog.rev(node) + + def node(self, rev): + return self._revlog.node(rev) + + def lookup(self, value): + return self._revlog.lookup(value) + + def parentrevs(self, rev): + return self._revlog.parentrevs(rev) + + def parents(self, node): + return self._revlog.parents(node) + + def linkrev(self, rev): + return self._revlog.linkrev(rev) + + def checksize(self): + return self._revlog.checksize() + + def revision(self, node, _df=None, raw=False): + return self._revlog.revision(node, _df=_df, raw=raw) + + def revdiff(self, rev1, rev2): + return self._revlog.revdiff(rev1, rev2) + + def cmp(self, node, text): + return self._revlog.cmp(node, text) + + def deltaparent(self, rev): + return self._revlog.deltaparent(rev) + + def emitrevisiondeltas(self, requests): + return self._revlog.emitrevisiondeltas(requests) + + def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None): + return self._revlog.addgroup(deltas, linkmapper, transaction, + addrevisioncb=addrevisioncb) + + def getstrippoint(self, minlink): + return self._revlog.getstrippoint(minlink) + + def strip(self, minlink, transaction): + return self._revlog.strip(minlink, transaction) + + def files(self): + return self._revlog.files() + + def clone(self, tr, destrevlog, **kwargs): + if not isinstance(destrevlog, manifestrevlog): + raise error.ProgrammingError('expected manifestrevlog to clone()') + + return self._revlog.clone(tr, destrevlog._revlog, **kwargs) + + @property + def indexfile(self): + return self._revlog.indexfile + + @indexfile.setter + def indexfile(self, value): + self._revlog.indexfile = value + + @property + def opener(self): + return self._revlog.opener + + @opener.setter + def opener(self, value): + self._revlog.opener = value + @interfaceutil.implementer(repository.imanifestlog) class manifestlog(object): """A collection class representing the collection of manifest snapshots diff --git a/mercurial/repository.py b/mercurial/repository.py --- a/mercurial/repository.py +++ b/mercurial/repository.py @@ -988,6 +988,166 @@ Returns the binary node of the created revision. """ +class imanifeststorage(interfaceutil.Interface): + """Storage interface for manifest data.""" + + index = interfaceutil.Attribute( + """An ``ifilerevisionssequence`` instance.""") + + indexfile = interfaceutil.Attribute( + """Path of revlog index file. + + TODO this is revlog specific and should not be exposed. + """) + + opener = interfaceutil.Attribute( + """VFS opener to use to access underlying files used for storage. + + TODO this is revlog specific and should not be exposed. + """) + + version = interfaceutil.Attribute( + """Revlog version number. + + TODO this is revlog specific and should not be exposed. + """) + + _generaldelta = interfaceutil.Attribute( + """Whether generaldelta storage is being used. + + TODO this is revlog specific and should not be exposed. + """) + + fulltextcache = interfaceutil.Attribute( + """Dict with cache of fulltexts. + + TODO this doesn't feel appropriate for the storage interface. + """) + + def __len__(): + """Obtain the number of revisions stored for this manifest.""" + + def __iter__(): + """Iterate over revision numbers for this manifest.""" + + def rev(node): + """Obtain the revision number given a binary node. + + Raises ``error.LookupError`` if the node is not known. + """ + + def node(rev): + """Obtain the node value given a revision number. + + Raises ``error.LookupError`` if the revision is not known. + """ + + def lookup(value): + """Attempt to resolve a value to a node. + + Value can be a binary node, hex node, revision number, or a bytes + 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): + """Returns a 2-tuple of parent nodes for a node. + + Values will be ``nullid`` if the parent is empty. + """ + + def parentrevs(rev): + """Like parents() but operates on revision numbers.""" + + def linkrev(rev): + """Obtain the changeset revision number a revision is linked to.""" + + def revision(node, _df=None, raw=False): + """Obtain fulltext data for a node.""" + + def revdiff(rev1, rev2): + """Obtain a delta between two revision numbers. + + The returned data is the result of ``bdiff.bdiff()`` on the raw + revision data. + """ + + def cmp(node, fulltext): + """Compare fulltext to another revision. + + Returns True if the fulltext is different from what is stored. + """ + + def emitrevisiondeltas(requests): + """Produce ``irevisiondelta`` from ``irevisiondeltarequest``s. + + See the documentation for ``ifiledata`` for more. + """ + + def addgroup(deltas, linkmapper, transaction, addrevisioncb=None): + """Process a series of deltas for storage. + + See the documentation in ``ifilemutation`` for more. + """ + + def getstrippoint(minlink): + """Find minimum revision that must be stripped to strip a linkrev. + + See the documentation in ``ifilemutation`` for more. + """ + + def strip(minlink, transaction): + """Remove storage of items starting at a linkrev. + + See the documentation in ``ifilemutation`` for more. + """ + + def checksize(): + """Obtain the expected sizes of backing files. + + TODO this is used by verify and it should not be part of the interface. + """ + + def files(): + """Obtain paths that are backing storage for this manifest. + + TODO this is used by verify and there should probably be a better API + for this functionality. + """ + + def deltaparent(rev): + """Obtain the revision that a revision is delta'd against. + + TODO delta encoding is an implementation detail of storage and should + not be exposed to the storage interface. + """ + + def clone(tr, dest, **kwargs): + """Clone this instance to another.""" + + def clearcaches(clear_persisted_data=False): + """Clear any caches associated with this instance.""" + + def dirlog(d): + """Obtain a manifest storage instance for a tree.""" + + def add(m, transaction, link, p1, p2, added, removed, readtree=None): + """Add a revision to storage. + + ``m`` is an object conforming to ``imanifestdict``. + + ``link`` is the linkrev revision number. + + ``p1`` and ``p2`` are the parent revision numbers. + + ``added`` and ``removed`` are iterables of added and removed paths, + respectively. + """ + class imanifestlog(interfaceutil.Interface): """Interface representing a collection of manifest snapshots. diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py --- a/tests/test-check-interfaces.py +++ b/tests/test-check-interfaces.py @@ -177,6 +177,7 @@ ziverify.verifyClass(repository.imanifestrevisionwritable, manifest.memtreemanifestctx) ziverify.verifyClass(repository.imanifestlog, manifest.manifestlog) + ziverify.verifyClass(repository.imanifeststorage, manifest.manifestrevlog) vfs = vfsmod.vfs(b'.') fl = filelog.filelog(vfs, b'dummy.i') @@ -198,6 +199,9 @@ # Conforms to imanifestdict. checkzobject(mctx.read()) + mrl = manifest.manifestrevlog(vfs) + checkzobject(mrl) + ziverify.verifyClass(repository.irevisiondelta, revlog.revlogrevisiondelta) ziverify.verifyClass(repository.irevisiondeltarequest,