docket.py
138 lines
| 4.3 KiB
| text/x-python
|
PythonLexer
r48008 | # docket - code related to revlog "docket" | |||
# | ||||
# Copyright 2021 Pierre-Yves David <pierre-yves.david@octobus.net> | ||||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
### Revlog docket file | ||||
# | ||||
# The revlog is stored on disk using multiple files: | ||||
# | ||||
# * a small docket file, containing metadata and a pointer, | ||||
# | ||||
# * an index file, containing fixed width information about revisions, | ||||
# | ||||
# * a data file, containing variable width data for these revisions, | ||||
from __future__ import absolute_import | ||||
import struct | ||||
r48015 | from .. import ( | |||
error, | ||||
) | ||||
r48008 | from . import ( | |||
constants, | ||||
) | ||||
# Docket format | ||||
# | ||||
# * 4 bytes: revlog version | ||||
# | This is mandatory as docket must be compatible with the previous | ||||
# | revlog index header. | ||||
r48012 | # * 8 bytes: size of index data | |||
r48015 | # * 8 bytes: pending size of index data | |||
S_HEADER = struct.Struct(constants.INDEX_HEADER.format + 'LL') | ||||
r48008 | ||||
class RevlogDocket(object): | ||||
"""metadata associated with revlog""" | ||||
r48015 | def __init__( | |||
self, | ||||
revlog, | ||||
use_pending=False, | ||||
version_header=None, | ||||
index_end=0, | ||||
pending_index_end=0, | ||||
): | ||||
r48008 | self._version_header = version_header | |||
r48015 | self._read_only = bool(use_pending) | |||
r48008 | self._dirty = False | |||
self._radix = revlog.radix | ||||
self._path = revlog._docket_file | ||||
self._opener = revlog.opener | ||||
r48015 | # this assert should be True as long as we have a single index filename | |||
assert index_end <= pending_index_end | ||||
self._initial_index_end = index_end | ||||
self._pending_index_end = pending_index_end | ||||
if use_pending: | ||||
self._index_end = self._pending_index_end | ||||
else: | ||||
self._index_end = self._initial_index_end | ||||
r48008 | ||||
def index_filepath(self): | ||||
"""file path to the current index file associated to this docket""" | ||||
# very simplistic version at first | ||||
return b"%s.idx" % self._radix | ||||
r48012 | @property | |||
def index_end(self): | ||||
return self._index_end | ||||
@index_end.setter | ||||
def index_end(self, new_size): | ||||
if new_size != self._index_end: | ||||
self._index_end = new_size | ||||
self._dirty = True | ||||
r48015 | def write(self, transaction, pending=False, stripping=False): | |||
r48008 | """write the modification of disk if any | |||
This make the new content visible to all process""" | ||||
r48015 | if not self._dirty: | |||
return False | ||||
else: | ||||
if self._read_only: | ||||
msg = b'writing read-only docket: %s' | ||||
msg %= self._path | ||||
raise error.ProgrammingError(msg) | ||||
r48012 | if not stripping: | |||
# XXX we could, leverage the docket while stripping. However it | ||||
# is not powerfull enough at the time of this comment | ||||
transaction.addbackup(self._path, location=b'store') | ||||
r48008 | with self._opener(self._path, mode=b'w', atomictemp=True) as f: | |||
r48015 | f.write(self._serialize(pending=pending)) | |||
# if pending we still need to the write final data eventually | ||||
self._dirty = pending | ||||
return True | ||||
r48008 | ||||
r48015 | def _serialize(self, pending=False): | |||
if pending: | ||||
official_index_end = self._initial_index_end | ||||
else: | ||||
official_index_end = self._index_end | ||||
# this assert should be True as long as we have a single index filename | ||||
assert official_index_end <= self._index_end | ||||
r48012 | data = ( | |||
self._version_header, | ||||
r48015 | official_index_end, | |||
r48012 | self._index_end, | |||
) | ||||
return S_HEADER.pack(*data) | ||||
r48008 | ||||
def default_docket(revlog, version_header): | ||||
"""given a revlog version a new docket object for the given revlog""" | ||||
if (version_header & 0xFFFF) != constants.REVLOGV2: | ||||
return None | ||||
docket = RevlogDocket(revlog, version_header=version_header) | ||||
docket._dirty = True | ||||
return docket | ||||
r48015 | def parse_docket(revlog, data, use_pending=False): | |||
r48008 | """given some docket data return a docket object for the given revlog""" | |||
header = S_HEADER.unpack(data[: S_HEADER.size]) | ||||
r48015 | version_header, index_size, pending_index_size = header | |||
r48008 | docket = RevlogDocket( | |||
revlog, | ||||
r48015 | use_pending=use_pending, | |||
r48008 | version_header=version_header, | |||
r48012 | index_end=index_size, | |||
r48015 | pending_index_end=pending_index_size, | |||
r48008 | ) | |||
return docket | ||||