diff mercurial/dirstateutils/docket.py @ 47674:ff97e793ed36

dirstate-v2: Introduce a docket file .hg/dirstate now only contains some metadata to point to a separate data file named .hg/dirstate.{}.d with a random hexadecimal identifier. For now every update creates a new data file and removes the old one, but later we?ll (usually) append to an existing file. Separating into two files allows doing the "write to a temporary file then atomically rename into destination" dance with only a small docket file, without always rewriting a lot of data. Differential Revision: https://phab.mercurial-scm.org/D11088
author Simon Sapin <simon.sapin@octobus.net>
date Thu, 08 Jul 2021 12:18:21 +0200
parents
children 78f7f0d490ee
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/dirstateutils/docket.py	Thu Jul 08 12:18:21 2021 +0200
@@ -0,0 +1,62 @@
+# dirstatedocket.py - docket file for dirstate-v2
+#
+# Copyright Mercurial Contributors
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+from __future__ import absolute_import
+
+import struct
+
+from ..revlogutils import docket as docket_mod
+
+
+V2_FORMAT_MARKER = b"dirstate-v2\n"
+
+# * 12 bytes: format marker
+# * 32 bytes: node ID of the working directory's first parent
+# * 32 bytes: node ID of the working directory's second parent
+# * 4 bytes: big-endian used size of the data file
+# * 1 byte: length of the data file's UUID
+# * variable: data file's UUID
+#
+# Node IDs are null-padded if shorter than 32 bytes.
+# A data file shorter than the specified used size is corrupted (truncated)
+HEADER = struct.Struct(">{}s32s32sLB".format(len(V2_FORMAT_MARKER)))
+
+
+class DirstateDocket(object):
+    data_filename_pattern = b'dirstate.%s.d'
+
+    def __init__(self, parents, data_size, uuid):
+        self.parents = parents
+        self.data_size = data_size
+        self.uuid = uuid
+
+    @classmethod
+    def with_new_uuid(cls, parents, data):
+        return cls(parents, data, docket_mod.make_uid())
+
+    @classmethod
+    def parse(cls, data, nodeconstants):
+        if not data:
+            parents = (nodeconstants.nullid, nodeconstants.nullid)
+            return cls(parents, 0, None)
+        marker, p1, p2, data_size, uuid_size = HEADER.unpack_from(data)
+        if marker != V2_FORMAT_MARKER:
+            raise ValueError("expected dirstate-v2 marker")
+        uuid = data[HEADER.size : HEADER.size + uuid_size]
+        p1 = p1[: nodeconstants.nodelen]
+        p2 = p2[: nodeconstants.nodelen]
+        return cls((p1, p2), data_size, uuid)
+
+    def serialize(self):
+        p1, p2 = self.parents
+        header = HEADER.pack(
+            V2_FORMAT_MARKER, p1, p2, self.data_size, len(self.uuid)
+        )
+        return header + self.uuid
+
+    def data_filename(self):
+        return self.data_filename_pattern % self.uuid