2016-04-26 23:00:31 +03:00
|
|
|
# remotefilelog.py - filelog implementation where filelog history is stored
|
|
|
|
# remotely
|
2013-05-07 04:26:27 +04:00
|
|
|
#
|
|
|
|
# Copyright 2013 Facebook, Inc.
|
|
|
|
#
|
|
|
|
# This software may be used and distributed according to the terms of the
|
|
|
|
# GNU General Public License version 2 or any later version.
|
2017-04-27 05:52:20 +03:00
|
|
|
from __future__ import absolute_import
|
2013-05-07 04:26:27 +04:00
|
|
|
|
2017-04-27 05:52:20 +03:00
|
|
|
from . import (
|
|
|
|
constants,
|
|
|
|
fileserverclient,
|
|
|
|
shallowutil,
|
|
|
|
)
|
2016-10-21 21:02:09 +03:00
|
|
|
import collections, os
|
|
|
|
from mercurial.node import bin, nullid
|
2017-04-27 05:52:20 +03:00
|
|
|
from mercurial import filelog, revlog, mdiff, ancestor, error
|
2013-05-21 02:03:07 +04:00
|
|
|
from mercurial.i18n import _
|
2013-05-18 05:08:53 +04:00
|
|
|
|
2017-05-17 01:56:15 +03:00
|
|
|
class remotefilelognodemap(object):
|
|
|
|
def __init__(self, filename, store):
|
|
|
|
self._filename = filename
|
|
|
|
self._store = store
|
|
|
|
|
|
|
|
def __contains__(self, node):
|
|
|
|
missing = self._store.getmissing([(self._filename, node)])
|
|
|
|
return not bool(missing)
|
|
|
|
|
|
|
|
def __get__(self, node):
|
|
|
|
if node not in self:
|
|
|
|
raise KeyError(node)
|
|
|
|
return node
|
|
|
|
|
2013-05-18 05:08:53 +04:00
|
|
|
class remotefilelog(object):
|
2013-06-12 03:41:55 +04:00
|
|
|
def __init__(self, opener, path, repo):
|
2013-05-18 05:08:53 +04:00
|
|
|
self.opener = opener
|
|
|
|
self.filename = path
|
2013-06-12 03:41:55 +04:00
|
|
|
self.repo = repo
|
2017-05-17 01:56:15 +03:00
|
|
|
self.nodemap = remotefilelognodemap(self.filename, repo.contentstore)
|
2013-05-18 05:08:53 +04:00
|
|
|
|
2013-05-21 02:03:07 +04:00
|
|
|
self.version = 1
|
|
|
|
|
2013-05-18 05:08:53 +04:00
|
|
|
def read(self, node):
|
2016-04-05 02:48:55 +03:00
|
|
|
"""returns the file contents at this node"""
|
2016-04-05 02:26:12 +03:00
|
|
|
t = self.revision(node)
|
|
|
|
if not t.startswith('\1\n'):
|
|
|
|
return t
|
|
|
|
s = t.index('\1\n', 2)
|
|
|
|
return t[s + 2:]
|
2013-05-18 05:08:53 +04:00
|
|
|
|
|
|
|
def add(self, text, meta, transaction, linknode, p1=None, p2=None):
|
|
|
|
hashtext = text
|
|
|
|
|
|
|
|
# hash with the metadata, like in vanilla filelogs
|
2016-04-26 23:00:31 +03:00
|
|
|
hashtext = shallowutil.createrevlogtext(text, meta.get('copy'),
|
|
|
|
meta.get('copyrev'))
|
2013-05-18 05:08:53 +04:00
|
|
|
node = revlog.hash(hashtext, p1, p2)
|
2017-04-27 05:52:20 +03:00
|
|
|
return self.addrevision(hashtext, transaction, linknode, p1, p2,
|
|
|
|
node=node)
|
2013-05-18 05:08:53 +04:00
|
|
|
|
2017-05-17 01:57:12 +03:00
|
|
|
def _createfileblob(self, text, meta, flags, p1, p2, node, linknode):
|
|
|
|
# text passed to "_createfileblob" does not include filelog metadata
|
|
|
|
header = shallowutil.buildfileblobheader(len(text), flags)
|
|
|
|
data = "%s\0%s" % (header, text)
|
|
|
|
|
|
|
|
realp1 = p1
|
|
|
|
copyfrom = ""
|
|
|
|
if meta and 'copy' in meta:
|
|
|
|
copyfrom = meta['copy']
|
|
|
|
realp1 = bin(meta['copyrev'])
|
|
|
|
|
|
|
|
data += "%s%s%s%s%s\0" % (node, realp1, p2, linknode, copyfrom)
|
|
|
|
|
2017-06-13 20:21:30 +03:00
|
|
|
visited = set()
|
|
|
|
|
2017-05-17 01:57:12 +03:00
|
|
|
pancestors = {}
|
|
|
|
queue = []
|
|
|
|
if realp1 != nullid:
|
|
|
|
p1flog = self
|
|
|
|
if copyfrom:
|
|
|
|
p1flog = remotefilelog(self.opener, copyfrom, self.repo)
|
|
|
|
|
|
|
|
pancestors.update(p1flog.ancestormap(realp1))
|
|
|
|
queue.append(realp1)
|
2017-06-13 20:21:30 +03:00
|
|
|
visited.add(realp1)
|
2017-05-17 01:57:12 +03:00
|
|
|
if p2 != nullid:
|
|
|
|
pancestors.update(self.ancestormap(p2))
|
|
|
|
queue.append(p2)
|
2017-06-13 20:21:30 +03:00
|
|
|
visited.add(p2)
|
2017-05-17 01:57:12 +03:00
|
|
|
|
|
|
|
ancestortext = ""
|
|
|
|
|
|
|
|
# add the ancestors in topological order
|
|
|
|
while queue:
|
|
|
|
c = queue.pop(0)
|
|
|
|
pa1, pa2, ancestorlinknode, pacopyfrom = pancestors[c]
|
|
|
|
|
|
|
|
pacopyfrom = pacopyfrom or ''
|
|
|
|
ancestortext += "%s%s%s%s%s\0" % (
|
|
|
|
c, pa1, pa2, ancestorlinknode, pacopyfrom)
|
|
|
|
|
|
|
|
if pa1 != nullid and pa1 not in visited:
|
|
|
|
queue.append(pa1)
|
2017-06-13 20:21:30 +03:00
|
|
|
visited.add(pa1)
|
2017-05-17 01:57:12 +03:00
|
|
|
if pa2 != nullid and pa2 not in visited:
|
|
|
|
queue.append(pa2)
|
2017-06-13 20:21:30 +03:00
|
|
|
visited.add(pa2)
|
2017-05-17 01:57:12 +03:00
|
|
|
|
|
|
|
data += ancestortext
|
|
|
|
|
|
|
|
return data
|
|
|
|
|
2017-04-27 05:52:20 +03:00
|
|
|
def addrevision(self, text, transaction, linknode, p1, p2, cachedelta=None,
|
|
|
|
node=None, flags=revlog.REVIDX_DEFAULT_FLAGS):
|
|
|
|
# text passed to "addrevision" includes hg filelog metadata header
|
|
|
|
if node is None:
|
|
|
|
node = revlog.hash(text, p1, p2)
|
|
|
|
|
|
|
|
meta, metaoffset = filelog.parsemeta(text)
|
|
|
|
rawtext, validatehash = self._processflags(text, flags, 'write')
|
2017-05-17 02:01:13 +03:00
|
|
|
return self.addrawrevision(rawtext, transaction, linknode, p1, p2,
|
|
|
|
node, flags, cachedelta,
|
|
|
|
_metatuple=(meta, metaoffset))
|
|
|
|
|
|
|
|
def addrawrevision(self, rawtext, transaction, linknode, p1, p2, node,
|
|
|
|
flags, cachedelta=None, _metatuple=None):
|
|
|
|
if _metatuple:
|
|
|
|
# _metatuple: used by "addrevision" internally by remotefilelog
|
|
|
|
# meta was parsed confidently
|
|
|
|
meta, metaoffset = _metatuple
|
2018-02-07 01:18:44 +03:00
|
|
|
if flags == revlog.REVIDX_DEFAULT_FLAGS:
|
|
|
|
# For non-LFS text, remove hg filelog metadata
|
|
|
|
blobtext = rawtext[metaoffset:]
|
|
|
|
else:
|
|
|
|
# For LFS text, which does not contain the filelog metadata,
|
|
|
|
# do not mangle it.
|
|
|
|
blobtext = rawtext
|
2017-05-17 02:01:13 +03:00
|
|
|
else:
|
2018-02-07 01:18:44 +03:00
|
|
|
# Not from self.addrevision, but something else (repo._filecommit)
|
2017-05-17 02:01:13 +03:00
|
|
|
# calls addrawrevision directly. remotefilelog needs to get and
|
|
|
|
# strip filelog metadata.
|
2018-02-07 01:18:44 +03:00
|
|
|
meta, blobtext = shallowutil.parsemeta(rawtext, flags)
|
2017-05-17 01:57:12 +03:00
|
|
|
data = self._createfileblob(blobtext, meta, flags, p1, p2, node,
|
|
|
|
linknode)
|
2016-04-05 02:48:55 +03:00
|
|
|
self.repo.contentstore.addremotefilelognode(self.filename, node, data)
|
2013-05-18 05:08:53 +04:00
|
|
|
|
|
|
|
return node
|
|
|
|
|
|
|
|
def renamed(self, node):
|
2016-04-05 02:26:12 +03:00
|
|
|
ancestors = self.repo.metadatastore.getancestors(self.filename, node)
|
|
|
|
p1, p2, linknode, copyfrom = ancestors[node]
|
2013-05-15 06:48:05 +04:00
|
|
|
if copyfrom:
|
2013-05-18 05:08:53 +04:00
|
|
|
return (copyfrom, p1)
|
2013-05-07 04:47:26 +04:00
|
|
|
|
|
|
|
return False
|
|
|
|
|
2013-05-18 05:08:53 +04:00
|
|
|
def size(self, node):
|
|
|
|
"""return the size of a given revision"""
|
2016-04-10 19:46:24 +03:00
|
|
|
return len(self.read(node))
|
2013-05-18 05:08:53 +04:00
|
|
|
|
2014-11-29 16:20:28 +03:00
|
|
|
rawsize = size
|
|
|
|
|
2018-02-07 01:18:25 +03:00
|
|
|
def candelta(self, basenode, node):
|
|
|
|
# Do not use delta if either node is LFS. Avoids issues if clients have
|
|
|
|
# the delta base stored in different forms: one LFS, one non-LFS.
|
|
|
|
if self.flags(basenode) or self.flags(node):
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2013-05-18 05:08:53 +04:00
|
|
|
def cmp(self, node, text):
|
|
|
|
"""compare text with a given file revision
|
|
|
|
|
|
|
|
returns True if text is different than what is stored.
|
|
|
|
"""
|
|
|
|
|
|
|
|
if node == nullid:
|
|
|
|
return True
|
|
|
|
|
|
|
|
nodetext = self.read(node)
|
|
|
|
return nodetext != text
|
|
|
|
|
2013-06-12 03:41:55 +04:00
|
|
|
def __nonzero__(self):
|
|
|
|
return True
|
|
|
|
|
2013-05-18 05:08:53 +04:00
|
|
|
def __len__(self):
|
|
|
|
if self.filename == '.hgtags':
|
2013-11-06 05:19:59 +04:00
|
|
|
# The length of .hgtags is used to fast path tag checking.
|
|
|
|
# remotefilelog doesn't support .hgtags since the entire .hgtags
|
|
|
|
# history is needed. Use the excludepattern setting to make
|
2014-09-17 00:28:03 +04:00
|
|
|
# .hgtags a normal filelog.
|
2013-05-18 05:08:53 +04:00
|
|
|
return 0
|
|
|
|
|
2016-04-26 23:00:31 +03:00
|
|
|
raise RuntimeError("len not supported")
|
2013-05-18 05:08:53 +04:00
|
|
|
|
2013-05-21 02:03:07 +04:00
|
|
|
def empty(self):
|
|
|
|
return False
|
|
|
|
|
2017-04-27 05:55:02 +03:00
|
|
|
def flags(self, node):
|
|
|
|
if isinstance(node, int):
|
|
|
|
raise error.ProgrammingError(
|
|
|
|
'remotefilelog does not accept integer rev for flags')
|
2018-02-07 01:18:25 +03:00
|
|
|
if node == nullid:
|
|
|
|
return revlog.REVIDX_DEFAULT_FLAGS
|
2017-04-27 05:55:02 +03:00
|
|
|
store = self.repo.contentstore
|
|
|
|
return store.getmeta(self.filename, node).get(constants.METAKEYFLAG, 0)
|
2015-12-31 00:33:47 +03:00
|
|
|
|
2013-05-18 05:08:53 +04:00
|
|
|
def parents(self, node):
|
|
|
|
if node == nullid:
|
|
|
|
return nullid, nullid
|
|
|
|
|
2016-04-05 02:26:12 +03:00
|
|
|
ancestormap = self.repo.metadatastore.getancestors(self.filename, node)
|
2013-05-18 05:08:53 +04:00
|
|
|
p1, p2, linknode, copyfrom = ancestormap[node]
|
2013-05-21 02:03:07 +04:00
|
|
|
if copyfrom:
|
2013-05-18 05:08:53 +04:00
|
|
|
p1 = nullid
|
|
|
|
|
|
|
|
return p1, p2
|
|
|
|
|
|
|
|
def linknode(self, node):
|
2016-04-05 02:26:12 +03:00
|
|
|
ancestormap = self.repo.metadatastore.getancestors(self.filename, node)
|
|
|
|
p1, p2, linknode, copyfrom = ancestormap[node]
|
|
|
|
return linknode
|
2013-05-18 05:08:53 +04:00
|
|
|
|
|
|
|
def revdiff(self, node1, node2):
|
2018-02-07 01:18:35 +03:00
|
|
|
if node1 != nullid and (self.flags(node1) or self.flags(node2)):
|
|
|
|
raise error.ProgrammingError(
|
|
|
|
'cannot revdiff revisions with non-zero flags')
|
2017-05-10 00:47:58 +03:00
|
|
|
return mdiff.textdiff(self.revision(node1, raw=True),
|
|
|
|
self.revision(node2, raw=True))
|
2013-05-18 05:08:53 +04:00
|
|
|
|
|
|
|
def lookup(self, node):
|
|
|
|
if len(node) == 40:
|
|
|
|
node = bin(node)
|
|
|
|
if len(node) != 20:
|
2016-04-26 23:00:31 +03:00
|
|
|
raise error.LookupError(node, self.filename,
|
|
|
|
_('invalid lookup input'))
|
2013-05-18 05:08:53 +04:00
|
|
|
|
|
|
|
return node
|
|
|
|
|
2016-02-05 17:53:45 +03:00
|
|
|
def rev(self, node):
|
|
|
|
# This is a hack to make TortoiseHG work.
|
|
|
|
return node
|
|
|
|
|
2017-04-27 05:55:02 +03:00
|
|
|
def node(self, rev):
|
|
|
|
# This is a hack.
|
|
|
|
if isinstance(rev, int):
|
|
|
|
raise error.ProgrammingError(
|
|
|
|
'remotefilelog does not convert integer rev to node')
|
|
|
|
return rev
|
|
|
|
|
2017-04-27 05:52:20 +03:00
|
|
|
def revision(self, node, raw=False):
|
2013-05-18 05:08:53 +04:00
|
|
|
"""returns the revlog contents at this node.
|
|
|
|
this includes the meta data traditionally included in file revlogs.
|
|
|
|
this is generally only used for bundling and communicating with vanilla
|
|
|
|
hg clients.
|
|
|
|
"""
|
|
|
|
if node == nullid:
|
|
|
|
return ""
|
|
|
|
if len(node) != 20:
|
2016-04-26 23:00:31 +03:00
|
|
|
raise error.LookupError(node, self.filename,
|
|
|
|
_('invalid revision input'))
|
2013-05-18 05:08:53 +04:00
|
|
|
|
2017-04-27 05:52:20 +03:00
|
|
|
store = self.repo.contentstore
|
|
|
|
rawtext = store.get(self.filename, node)
|
|
|
|
if raw:
|
|
|
|
return rawtext
|
|
|
|
flags = store.getmeta(self.filename, node).get(constants.METAKEYFLAG, 0)
|
|
|
|
if flags == 0:
|
|
|
|
return rawtext
|
|
|
|
text, verifyhash = self._processflags(rawtext, flags, 'read')
|
|
|
|
return text
|
|
|
|
|
2018-02-07 01:18:14 +03:00
|
|
|
def _deltachain(self, node):
|
|
|
|
"""Obtain the delta chain for a revision.
|
|
|
|
|
|
|
|
Return (chain, False), chain is a list of nodes. This is to be
|
|
|
|
compatible with revlog API.
|
|
|
|
"""
|
|
|
|
store = self.repo.contentstore
|
|
|
|
chain = store.getdeltachain(self.filename, node)
|
|
|
|
return ([x[1] for x in chain], False)
|
|
|
|
|
2017-04-27 05:52:20 +03:00
|
|
|
def _processflags(self, text, flags, operation, raw=False):
|
|
|
|
# mostly copied from hg/mercurial/revlog.py
|
|
|
|
validatehash = True
|
|
|
|
orderedflags = revlog.REVIDX_FLAGS_ORDER
|
|
|
|
if operation == 'write':
|
|
|
|
orderedflags = reversed(orderedflags)
|
|
|
|
for flag in orderedflags:
|
|
|
|
if flag & flags:
|
|
|
|
vhash = True
|
|
|
|
if flag not in revlog._flagprocessors:
|
|
|
|
message = _("missing processor for flag '%#x'") % (flag)
|
|
|
|
raise revlog.RevlogError(message)
|
|
|
|
readfunc, writefunc, rawfunc = revlog._flagprocessors[flag]
|
|
|
|
if raw:
|
|
|
|
vhash = rawfunc(self, text)
|
|
|
|
elif operation == 'read':
|
|
|
|
text, vhash = readfunc(self, text)
|
|
|
|
elif operation == 'write':
|
|
|
|
text, vhash = writefunc(self, text)
|
|
|
|
validatehash = validatehash and vhash
|
|
|
|
return text, validatehash
|
2013-05-18 05:08:53 +04:00
|
|
|
|
|
|
|
def _read(self, id):
|
|
|
|
"""reads the raw file blob from disk, cache, or server"""
|
2014-02-12 04:25:55 +04:00
|
|
|
fileservice = self.repo.fileservice
|
|
|
|
localcache = fileservice.localcache
|
2016-04-26 23:00:31 +03:00
|
|
|
cachekey = fileserverclient.getcachekey(self.repo.name, self.filename,
|
|
|
|
id)
|
2013-09-09 21:23:29 +04:00
|
|
|
try:
|
2014-02-12 04:25:55 +04:00
|
|
|
return localcache.read(cachekey)
|
|
|
|
except KeyError:
|
2013-09-09 21:23:29 +04:00
|
|
|
pass
|
2013-05-18 05:08:53 +04:00
|
|
|
|
2013-09-07 00:28:15 +04:00
|
|
|
localkey = fileserverclient.getlocalkey(self.filename, id)
|
|
|
|
localpath = os.path.join(self.localpath, localkey)
|
2013-09-09 21:23:29 +04:00
|
|
|
try:
|
2016-04-05 02:26:12 +03:00
|
|
|
return shallowutil.readfile(localpath)
|
2013-09-09 21:23:29 +04:00
|
|
|
except IOError:
|
|
|
|
pass
|
2013-05-18 05:08:53 +04:00
|
|
|
|
2014-02-12 04:25:55 +04:00
|
|
|
fileservice.prefetch([(self.filename, id)])
|
2013-09-09 21:23:29 +04:00
|
|
|
try:
|
2014-02-12 04:25:55 +04:00
|
|
|
return localcache.read(cachekey)
|
|
|
|
except KeyError:
|
2013-09-09 21:23:29 +04:00
|
|
|
pass
|
2013-05-18 05:08:53 +04:00
|
|
|
|
2013-05-21 02:03:07 +04:00
|
|
|
raise error.LookupError(id, self.filename, _('no node'))
|
2013-05-18 05:08:53 +04:00
|
|
|
|
2015-12-02 10:49:48 +03:00
|
|
|
def ancestormap(self, node):
|
2016-04-05 02:26:12 +03:00
|
|
|
return self.repo.metadatastore.getancestors(self.filename, node)
|
2013-05-07 04:50:36 +04:00
|
|
|
|
2013-05-21 02:03:07 +04:00
|
|
|
def ancestor(self, a, b):
|
|
|
|
if a == nullid or b == nullid:
|
|
|
|
return nullid
|
|
|
|
|
2014-09-19 23:21:30 +04:00
|
|
|
revmap, parentfunc = self._buildrevgraph(a, b)
|
2016-04-26 23:00:31 +03:00
|
|
|
nodemap = dict(((v, k) for (k, v) in revmap.iteritems()))
|
2014-09-19 23:21:30 +04:00
|
|
|
|
|
|
|
ancs = ancestor.ancestors(parentfunc, revmap[a], revmap[b])
|
|
|
|
if ancs:
|
|
|
|
# choose a consistent winner when there's a tie
|
|
|
|
return min(map(nodemap.__getitem__, ancs))
|
|
|
|
return nullid
|
|
|
|
|
|
|
|
def commonancestorsheads(self, a, b):
|
|
|
|
"""calculate all the heads of the common ancestors of nodes a and b"""
|
|
|
|
|
|
|
|
if a == nullid or b == nullid:
|
|
|
|
return nullid
|
|
|
|
|
|
|
|
revmap, parentfunc = self._buildrevgraph(a, b)
|
2016-04-26 23:00:31 +03:00
|
|
|
nodemap = dict(((v, k) for (k, v) in revmap.iteritems()))
|
2013-05-21 02:03:07 +04:00
|
|
|
|
2014-09-19 23:21:30 +04:00
|
|
|
ancs = ancestor.commonancestorsheads(parentfunc, revmap[a], revmap[b])
|
|
|
|
return map(nodemap.__getitem__, ancs)
|
2013-06-13 04:56:18 +04:00
|
|
|
|
2014-09-19 23:21:30 +04:00
|
|
|
def _buildrevgraph(self, a, b):
|
|
|
|
"""Builds a numeric revision graph for the given two nodes.
|
|
|
|
Returns a node->rev map and a rev->[revs] parent function.
|
|
|
|
"""
|
|
|
|
amap = self.ancestormap(a)
|
|
|
|
bmap = self.ancestormap(b)
|
2013-05-21 02:03:07 +04:00
|
|
|
|
2014-09-19 23:21:30 +04:00
|
|
|
# Union the two maps
|
|
|
|
parentsmap = collections.defaultdict(list)
|
|
|
|
allparents = set()
|
|
|
|
for mapping in (amap, bmap):
|
|
|
|
for node, pdata in mapping.iteritems():
|
|
|
|
parents = parentsmap[node]
|
|
|
|
p1, p2, linknode, copyfrom = pdata
|
|
|
|
# Don't follow renames (copyfrom).
|
|
|
|
# remotefilectx.ancestor does that.
|
|
|
|
if p1 != nullid and not copyfrom:
|
|
|
|
parents.append(p1)
|
|
|
|
allparents.add(p1)
|
|
|
|
if p2 != nullid:
|
|
|
|
parents.append(p2)
|
2015-07-01 02:43:01 +03:00
|
|
|
allparents.add(p2)
|
2014-09-19 23:21:30 +04:00
|
|
|
|
|
|
|
|
|
|
|
# Breadth first traversal to build linkrev graph
|
|
|
|
parentrevs = collections.defaultdict(list)
|
|
|
|
revmap = {}
|
|
|
|
queue = collections.deque(((None, n) for n in parentsmap.iterkeys()
|
|
|
|
if n not in allparents))
|
|
|
|
while queue:
|
|
|
|
prevrev, current = queue.pop()
|
|
|
|
if current in revmap:
|
|
|
|
if prevrev:
|
|
|
|
parentrevs[prevrev].append(revmap[current])
|
|
|
|
continue
|
|
|
|
|
|
|
|
# Assign linkrevs in reverse order, so start at
|
|
|
|
# len(parentsmap) and work backwards.
|
|
|
|
currentrev = len(parentsmap) - len(revmap) - 1
|
|
|
|
revmap[current] = currentrev
|
|
|
|
|
|
|
|
if prevrev:
|
|
|
|
parentrevs[prevrev].append(currentrev)
|
|
|
|
|
|
|
|
for parent in parentsmap.get(current):
|
|
|
|
queue.appendleft((currentrev, parent))
|
|
|
|
|
|
|
|
return revmap, parentrevs.__getitem__
|
2013-05-21 02:03:07 +04:00
|
|
|
|
2013-05-18 05:08:53 +04:00
|
|
|
def strip(self, minlink, transaction):
|
|
|
|
pass
|
|
|
|
|
2013-05-21 02:03:07 +04:00
|
|
|
# misc unused things
|
|
|
|
def files(self):
|
|
|
|
return []
|
|
|
|
|
|
|
|
def checksize(self):
|
|
|
|
return 0, 0
|