changeset 50080:0dc2fb4b4b11

dirstate: factor the "changing" context logic out This makes it reusable for other types of changes.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Thu, 26 Jan 2023 17:16:24 +0100
parents e1cff85484e2
children d50d45cd5a5f
files mercurial/dirstate.py
diffstat 1 files changed, 31 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/dirstate.py	Thu Jan 26 15:50:45 2023 +0100
+++ b/mercurial/dirstate.py	Thu Jan 26 17:16:24 2023 +0100
@@ -91,6 +91,9 @@
     return wrap
 
 
+CHANGE_TYPE_PARENTS = "parents"
+
+
 @interfaceutil.implementer(intdirstate.idirstate)
 class dirstate:
     def __init__(
@@ -129,6 +132,8 @@
         self._filecache = {}
         # nesting level of `changing_parents` context
         self._changing_level = 0
+        # the change currently underway
+        self._change_type = None
         # True if the current dirstate changing operations have been
         # invalidated (used to make sure all nested contexts have been exited)
         self._invalidated_context = False
@@ -151,19 +156,25 @@
         self._pl
 
     @contextlib.contextmanager
-    def changing_parents(self, repo):
-        """Context manager for handling dirstate parents.
-
-        If an exception occurs in the scope of the context manager,
-        the incoherent dirstate won't be written when wlock is
-        released.
-        """
+    def _changing(self, repo, change_type):
         if repo.currentwlock() is None:
-            msg = b"changing parents without holding the wlock"
+            msg = b"trying to change the dirstate without holding the wlock"
             raise error.ProgrammingError(msg)
         if self._invalidated_context:
             msg = "trying to use an invalidated dirstate before it has reset"
             raise error.ProgrammingError(msg)
+
+        # different type of change are mutually exclusive
+        if self._change_type is None:
+            assert self._changing_level == 0
+            self._change_type = change_type
+        elif self._change_type != change_type:
+            msg = (
+                'trying to open "%s" dirstate-changing context while a "%s" is'
+                ' already open'
+            )
+            msg %= (change_type, self._change_type)
+            raise error.ProgrammingError(msg)
         self._changing_level += 1
         try:
             yield
@@ -180,6 +191,7 @@
                 # The invalidation is complete once we exit the final context
                 # manager
                 if self._changing_level <= 0:
+                    self._change_type = None
                     assert self._changing_level == 0
                     if self._invalidated_context:
                         self._invalidated_context = False
@@ -194,6 +206,11 @@
                         # instead of the top level one.
                         self.write(repo.currenttransaction())
 
+    @contextlib.contextmanager
+    def changing_parents(self, repo):
+        with self._changing(repo, CHANGE_TYPE_PARENTS) as c:
+            yield c
+
     # here to help migration to the new code
     def parentchange(self):
         msg = (
@@ -211,6 +228,9 @@
         return self._changing_level > 0
 
     def pendingparentchange(self):
+        return self.is_changing_parent()
+
+    def is_changing_parent(self):
         """Returns true if the dirstate is in the middle of a set of changes
         that modify the dirstate parent.
         """
@@ -222,7 +242,9 @@
         """Returns true if the dirstate is in the middle of a set of changes
         that modify the dirstate parent.
         """
-        return self._changing_level > 0
+        if self._changing_level <= 0:
+            return False
+        return self._change_type == CHANGE_TYPE_PARENTS
 
     @propertycache
     def _map(self):