simplestorerepo.py
744 lines
| 19.7 KiB
| text/x-python
|
PythonLexer
/ tests / simplestorerepo.py
Gregory Szorc
|
r37355 | # simplestorerepo.py - Extension that swaps in alternate repository storage. | ||
# | ||||
# Copyright 2018 Gregory Szorc <gregory.szorc@gmail.com> | ||||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
Gregory Szorc
|
r37356 | # To use this with the test suite: | ||
# | ||||
# $ HGREPOFEATURES="simplestore" ./run-tests.py \ | ||||
# --extra-config-opt extensions.simplestore=`pwd`/simplestorerepo.py | ||||
Gregory Szorc
|
r37355 | |||
Gregory Szorc
|
r37433 | import stat | ||
Gregory Szorc
|
r37355 | from mercurial.i18n import _ | ||
from mercurial.node import ( | ||||
bin, | ||||
hex, | ||||
nullrev, | ||||
) | ||||
Augie Fackler
|
r43346 | from mercurial.thirdparty import attr | ||
Gregory Szorc
|
r37355 | from mercurial import ( | ||
ancestor, | ||||
Gregory Szorc
|
r37364 | bundlerepo, | ||
Gregory Szorc
|
r37355 | error, | ||
Gregory Szorc
|
r37433 | extensions, | ||
localrepo, | ||||
Gregory Szorc
|
r37355 | mdiff, | ||
pycompat, | ||||
revlog, | ||||
Gregory Szorc
|
r37433 | store, | ||
Gregory Szorc
|
r37435 | verify, | ||
Gregory Szorc
|
r37355 | ) | ||
Pulkit Goyal
|
r43078 | from mercurial.interfaces import ( | ||
repository, | ||||
Pulkit Goyal
|
r43079 | util as interfaceutil, | ||
Pulkit Goyal
|
r43078 | ) | ||
Gregory Szorc
|
r38565 | from mercurial.utils import ( | ||
Augie Fackler
|
r41193 | cborutil, | ||
Gregory Szorc
|
r39913 | storageutil, | ||
Gregory Szorc
|
r38565 | ) | ||
Augie Fackler
|
r43346 | from mercurial.revlogutils import flagutil | ||
Gregory Szorc
|
r37355 | |||
# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for | ||||
# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should | ||||
# be specifying the version(s) of Mercurial they are tested with, or | ||||
# leave the attribute unspecified. | ||||
Matt Harbison
|
r44132 | testedwith = b'ships-with-hg-core' | ||
Gregory Szorc
|
r37355 | |||
Matt Harbison
|
r44132 | REQUIREMENT = b'testonly-simplestore' | ||
Gregory Szorc
|
r37433 | |||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r37355 | def validatenode(node): | ||
if isinstance(node, int): | ||||
raise ValueError('expected node; got int') | ||||
if len(node) != 20: | ||||
raise ValueError('expected 20 byte node') | ||||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r37355 | def validaterev(rev): | ||
if not isinstance(rev, int): | ||||
raise ValueError('expected int') | ||||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r39813 | class simplestoreerror(error.StorageError): | ||
pass | ||||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r39267 | @interfaceutil.implementer(repository.irevisiondelta) | ||
Augie Fackler
|
r41192 | @attr.s(slots=True) | ||
Gregory Szorc
|
r49801 | class simplestorerevisiondelta: | ||
Gregory Szorc
|
r39267 | node = attr.ib() | ||
p1node = attr.ib() | ||||
p2node = attr.ib() | ||||
basenode = attr.ib() | ||||
flags = attr.ib() | ||||
baserevisionsize = attr.ib() | ||||
revision = attr.ib() | ||||
delta = attr.ib() | ||||
Augie Fackler
|
r41192 | linknode = attr.ib(default=None) | ||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r41192 | @interfaceutil.implementer(repository.iverifyproblem) | ||
@attr.s(frozen=True) | ||||
Gregory Szorc
|
r49801 | class simplefilestoreproblem: | ||
Augie Fackler
|
r41192 | warning = attr.ib(default=None) | ||
error = attr.ib(default=None) | ||||
node = attr.ib(default=None) | ||||
Gregory Szorc
|
r39267 | |||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r38565 | @interfaceutil.implementer(repository.ifilestorage) | ||
Gregory Szorc
|
r49801 | class filestorage: | ||
Gregory Szorc
|
r37355 | """Implements storage for a tracked path. | ||
Data is stored in the VFS in a directory corresponding to the tracked | ||||
path. | ||||
Index data is stored in an ``index`` file using CBOR. | ||||
Fulltext data is stored in files having names of the node. | ||||
""" | ||||
r43143 | _flagserrorclass = simplestoreerror | |||
Joerg Sonnenberger
|
r47538 | def __init__(self, repo, svfs, path): | ||
self.nullid = repo.nullid | ||||
self._repo = repo | ||||
Gregory Szorc
|
r37355 | self._svfs = svfs | ||
self._path = path | ||||
self._storepath = b'/'.join([b'data', path]) | ||||
self._indexpath = b'/'.join([self._storepath, b'index']) | ||||
indexdata = self._svfs.tryread(self._indexpath) | ||||
if indexdata: | ||||
Augie Fackler
|
r41193 | indexdata = cborutil.decodeall(indexdata) | ||
Gregory Szorc
|
r37355 | |||
self._indexdata = indexdata or [] | ||||
self._indexbynode = {} | ||||
self._indexbyrev = {} | ||||
Gregory Szorc
|
r39896 | self._index = [] | ||
Gregory Szorc
|
r37355 | self._refreshindex() | ||
r43143 | self._flagprocessors = dict(flagutil.flagprocessors) | |||
Gregory Szorc
|
r37355 | def _refreshindex(self): | ||
self._indexbynode.clear() | ||||
self._indexbyrev.clear() | ||||
Gregory Szorc
|
r39896 | self._index = [] | ||
Gregory Szorc
|
r37355 | |||
for i, entry in enumerate(self._indexdata): | ||||
self._indexbynode[entry[b'node']] = entry | ||||
self._indexbyrev[i] = entry | ||||
Joerg Sonnenberger
|
r47771 | self._indexbynode[self._repo.nullid] = { | ||
b'node': self._repo.nullid, | ||||
b'p1': self._repo.nullid, | ||||
b'p2': self._repo.nullid, | ||||
Gregory Szorc
|
r37355 | b'linkrev': nullrev, | ||
b'flags': 0, | ||||
} | ||||
self._indexbyrev[nullrev] = { | ||||
Joerg Sonnenberger
|
r47771 | b'node': self._repo.nullid, | ||
b'p1': self._repo.nullid, | ||||
b'p2': self._repo.nullid, | ||||
Gregory Szorc
|
r37355 | b'linkrev': nullrev, | ||
b'flags': 0, | ||||
} | ||||
for i, entry in enumerate(self._indexdata): | ||||
p1rev, p2rev = self.parentrevs(self.rev(entry[b'node'])) | ||||
# start, length, rawsize, chainbase, linkrev, p1, p2, node | ||||
Augie Fackler
|
r43346 | self._index.append( | ||
(0, 0, 0, -1, entry[b'linkrev'], p1rev, p2rev, entry[b'node']) | ||||
) | ||||
Gregory Szorc
|
r37355 | |||
Joerg Sonnenberger
|
r47771 | self._index.append((0, 0, 0, -1, -1, -1, -1, self._repo.nullid)) | ||
Gregory Szorc
|
r37355 | |||
def __len__(self): | ||||
return len(self._indexdata) | ||||
def __iter__(self): | ||||
return iter(range(len(self))) | ||||
def revs(self, start=0, stop=None): | ||||
step = 1 | ||||
if stop is not None: | ||||
if start > stop: | ||||
step = -1 | ||||
stop += step | ||||
else: | ||||
stop = len(self) | ||||
return range(start, stop, step) | ||||
def parents(self, node): | ||||
validatenode(node) | ||||
if node not in self._indexbynode: | ||||
raise KeyError('unknown node') | ||||
entry = self._indexbynode[node] | ||||
return entry[b'p1'], entry[b'p2'] | ||||
def parentrevs(self, rev): | ||||
p1, p2 = self.parents(self._indexbyrev[rev][b'node']) | ||||
return self.rev(p1), self.rev(p2) | ||||
def rev(self, node): | ||||
validatenode(node) | ||||
Gregory Szorc
|
r37426 | try: | ||
self._indexbynode[node] | ||||
except KeyError: | ||||
raise error.LookupError(node, self._indexpath, _('no node')) | ||||
Gregory Szorc
|
r37355 | |||
for rev, entry in self._indexbyrev.items(): | ||||
if entry[b'node'] == node: | ||||
return rev | ||||
Matt Harbison
|
r44132 | raise error.ProgrammingError(b'this should not occur') | ||
Gregory Szorc
|
r37355 | |||
def node(self, rev): | ||||
validaterev(rev) | ||||
return self._indexbyrev[rev][b'node'] | ||||
Augie Fackler
|
r41192 | def hasnode(self, node): | ||
validatenode(node) | ||||
return node in self._indexbynode | ||||
def censorrevision(self, tr, censornode, tombstone=b''): | ||||
raise NotImplementedError('TODO') | ||||
Gregory Szorc
|
r37355 | def lookup(self, node): | ||
if isinstance(node, int): | ||||
return self.node(node) | ||||
if len(node) == 20: | ||||
Gregory Szorc
|
r37426 | self.rev(node) | ||
return node | ||||
Gregory Szorc
|
r37355 | |||
try: | ||||
rev = int(node) | ||||
if '%d' % rev != node: | ||||
raise ValueError | ||||
if rev < 0: | ||||
rev = len(self) + rev | ||||
if rev < 0 or rev >= len(self): | ||||
raise ValueError | ||||
return self.node(rev) | ||||
except (ValueError, OverflowError): | ||||
pass | ||||
if len(node) == 40: | ||||
try: | ||||
rawnode = bin(node) | ||||
self.rev(rawnode) | ||||
return rawnode | ||||
Gregory Szorc
|
r37426 | except TypeError: | ||
Gregory Szorc
|
r37355 | pass | ||
Gregory Szorc
|
r37426 | raise error.LookupError(node, self._path, _('invalid lookup input')) | ||
Gregory Szorc
|
r37355 | |||
def linkrev(self, rev): | ||||
validaterev(rev) | ||||
return self._indexbyrev[rev][b'linkrev'] | ||||
Gregory Szorc
|
r39909 | def _flags(self, rev): | ||
Gregory Szorc
|
r37355 | validaterev(rev) | ||
return self._indexbyrev[rev][b'flags'] | ||||
Gregory Szorc
|
r39270 | def _candelta(self, baserev, rev): | ||
Gregory Szorc
|
r37355 | validaterev(baserev) | ||
validaterev(rev) | ||||
Augie Fackler
|
r43346 | if (self._flags(baserev) & revlog.REVIDX_RAWTEXT_CHANGING_FLAGS) or ( | ||
self._flags(rev) & revlog.REVIDX_RAWTEXT_CHANGING_FLAGS | ||||
): | ||||
Gregory Szorc
|
r37355 | return False | ||
return True | ||||
def checkhash(self, text, node, p1=None, p2=None, rev=None): | ||||
if p1 is None and p2 is None: | ||||
p1, p2 = self.parents(node) | ||||
Gregory Szorc
|
r39913 | if node != storageutil.hashrevisionsha1(text, p1, p2): | ||
Augie Fackler
|
r43346 | raise simplestoreerror( | ||
_("integrity check failed on %s") % self._path | ||||
) | ||||
Gregory Szorc
|
r37355 | |||
Augie Fackler
|
r41192 | def revision(self, nodeorrev, raw=False): | ||
if isinstance(nodeorrev, int): | ||||
node = self.node(nodeorrev) | ||||
else: | ||||
node = nodeorrev | ||||
Gregory Szorc
|
r37355 | validatenode(node) | ||
Joerg Sonnenberger
|
r47771 | if node == self._repo.nullid: | ||
Gregory Szorc
|
r37355 | return b'' | ||
rev = self.rev(node) | ||||
Gregory Szorc
|
r39909 | flags = self._flags(rev) | ||
Gregory Szorc
|
r37355 | |||
path = b'/'.join([self._storepath, hex(node)]) | ||||
rawtext = self._svfs.read(path) | ||||
r43149 | if raw: | |||
r43262 | validatehash = flagutil.processflagsraw(self, rawtext, flags) | |||
r43149 | text = rawtext | |||
else: | ||||
r43261 | r = flagutil.processflagsread(self, rawtext, flags) | |||
Raphaël Gomès
|
r47443 | text, validatehash = r | ||
Gregory Szorc
|
r37355 | if validatehash: | ||
self.checkhash(text, node, rev=rev) | ||||
return text | ||||
r42950 | def rawdata(self, nodeorrev): | |||
return self.revision(raw=True) | ||||
Gregory Szorc
|
r37355 | def read(self, node): | ||
validatenode(node) | ||||
revision = self.revision(node) | ||||
if not revision.startswith(b'\1\n'): | ||||
return revision | ||||
start = revision.index(b'\1\n', 2) | ||||
Augie Fackler
|
r43346 | return revision[start + 2 :] | ||
Gregory Szorc
|
r37355 | |||
def renamed(self, node): | ||||
validatenode(node) | ||||
Joerg Sonnenberger
|
r47771 | if self.parents(node)[0] != self._repo.nullid: | ||
Gregory Szorc
|
r37355 | return False | ||
fulltext = self.revision(node) | ||||
Gregory Szorc
|
r39914 | m = storageutil.parsemeta(fulltext)[0] | ||
Gregory Szorc
|
r37355 | |||
if m and 'copy' in m: | ||||
return m['copy'], bin(m['copyrev']) | ||||
return False | ||||
def cmp(self, node, text): | ||||
validatenode(node) | ||||
t = text | ||||
if text.startswith(b'\1\n'): | ||||
t = b'\1\n\1\n' + text | ||||
p1, p2 = self.parents(node) | ||||
Gregory Szorc
|
r39913 | if storageutil.hashrevisionsha1(t, p1, p2) == node: | ||
Gregory Szorc
|
r37355 | return False | ||
if self.iscensored(self.rev(node)): | ||||
return text != b'' | ||||
if self.renamed(node): | ||||
t2 = self.read(node) | ||||
return t2 != text | ||||
return True | ||||
def size(self, rev): | ||||
validaterev(rev) | ||||
node = self._indexbyrev[rev][b'node'] | ||||
if self.renamed(node): | ||||
return len(self.read(node)) | ||||
if self.iscensored(rev): | ||||
return 0 | ||||
return len(self.revision(node)) | ||||
def iscensored(self, rev): | ||||
validaterev(rev) | ||||
Gregory Szorc
|
r40083 | return self._flags(rev) & repository.REVISION_FLAG_CENSORED | ||
Gregory Szorc
|
r37355 | |||
def commonancestorsheads(self, a, b): | ||||
validatenode(a) | ||||
validatenode(b) | ||||
a = self.rev(a) | ||||
b = self.rev(b) | ||||
ancestors = ancestor.commonancestorsheads(self.parentrevs, a, b) | ||||
return pycompat.maplist(self.node, ancestors) | ||||
def descendants(self, revs): | ||||
# This is a copy of revlog.descendants() | ||||
first = min(revs) | ||||
if first == nullrev: | ||||
for i in self: | ||||
yield i | ||||
return | ||||
seen = set(revs) | ||||
for i in self.revs(start=first + 1): | ||||
for x in self.parentrevs(i): | ||||
if x != nullrev and x in seen: | ||||
seen.add(i) | ||||
yield i | ||||
break | ||||
# Required by verify. | ||||
def files(self): | ||||
entries = self._svfs.listdir(self._storepath) | ||||
# Strip out undo.backup.* files created as part of transaction | ||||
# recording. | ||||
entries = [f for f in entries if not f.startswith('undo.backup.')] | ||||
return [b'/'.join((self._storepath, f)) for f in entries] | ||||
Augie Fackler
|
r43346 | def storageinfo( | ||
self, | ||||
exclusivefiles=False, | ||||
sharedfiles=False, | ||||
revisionscount=False, | ||||
trackedsize=False, | ||||
storedsize=False, | ||||
): | ||||
Augie Fackler
|
r41192 | # TODO do a real implementation of this | ||
return { | ||||
'exclusivefiles': [], | ||||
'sharedfiles': [], | ||||
'revisionscount': len(self), | ||||
'trackedsize': 0, | ||||
'storedsize': None, | ||||
} | ||||
def verifyintegrity(self, state): | ||||
state['skipread'] = set() | ||||
for rev in self: | ||||
node = self.node(rev) | ||||
try: | ||||
self.revision(node) | ||||
except Exception as e: | ||||
yield simplefilestoreproblem( | ||||
Augie Fackler
|
r43346 | error='unpacking %s: %s' % (node, e), node=node | ||
) | ||||
Augie Fackler
|
r41192 | state['skipread'].add(node) | ||
Augie Fackler
|
r43346 | def emitrevisions( | ||
self, | ||||
nodes, | ||||
nodesorder=None, | ||||
revisiondata=False, | ||||
assumehaveparentrevisions=False, | ||||
deltamode=repository.CG_DELTAMODE_STD, | ||||
Raphaël Gomès
|
r47449 | sidedata_helpers=None, | ||
Augie Fackler
|
r43346 | ): | ||
Augie Fackler
|
r41192 | # TODO this will probably break on some ordering options. | ||
Joerg Sonnenberger
|
r47771 | nodes = [n for n in nodes if n != self._repo.nullid] | ||
Augie Fackler
|
r41192 | if not nodes: | ||
return | ||||
for delta in storageutil.emitrevisions( | ||||
Augie Fackler
|
r43346 | self, | ||
nodes, | ||||
nodesorder, | ||||
simplestorerevisiondelta, | ||||
revisiondata=revisiondata, | ||||
assumehaveparentrevisions=assumehaveparentrevisions, | ||||
deltamode=deltamode, | ||||
Raphaël Gomès
|
r47449 | sidedata_helpers=sidedata_helpers, | ||
Augie Fackler
|
r43346 | ): | ||
Augie Fackler
|
r41192 | yield delta | ||
Gregory Szorc
|
r37355 | def add(self, text, meta, transaction, linkrev, p1, p2): | ||
if meta or text.startswith(b'\1\n'): | ||||
Gregory Szorc
|
r39914 | text = storageutil.packmeta(meta, text) | ||
Gregory Szorc
|
r37355 | |||
return self.addrevision(text, transaction, linkrev, p1, p2) | ||||
Augie Fackler
|
r43346 | def addrevision( | ||
self, | ||||
text, | ||||
transaction, | ||||
linkrev, | ||||
p1, | ||||
p2, | ||||
node=None, | ||||
flags=revlog.REVIDX_DEFAULT_FLAGS, | ||||
cachedelta=None, | ||||
): | ||||
Gregory Szorc
|
r37355 | validatenode(p1) | ||
validatenode(p2) | ||||
if flags: | ||||
Gregory Szorc
|
r39913 | node = node or storageutil.hashrevisionsha1(text, p1, p2) | ||
Gregory Szorc
|
r37355 | |||
r43260 | rawtext, validatehash = flagutil.processflagswrite(self, text, flags) | |||
Gregory Szorc
|
r37355 | |||
Gregory Szorc
|
r39913 | node = node or storageutil.hashrevisionsha1(text, p1, p2) | ||
Gregory Szorc
|
r37355 | |||
if node in self._indexbynode: | ||||
return node | ||||
if validatehash: | ||||
self.checkhash(rawtext, node, p1=p1, p2=p2) | ||||
Augie Fackler
|
r43346 | return self._addrawrevision( | ||
node, rawtext, transaction, linkrev, p1, p2, flags | ||||
) | ||||
Gregory Szorc
|
r37454 | |||
def _addrawrevision(self, node, rawtext, transaction, link, p1, p2, flags): | ||||
transaction.addbackup(self._indexpath) | ||||
Gregory Szorc
|
r37355 | path = b'/'.join([self._storepath, hex(node)]) | ||
Gregory Szorc
|
r37454 | self._svfs.write(path, rawtext) | ||
Gregory Szorc
|
r37355 | |||
Augie Fackler
|
r43346 | self._indexdata.append( | ||
{ | ||||
b'node': node, | ||||
b'p1': p1, | ||||
b'p2': p2, | ||||
b'linkrev': link, | ||||
b'flags': flags, | ||||
} | ||||
) | ||||
Gregory Szorc
|
r37355 | |||
self._reflectindexupdate() | ||||
return node | ||||
def _reflectindexupdate(self): | ||||
self._refreshindex() | ||||
Augie Fackler
|
r43346 | self._svfs.write( | ||
self._indexpath, ''.join(cborutil.streamencode(self._indexdata)) | ||||
) | ||||
Gregory Szorc
|
r37355 | |||
Augie Fackler
|
r43346 | def addgroup( | ||
self, | ||||
deltas, | ||||
linkmapper, | ||||
transaction, | ||||
addrevisioncb=None, | ||||
Joerg Sonnenberger
|
r46373 | duplicaterevisioncb=None, | ||
Augie Fackler
|
r43346 | maybemissingparents=False, | ||
): | ||||
Gregory Szorc
|
r40425 | if maybemissingparents: | ||
Augie Fackler
|
r43346 | raise error.Abort( | ||
_('simple store does not support missing parents ' 'write mode') | ||||
) | ||||
Gregory Szorc
|
r40425 | |||
Joerg Sonnenberger
|
r46373 | empty = True | ||
Gregory Szorc
|
r37355 | |||
transaction.addbackup(self._indexpath) | ||||
for node, p1, p2, linknode, deltabase, delta, flags in deltas: | ||||
linkrev = linkmapper(linknode) | ||||
Gregory Szorc
|
r37454 | flags = flags or revlog.REVIDX_DEFAULT_FLAGS | ||
Gregory Szorc
|
r37355 | |||
if node in self._indexbynode: | ||||
Joerg Sonnenberger
|
r46373 | if duplicaterevisioncb: | ||
Joerg Sonnenberger
|
r47259 | duplicaterevisioncb(self, self.rev(node)) | ||
Joerg Sonnenberger
|
r46373 | empty = False | ||
Gregory Szorc
|
r37355 | continue | ||
# Need to resolve the fulltext from the delta base. | ||||
Joerg Sonnenberger
|
r47771 | if deltabase == self._repo.nullid: | ||
Gregory Szorc
|
r37355 | text = mdiff.patch(b'', delta) | ||
else: | ||||
text = mdiff.patch(self.revision(deltabase), delta) | ||||
Joerg Sonnenberger
|
r47259 | rev = self._addrawrevision( | ||
Augie Fackler
|
r43346 | node, text, transaction, linkrev, p1, p2, flags | ||
) | ||||
Gregory Szorc
|
r37355 | |||
if addrevisioncb: | ||||
Joerg Sonnenberger
|
r47259 | addrevisioncb(self, rev) | ||
Joerg Sonnenberger
|
r46373 | empty = False | ||
return not empty | ||||
Gregory Szorc
|
r37355 | |||
Augie Fackler
|
r41192 | def _headrevs(self): | ||
# Assume all revisions are heads by default. | ||||
revishead = {rev: True for rev in self._indexbyrev} | ||||
for rev, entry in self._indexbyrev.items(): | ||||
# Unset head flag for all seen parents. | ||||
revishead[self.rev(entry[b'p1'])] = False | ||||
revishead[self.rev(entry[b'p2'])] = False | ||||
Augie Fackler
|
r43346 | return [rev for rev, ishead in sorted(revishead.items()) if ishead] | ||
Gregory Szorc
|
r37355 | |||
def heads(self, start=None, stop=None): | ||||
# This is copied from revlog.py. | ||||
if start is None and stop is None: | ||||
if not len(self): | ||||
Joerg Sonnenberger
|
r47771 | return [self._repo.nullid] | ||
Augie Fackler
|
r41192 | return [self.node(r) for r in self._headrevs()] | ||
Gregory Szorc
|
r37355 | |||
if start is None: | ||||
Joerg Sonnenberger
|
r47771 | start = self._repo.nullid | ||
Gregory Szorc
|
r37355 | if stop is None: | ||
stop = [] | ||||
Augie Fackler
|
r44937 | stoprevs = {self.rev(n) for n in stop} | ||
Gregory Szorc
|
r37355 | startrev = self.rev(start) | ||
reachable = {startrev} | ||||
heads = {startrev} | ||||
parentrevs = self.parentrevs | ||||
for r in self.revs(start=startrev + 1): | ||||
for p in parentrevs(r): | ||||
if p in reachable: | ||||
if r not in stoprevs: | ||||
reachable.add(r) | ||||
heads.add(r) | ||||
if p in heads and p not in stoprevs: | ||||
heads.remove(p) | ||||
return [self.node(r) for r in heads] | ||||
def children(self, node): | ||||
validatenode(node) | ||||
# This is a copy of revlog.children(). | ||||
c = [] | ||||
p = self.rev(node) | ||||
for r in self.revs(start=p + 1): | ||||
prevs = [pr for pr in self.parentrevs(r) if pr != nullrev] | ||||
if prevs: | ||||
for pr in prevs: | ||||
if pr == p: | ||||
c.append(self.node(r)) | ||||
elif p == nullrev: | ||||
c.append(self.node(r)) | ||||
return c | ||||
def getstrippoint(self, minlink): | ||||
Augie Fackler
|
r41192 | return storageutil.resolvestripinfo( | ||
Augie Fackler
|
r43346 | minlink, | ||
len(self) - 1, | ||||
self._headrevs(), | ||||
self.linkrev, | ||||
self.parentrevs, | ||||
) | ||||
Gregory Szorc
|
r37355 | |||
def strip(self, minlink, transaction): | ||||
if not len(self): | ||||
return | ||||
rev, _ignored = self.getstrippoint(minlink) | ||||
if rev == len(self): | ||||
return | ||||
# Purge index data starting at the requested revision. | ||||
self._indexdata[rev:] = [] | ||||
self._reflectindexupdate() | ||||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r37433 | def issimplestorefile(f, kind, st): | ||
if kind != stat.S_IFREG: | ||||
return False | ||||
if store.isrevlog(f, kind, st): | ||||
return False | ||||
# Ignore transaction undo files. | ||||
if f.startswith('undo.'): | ||||
return False | ||||
# Otherwise assume it belongs to the simple store. | ||||
return True | ||||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r37433 | class simplestore(store.encodedstore): | ||
r51397 | def data_entries(self, undecodable=None): | |||
for x in super(simplestore, self).data_entries(): | ||||
Gregory Szorc
|
r37433 | yield x | ||
# Supplement with non-revlog files. | ||||
extrafiles = self._walk('data', True, filefilter=issimplestorefile) | ||||
Valentin Gatien-Baron
|
r48691 | for f1, size in extrafiles: | ||
Gregory Szorc
|
r37433 | try: | ||
Valentin Gatien-Baron
|
r48691 | f2 = store.decodefilename(f1) | ||
Gregory Szorc
|
r37433 | except KeyError: | ||
Valentin Gatien-Baron
|
r48691 | if undecodable is None: | ||
raise error.StorageError(b'undecodable revlog name %s' % f1) | ||||
else: | ||||
undecodable.append(f1) | ||||
continue | ||||
Gregory Szorc
|
r37433 | |||
Valentin Gatien-Baron
|
r48691 | yield f2, size | ||
Gregory Szorc
|
r37433 | |||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r37355 | def reposetup(ui, repo): | ||
if not repo.local(): | ||||
return | ||||
Gregory Szorc
|
r37364 | if isinstance(repo, bundlerepo.bundlerepository): | ||
raise error.Abort(_('cannot use simple store with bundlerepo')) | ||||
Gregory Szorc
|
r37355 | class simplestorerepo(repo.__class__): | ||
def file(self, f): | ||||
Joerg Sonnenberger
|
r47538 | return filestorage(repo, self.svfs, f) | ||
Gregory Szorc
|
r37355 | |||
repo.__class__ = simplestorerepo | ||||
Gregory Szorc
|
r37433 | |||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r37433 | def featuresetup(ui, supported): | ||
supported.add(REQUIREMENT) | ||||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r41192 | def newreporequirements(orig, ui, createopts): | ||
Gregory Szorc
|
r37433 | """Modifies default requirements for new repos to use the simple store.""" | ||
Augie Fackler
|
r41192 | requirements = orig(ui, createopts) | ||
Gregory Szorc
|
r37433 | |||
# These requirements are only used to affect creation of the store | ||||
# object. We have our own store. So we can remove them. | ||||
# TODO do this once we feel like taking the test hit. | ||||
Augie Fackler
|
r43346 | # if 'fncache' in requirements: | ||
Gregory Szorc
|
r37433 | # requirements.remove('fncache') | ||
Augie Fackler
|
r43346 | # if 'dotencode' in requirements: | ||
Gregory Szorc
|
r37433 | # requirements.remove('dotencode') | ||
requirements.add(REQUIREMENT) | ||||
return requirements | ||||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r37433 | def makestore(orig, requirements, path, vfstype): | ||
if REQUIREMENT not in requirements: | ||||
return orig(requirements, path, vfstype) | ||||
return simplestore(path, vfstype) | ||||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r37435 | def verifierinit(orig, self, *args, **kwargs): | ||
orig(self, *args, **kwargs) | ||||
# We don't care that files in the store don't align with what is | ||||
# advertised. So suppress these warnings. | ||||
self.warnorphanstorefiles = False | ||||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r37433 | def extsetup(ui): | ||
localrepo.featuresetupfuncs.add(featuresetup) | ||||
Augie Fackler
|
r43346 | extensions.wrapfunction( | ||
localrepo, 'newreporequirements', newreporequirements | ||||
) | ||||
Augie Fackler
|
r41192 | extensions.wrapfunction(localrepo, 'makestore', makestore) | ||
Gregory Szorc
|
r37435 | extensions.wrapfunction(verify.verifier, '__init__', verifierinit) | ||