diff --git a/mercurial/filelog.py b/mercurial/filelog.py --- a/mercurial/filelog.py +++ b/mercurial/filelog.py @@ -187,6 +187,14 @@ def verifyintegrity(self, state): return self._revlog.verifyintegrity(state) + def storageinfo(self, exclusivefiles=False, sharedfiles=False, + revisionscount=False, trackedsize=False, + storedsize=False): + return self._revlog.storageinfo( + exclusivefiles=exclusivefiles, sharedfiles=sharedfiles, + revisionscount=revisionscount, trackedsize=trackedsize, + storedsize=storedsize) + # TODO these aren't part of the interface and aren't internal methods. # Callers should be fixed to not use them. diff --git a/mercurial/manifest.py b/mercurial/manifest.py --- a/mercurial/manifest.py +++ b/mercurial/manifest.py @@ -1592,6 +1592,14 @@ return self._revlog.clone(tr, destrevlog._revlog, **kwargs) + def storageinfo(self, exclusivefiles=False, sharedfiles=False, + revisionscount=False, trackedsize=False, + storedsize=False): + return self._revlog.storageinfo( + exclusivefiles=exclusivefiles, sharedfiles=sharedfiles, + revisionscount=revisionscount, trackedsize=trackedsize, + storedsize=storedsize) + @property def indexfile(self): return self._revlog.indexfile diff --git a/mercurial/repository.py b/mercurial/repository.py --- a/mercurial/repository.py +++ b/mercurial/repository.py @@ -748,6 +748,41 @@ be a better API for that. """ + def storageinfo(exclusivefiles=False, sharedfiles=False, + revisionscount=False, trackedsize=False, + storedsize=False): + """Obtain information about storage for this file's data. + + Returns a dict describing storage for this tracked path. The keys + in the dict map to arguments of the same. The arguments are bools + indicating whether to calculate and obtain that data. + + exclusivefiles + Iterable of (vfs, path) describing files that are exclusively + used to back storage for this tracked path. + + sharedfiles + Iterable of (vfs, path) describing files that are used to back + storage for this tracked path. Those files may also provide storage + for other stored entities. + + revisionscount + Number of revisions available for retrieval. + + trackedsize + Total size in bytes of all tracked revisions. This is a sum of the + length of the fulltext of all revisions. + + storedsize + Total size in bytes used to store data for all tracked revisions. + This is commonly less than ``trackedsize`` due to internal usage + of deltas rather than fulltext revisions. + + Not all storage backends may support all queries are have a reasonable + value to use. In that case, the value should be set to ``None`` and + callers are expected to handle this special value. + """ + def verifyintegrity(state): """Verifies the integrity of file storage. @@ -1199,6 +1234,15 @@ manifest including files that did not match. """ + def storageinfo(exclusivefiles=False, sharedfiles=False, + revisionscount=False, trackedsize=False, + storedsize=False): + """Obtain information about storage for this manifest's data. + + See ``ifilestorage.storageinfo()`` for a description of this method. + This one behaves the same way, except for manifest data. + """ + class imanifestlog(interfaceutil.Interface): """Interface representing a collection of manifest snapshots. diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -2643,3 +2643,28 @@ yield revlogproblem( warning=_("warning: '%s' uses revlog format %d; expected %d") % (self.indexfile, version, state['expectedversion'])) + + def storageinfo(self, exclusivefiles=False, sharedfiles=False, + revisionscount=False, trackedsize=False, + storedsize=False): + d = {} + + if exclusivefiles: + d['exclusivefiles'] = [(self.opener, self.indexfile)] + if not self._inline: + d['exclusivefiles'].append((self.opener, self.datafile)) + + if sharedfiles: + d['sharedfiles'] = [] + + if revisionscount: + d['revisionscount'] = len(self) + + if trackedsize: + d['trackedsize'] = sum(map(self.rawsize, iter(self))) + + if storedsize: + d['storedsize'] = sum(self.opener.stat(path).st_size + for path in self.files()) + + return d diff --git a/mercurial/testing/storage.py b/mercurial/testing/storage.py --- a/mercurial/testing/storage.py +++ b/mercurial/testing/storage.py @@ -388,6 +388,10 @@ def testempty(self): f = self._makefilefn() + self.assertEqual(f.storageinfo(), {}) + self.assertEqual(f.storageinfo(revisionscount=True, trackedsize=True), + {'revisionscount': 0, 'trackedsize': 0}) + self.assertEqual(f.rawsize(nullrev), 0) for i in range(-5, 5): @@ -466,6 +470,10 @@ with self._maketransactionfn() as tr: node = f.add(fulltext, None, tr, 0, nullid, nullid) + self.assertEqual(f.storageinfo(), {}) + self.assertEqual(f.storageinfo(revisionscount=True, trackedsize=True), + {'revisionscount': 1, 'trackedsize': len(fulltext)}) + self.assertEqual(f.rawsize(0), len(fulltext)) with self.assertRaises(IndexError): @@ -553,6 +561,14 @@ node1 = f.add(fulltext1, None, tr, 1, node0, nullid) node2 = f.add(fulltext2, None, tr, 3, node1, nullid) + self.assertEqual(f.storageinfo(), {}) + self.assertEqual( + f.storageinfo(revisionscount=True, trackedsize=True), + { + 'revisionscount': 3, + 'trackedsize': len(fulltext0) + len(fulltext1) + len(fulltext2), + }) + self.assertEqual(f.rawsize(0), len(fulltext0)) self.assertEqual(f.rawsize(1), len(fulltext1)) self.assertEqual(f.rawsize(2), len(fulltext2))