sapling/remotefilelog/contentstore.py
Durham Goode c902797dc9 store: implement markledger and cleanup on stores
Summary:
This implements the new markledger and cleanup apis on the existing
remotefilelog stores. These apis are used to tell the repacker what each store
has, and allows each store to cleanup if its data has been repacked.

Test Plan:
Ran repack in conjunction with the future diffs that make use of
these apis.

Reviewers: rmcelroy, ttung, lcharignon, quark, mitrandir

Reviewed By: mitrandir

Differential Revision: https://phabricator.intern.facebook.com/D3249584

Signature: t1:3249584:1462226133:1e8faffc9f6bf8f7c94e6e79aee8865e3c41648c
2016-05-03 12:33:00 -07:00

154 lines
5.2 KiB
Python

import os, shutil
import basestore, shallowutil
from mercurial import error, mdiff
from mercurial.i18n import _
from mercurial.node import hex, nullid
class ChainIndicies(object):
"""A static class for easy reference to the delta chain indicies.
"""
# The filename of this revision delta
NAME = 0
# The mercurial file node for this revision delta
NODE = 1
# The filename of the delta base's revision. This is useful when delta
# between different files (like in the case of a move or copy, we can delta
# against the original file content).
BASENAME = 2
# The mercurial file node for the delta base revision. This is the nullid if
# this delta is a full text.
BASENODE = 3
# The actual delta or full text data.
DATA = 4
class unioncontentstore(object):
def __init__(self, *args, **kwargs):
self.stores = args
self.writestore = kwargs.get('writestore')
def get(self, name, node):
"""Fetches the full text revision contents of the given name+node pair.
If the full text doesn't exist, throws a KeyError.
Under the hood, this uses getdeltachain() across all the stores to build
up a full chain to produce the full text.
"""
chain = self.getdeltachain(name, node)
# The last entry in the chain is a full text, so we start our delta
# applies with that.
fulltext = chain.pop()[ChainIndicies.DATA]
text = fulltext
while chain:
delta = chain.pop()[ChainIndicies.DATA]
text = mdiff.patches(text, [delta])
return text
def getdeltachain(self, name, node):
"""Returns the deltachain for the given name/node pair.
Returns an ordered list of:
[(name, node, deltabasename, deltabasenode, deltacontent),...]
where the chain is terminated by a full text entry with a nullid
deltabasenode.
"""
chain = self._getpartialchain(name, node)
while chain[-1][ChainIndicies.BASENODE] != nullid:
x, x, deltabasename, deltabasenode, x = chain[-1]
morechain = self._getpartialchain(deltabasename, deltabasenode)
chain.extend(morechain)
return chain
def _getpartialchain(self, name, node):
"""Returns a partial delta chain for the given name/node pair.
A partial chain is a chain that may not be terminated in a full-text.
"""
for store in self.stores:
try:
return store.getdeltachain(name, node)
except KeyError:
pass
raise error.LookupError(name, hex(node), _('no node'))
def add(self, name, node, data):
raise RuntimeError("cannot add content only to remotefilelog "
"contentstore")
def getmissing(self, keys):
missing = keys
for store in self.stores:
if missing:
missing = store.getmissing(missing)
return missing
def addremotefilelognode(self, name, node, data):
if self.writestore:
self.writestore.addremotefilelognode(name, node, data)
else:
raise RuntimeError("no writable store configured")
def markledger(self, ledger):
for store in self.stores:
store.markledger(ledger)
class remotefilelogcontentstore(basestore.basestore):
def get(self, name, node):
data = self._getdata(name, node)
index, size = shallowutil.parsesize(data)
content = data[(index + 1):(index + 1 + size)]
ancestormap = shallowutil.ancestormap(data)
p1, p2, linknode, copyfrom = ancestormap[node]
copyrev = None
if copyfrom:
copyrev = hex(p1)
revision = shallowutil.createrevlogtext(content, copyfrom, copyrev)
return revision
def getdeltachain(self, name, node):
# Since remotefilelog content stores just contain full texts, we return
# a fake delta chain that just consists of a single full text revision.
# The nullid in the deltabasenode slot indicates that the revision is a
# fulltext.
revision = self.get(name, node)
return [(name, node, None, nullid, revision)]
def add(self, name, node, data):
raise RuntimeError("cannot add content only to remotefilelog "
"contentstore")
class remotecontentstore(object):
def __init__(self, ui, fileservice, shared):
self._fileservice = fileservice
self._shared = shared
def get(self, name, node):
self._fileservice.prefetch([(name, hex(node))])
return self._shared.get(name, node)
def getdeltachain(self, name, node):
# Since our remote content stores just contain full texts, we return a
# fake delta chain that just consists of a single full text revision.
# The nullid in the deltabasenode slot indicates that the revision is a
# fulltext.
revision = self.get(name, node)
return [(name, node, None, nullid, revision)]
def add(self, name, node, data):
raise RuntimeError("cannot add to a remote store")
def getmissing(self, keys):
return keys
def markledger(self, ledger):
pass