Changeset View
Changeset View
Standalone View
Standalone View
mercurial/dirstateutils/docket.py
# dirstatedocket.py - docket file for dirstate-v2 | # dirstatedocket.py - docket file for dirstate-v2 | ||||
# | # | ||||
# Copyright Mercurial Contributors | # Copyright Mercurial Contributors | ||||
# | # | ||||
# This software may be used and distributed according to the terms of the | # This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | # GNU General Public License version 2 or any later version. | ||||
from __future__ import absolute_import | from __future__ import absolute_import | ||||
import struct | import struct | ||||
from ..revlogutils import docket as docket_mod | from ..revlogutils import docket as docket_mod | ||||
V2_FORMAT_MARKER = b"dirstate-v2\n" | V2_FORMAT_MARKER = b"dirstate-v2\n" | ||||
# Must match the constant of the same name in | |||||
# `rust/hg-core/src/dirstate_tree/on_disk.rs` | |||||
TREE_METADATA_SIZE = 40 | |||||
# * 12 bytes: format marker | # * 12 bytes: format marker | ||||
# * 32 bytes: node ID of the working directory's first parent | # * 32 bytes: node ID of the working directory's first parent | ||||
# * 32 bytes: node ID of the working directory's second parent | # * 32 bytes: node ID of the working directory's second parent | ||||
# * 4 bytes: big-endian used size of the data file | # * 4 bytes: big-endian used size of the data file | ||||
# * {TREE_METADATA_SIZE} bytes: tree metadata, parsed separately | |||||
# * 1 byte: length of the data file's UUID | # * 1 byte: length of the data file's UUID | ||||
# * variable: data file's UUID | # * variable: data file's UUID | ||||
# | # | ||||
# Node IDs are null-padded if shorter than 32 bytes. | # Node IDs are null-padded if shorter than 32 bytes. | ||||
# A data file shorter than the specified used size is corrupted (truncated) | # A data file shorter than the specified used size is corrupted (truncated) | ||||
HEADER = struct.Struct(">{}s32s32sLB".format(len(V2_FORMAT_MARKER))) | HEADER = struct.Struct( | ||||
">{}s32s32sL{}sB".format(len(V2_FORMAT_MARKER), TREE_METADATA_SIZE) | |||||
) | |||||
class DirstateDocket(object): | class DirstateDocket(object): | ||||
data_filename_pattern = b'dirstate.%s.d' | data_filename_pattern = b'dirstate.%s.d' | ||||
def __init__(self, parents, data_size, uuid): | def __init__(self, parents, data_size, tree_metadata, uuid): | ||||
self.parents = parents | self.parents = parents | ||||
self.data_size = data_size | self.data_size = data_size | ||||
self.tree_metadata = tree_metadata | |||||
self.uuid = uuid | self.uuid = uuid | ||||
@classmethod | @classmethod | ||||
def with_new_uuid(cls, parents, data): | def with_new_uuid(cls, parents, data_size, tree_metadata): | ||||
return cls(parents, data, docket_mod.make_uid()) | return cls(parents, data_size, tree_metadata, docket_mod.make_uid()) | ||||
@classmethod | @classmethod | ||||
def parse(cls, data, nodeconstants): | def parse(cls, data, nodeconstants): | ||||
if not data: | if not data: | ||||
parents = (nodeconstants.nullid, nodeconstants.nullid) | parents = (nodeconstants.nullid, nodeconstants.nullid) | ||||
return cls(parents, 0, None) | return cls(parents, 0, b'', None) | ||||
marker, p1, p2, data_size, uuid_size = HEADER.unpack_from(data) | marker, p1, p2, data_size, meta, uuid_size = HEADER.unpack_from(data) | ||||
if marker != V2_FORMAT_MARKER: | if marker != V2_FORMAT_MARKER: | ||||
raise ValueError("expected dirstate-v2 marker") | raise ValueError("expected dirstate-v2 marker") | ||||
uuid = data[HEADER.size : HEADER.size + uuid_size] | uuid = data[HEADER.size : HEADER.size + uuid_size] | ||||
p1 = p1[: nodeconstants.nodelen] | p1 = p1[: nodeconstants.nodelen] | ||||
p2 = p2[: nodeconstants.nodelen] | p2 = p2[: nodeconstants.nodelen] | ||||
return cls((p1, p2), data_size, uuid) | return cls((p1, p2), data_size, meta, uuid) | ||||
def serialize(self): | def serialize(self): | ||||
p1, p2 = self.parents | p1, p2 = self.parents | ||||
header = HEADER.pack( | header = HEADER.pack( | ||||
V2_FORMAT_MARKER, p1, p2, self.data_size, len(self.uuid) | V2_FORMAT_MARKER, | ||||
p1, | |||||
p2, | |||||
self.data_size, | |||||
self.tree_metadata, | |||||
len(self.uuid), | |||||
) | ) | ||||
return header + self.uuid | return header + self.uuid | ||||
def data_filename(self): | def data_filename(self): | ||||
return self.data_filename_pattern % self.uuid | return self.data_filename_pattern % self.uuid |