changeset 48232:e7b5e8ba7cab

dirstate: Move more methods to the _dirstatemapcommon base class This reduces duplication slightly and will help with supporting dirstate-v2 when Rust is not enabled. Differential Revision: https://phab.mercurial-scm.org/D11621
author Simon Sapin <simon.sapin@octobus.net>
date Fri, 08 Oct 2021 13:15:22 +0200
parents 308d9c245337
children a32a96079e2d
files mercurial/dirstatemap.py
diffstat 1 files changed, 89 insertions(+), 128 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/dirstatemap.py	Wed Oct 13 17:32:52 2021 +0200
+++ b/mercurial/dirstatemap.py	Fri Oct 08 13:15:22 2021 +0200
@@ -56,6 +56,7 @@
         self._nodelen = 20  # Also update Rust code when changing this!
         self._parents = None
         self._dirtyparents = False
+        self._docket = None
 
         # for consistent view between _pl() and _read() invocations
         self._pendingmode = None
@@ -208,6 +209,93 @@
         )
         self._insert_entry(filename, entry)
 
+    ### disk interaction
+
+    def _opendirstatefile(self):
+        fp, mode = txnutil.trypending(self._root, self._opener, self._filename)
+        if self._pendingmode is not None and self._pendingmode != mode:
+            fp.close()
+            raise error.Abort(
+                _(b'working directory state may be changed parallelly')
+            )
+        self._pendingmode = mode
+        return fp
+
+    def _readdirstatefile(self, size=-1):
+        try:
+            with self._opendirstatefile() as fp:
+                return fp.read(size)
+        except IOError as err:
+            if err.errno != errno.ENOENT:
+                raise
+            # File doesn't exist, so the current state is empty
+            return b''
+
+    @property
+    def docket(self):
+        if not self._docket:
+            if not self._use_dirstate_v2:
+                raise error.ProgrammingError(
+                    b'dirstate only has a docket in v2 format'
+                )
+            self._docket = docketmod.DirstateDocket.parse(
+                self._readdirstatefile(), self._nodeconstants
+            )
+        return self._docket
+
+    def write_v2_no_append(self, tr, st, meta, packed):
+        old_docket = self.docket
+        new_docket = docketmod.DirstateDocket.with_new_uuid(
+            self.parents(), len(packed), meta
+        )
+        data_filename = new_docket.data_filename()
+        if tr:
+            tr.add(data_filename, 0)
+        self._opener.write(data_filename, packed)
+        # Write the new docket after the new data file has been
+        # written. Because `st` was opened with `atomictemp=True`,
+        # the actual `.hg/dirstate` file is only affected on close.
+        st.write(new_docket.serialize())
+        st.close()
+        # Remove the old data file after the new docket pointing to
+        # the new data file was written.
+        if old_docket.uuid:
+            data_filename = old_docket.data_filename()
+            unlink = lambda _tr=None: self._opener.unlink(data_filename)
+            if tr:
+                category = b"dirstate-v2-clean-" + old_docket.uuid
+                tr.addpostclose(category, unlink)
+            else:
+                unlink()
+        self._docket = new_docket
+
+    ### reading/setting parents
+
+    def parents(self):
+        if not self._parents:
+            if self._use_dirstate_v2:
+                self._parents = self.docket.parents
+            else:
+                read_len = self._nodelen * 2
+                st = self._readdirstatefile(read_len)
+                l = len(st)
+                if l == read_len:
+                    self._parents = (
+                        st[: self._nodelen],
+                        st[self._nodelen : 2 * self._nodelen],
+                    )
+                elif l == 0:
+                    self._parents = (
+                        self._nodeconstants.nullid,
+                        self._nodeconstants.nullid,
+                    )
+                else:
+                    raise error.Abort(
+                        _(b'working directory state appears damaged!')
+                    )
+
+        return self._parents
+
 
 class dirstatemap(_dirstatemapcommon):
     """Map encapsulating the dirstate's contents.
@@ -295,36 +383,6 @@
 
     ### reading/setting parents
 
-    def parents(self):
-        if not self._parents:
-            try:
-                fp = self._opendirstatefile()
-                st = fp.read(2 * self._nodelen)
-                fp.close()
-            except IOError as err:
-                if err.errno != errno.ENOENT:
-                    raise
-                # File doesn't exist, so the current state is empty
-                st = b''
-
-            l = len(st)
-            if l == self._nodelen * 2:
-                self._parents = (
-                    st[: self._nodelen],
-                    st[self._nodelen : 2 * self._nodelen],
-                )
-            elif l == 0:
-                self._parents = (
-                    self._nodeconstants.nullid,
-                    self._nodeconstants.nullid,
-                )
-            else:
-                raise error.Abort(
-                    _(b'working directory state appears damaged!')
-                )
-
-        return self._parents
-
     def setparents(self, p1, p2, fold_p2=False):
         self._parents = (p1, p2)
         self._dirtyparents = True
@@ -397,16 +455,6 @@
         st.close()
         self._dirtyparents = False
 
-    def _opendirstatefile(self):
-        fp, mode = txnutil.trypending(self._root, self._opener, self._filename)
-        if self._pendingmode is not None and self._pendingmode != mode:
-            fp.close()
-            raise error.Abort(
-                _(b'working directory state may be changed parallelly')
-            )
-        self._pendingmode = mode
-        return fp
-
     @propertycache
     def identity(self):
         self._map
@@ -506,26 +554,9 @@
 if rustmod is not None:
 
     class dirstatemap(_dirstatemapcommon):
-        def __init__(self, ui, opener, root, nodeconstants, use_dirstate_v2):
-            super(dirstatemap, self).__init__(
-                ui, opener, root, nodeconstants, use_dirstate_v2
-            )
-            self._docket = None
 
         ### Core data storage and access
 
-        @property
-        def docket(self):
-            if not self._docket:
-                if not self._use_dirstate_v2:
-                    raise error.ProgrammingError(
-                        b'dirstate only has a docket in v2 format'
-                    )
-                self._docket = docketmod.DirstateDocket.parse(
-                    self._readdirstatefile(), self._nodeconstants
-                )
-            return self._docket
-
         @propertycache
         def _map(self):
             """
@@ -616,31 +647,6 @@
                     rust_map.set_dirstate_item(f, e)
             return copies
 
-        def parents(self):
-            if not self._parents:
-                if self._use_dirstate_v2:
-                    self._parents = self.docket.parents
-                else:
-                    read_len = self._nodelen * 2
-                    st = self._readdirstatefile(read_len)
-                    l = len(st)
-                    if l == read_len:
-                        self._parents = (
-                            st[: self._nodelen],
-                            st[self._nodelen : 2 * self._nodelen],
-                        )
-                    elif l == 0:
-                        self._parents = (
-                            self._nodeconstants.nullid,
-                            self._nodeconstants.nullid,
-                        )
-                    else:
-                        raise error.Abort(
-                            _(b'working directory state appears damaged!')
-                        )
-
-            return self._parents
-
         ### disk interaction
 
         @propertycache
@@ -677,56 +683,11 @@
                 st.write(docket.serialize())
                 st.close()
             else:
-                old_docket = self.docket
-                new_docket = docketmod.DirstateDocket.with_new_uuid(
-                    self.parents(), len(packed), meta
-                )
-                data_filename = new_docket.data_filename()
-                if tr:
-                    tr.add(data_filename, 0)
-                self._opener.write(data_filename, packed)
-                # Write the new docket after the new data file has been
-                # written. Because `st` was opened with `atomictemp=True`,
-                # the actual `.hg/dirstate` file is only affected on close.
-                st.write(new_docket.serialize())
-                st.close()
-                # Remove the old data file after the new docket pointing to
-                # the new data file was written.
-                if old_docket.uuid:
-                    data_filename = old_docket.data_filename()
-                    unlink = lambda _tr=None: self._opener.unlink(data_filename)
-                    if tr:
-                        category = b"dirstate-v2-clean-" + old_docket.uuid
-                        tr.addpostclose(category, unlink)
-                    else:
-                        unlink()
-                self._docket = new_docket
+                self.write_v2_no_append(tr, st, meta, packed)
             # Reload from the newly-written file
             util.clearcachedproperty(self, b"_map")
             self._dirtyparents = False
 
-        def _opendirstatefile(self):
-            fp, mode = txnutil.trypending(
-                self._root, self._opener, self._filename
-            )
-            if self._pendingmode is not None and self._pendingmode != mode:
-                fp.close()
-                raise error.Abort(
-                    _(b'working directory state may be changed parallelly')
-                )
-            self._pendingmode = mode
-            return fp
-
-        def _readdirstatefile(self, size=-1):
-            try:
-                with self._opendirstatefile() as fp:
-                    return fp.read(size)
-            except IOError as err:
-                if err.errno != errno.ENOENT:
-                    raise
-                # File doesn't exist, so the current state is empty
-                return b''
-
         ### code related to maintaining and accessing "extra" property
         # (e.g. "has_dir")