Patchwork D11364: dirstate-item: keep the full information in memory (for pure form)

login
register
mail settings
Submitter phabricator
Date Aug. 27, 2021, 2:49 p.m.
Message ID <differential-rev-PHID-DREV-xdm3yynkgzl7otmr52ib-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/49679/
State Superseded
Headers show

Comments

phabricator - Aug. 27, 2021, 2:49 p.m.
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This changeset is finally reaching the area where we have been headed this whole
  time. Since all implementation details are contained inside de DirstateItem
  logic, we can change the way it is implemented.
  
  So we store the information that are passed to the object and use them to
  dynamically compute the "legacy" value.
  
  For now we only do this for the Pure implementation, as this is a good
  demonstration for the logic is fully insulated.
  
  The next step will be the update the C code too, to implement the various method
  with the new attribute (instead of the legacy one) and then start seriously
  thinking about the parameters we feed into DirstateItem.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11364

AFFECTED FILES
  mercurial/pure/parsers.py

CHANGE DETAILS




To: marmoute, #hg-reviewers
Cc: mercurial-patches, mercurial-devel

Patch

diff --git a/mercurial/pure/parsers.py b/mercurial/pure/parsers.py
--- a/mercurial/pure/parsers.py
+++ b/mercurial/pure/parsers.py
@@ -56,7 +56,17 @@ 
     - mtime,
     """
 
-    _state = attr.ib()
+    _wc_tracked = attr.ib()
+    _p1_tracked = attr.ib()
+    _p2_tracked = attr.ib()
+    # the three item above should probably be combined
+    #
+    # However it is unclear if they properly cover some of the most advanced
+    # merge case. So we should probably wait on this to be settled.
+    _merged = attr.ib()
+    _clean_p1 = attr.ib()
+    _clean_p2 = attr.ib()
+    _possibly_dirty = attr.ib()
     _mode = attr.ib()
     _size = attr.ib()
     _mtime = attr.ib()
@@ -76,47 +86,21 @@ 
             msg = b'`merged` argument incompatible with `clean_p1`/`clean_p2`'
             raise error.ProgrammingError(msg)
 
-        self._state = None
-        self._mode = 0
-        self._size = NONNORMAL
-        self._mtime = AMBIGUOUS_TIME
-        if not (p1_tracked or p2_tracked or wc_tracked):
-            pass  # the object has no state to record
-        elif merged:
-            self._state = b'm'
-            self._size = FROM_P2
-            self._mtime = AMBIGUOUS_TIME
-        elif not (p1_tracked or p2_tracked) and wc_tracked:
-            self._state = b'a'
-            self._size = NONNORMAL
-            self._mtime = AMBIGUOUS_TIME
-        elif (p1_tracked or p2_tracked) and not wc_tracked:
-            self._state = b'r'
-            self._size = 0
-            self._mtime = 0
-        elif clean_p2 and wc_tracked:
-            self._state = b'n'
-            self._size = FROM_P2
-            self._mtime = AMBIGUOUS_TIME
-        elif not p1_tracked and p2_tracked and wc_tracked:
-            self._state = b'n'
-            self._size = FROM_P2
-            self._mtime = AMBIGUOUS_TIME
-        elif possibly_dirty:
-            self._state = b'n'
-            self._size = NONNORMAL
-            self._mtime = AMBIGUOUS_TIME
-        elif wc_tracked:
-            # this is a "normal" file
-            if parentfiledata is None:
-                msg = b'failed to pass parentfiledata for a normal file'
-                raise error.ProgrammingError(msg)
-            self._state = b'n'
+        self._wc_tracked = wc_tracked
+        self._p1_tracked = p1_tracked
+        self._p2_tracked = p2_tracked
+        self._merged = merged
+        self._clean_p1 = clean_p1
+        self._clean_p2 = clean_p2
+        self._possibly_dirty = possibly_dirty
+        if parentfiledata is None:
+            self._mode = None
+            self._size = None
+            self._mtime = None
+        else:
             self._mode = parentfiledata[0]
             self._size = parentfiledata[1]
             self._mtime = parentfiledata[2]
-        else:
-            assert False, 'unreachable'
 
     @classmethod
     def new_added(cls):
@@ -125,10 +109,9 @@ 
         Should eventually be removed
         """
         instance = cls()
-        instance._state = b'a'
-        instance._mode = 0
-        instance._size = NONNORMAL
-        instance._mtime = AMBIGUOUS_TIME
+        instance._wc_tracked = True
+        instance._p1_tracked = False
+        instance._p2_tracked = False
         return instance
 
     @classmethod
@@ -138,10 +121,10 @@ 
         Should eventually be removed
         """
         instance = cls()
-        instance._state = b'm'
-        instance._mode = 0
-        instance._size = FROM_P2
-        instance._mtime = AMBIGUOUS_TIME
+        instance._wc_tracked = True
+        instance._p1_tracked = True  # might not be True because of rename ?
+        instance._p2_tracked = True  # might not be True because of rename ?
+        instance._merged = True
         return instance
 
     @classmethod
@@ -151,10 +134,10 @@ 
         Should eventually be removed
         """
         instance = cls()
-        instance._state = b'n'
-        instance._mode = 0
-        instance._size = FROM_P2
-        instance._mtime = AMBIGUOUS_TIME
+        instance._wc_tracked = True
+        instance._p1_tracked = False  # might actually be True
+        instance._p2_tracked = True
+        instance._clean_p2 = True
         return instance
 
     @classmethod
@@ -164,10 +147,9 @@ 
         Should eventually be removed
         """
         instance = cls()
-        instance._state = b'n'
-        instance._mode = 0
-        instance._size = NONNORMAL
-        instance._mtime = AMBIGUOUS_TIME
+        instance._wc_tracked = True
+        instance._p1_tracked = True
+        instance._possibly_dirty = True
         return instance
 
     @classmethod
@@ -179,7 +161,8 @@ 
         assert size != FROM_P2
         assert size != NONNORMAL
         instance = cls()
-        instance._state = b'n'
+        instance._wc_tracked = True
+        instance._p1_tracked = True
         instance._mode = mode
         instance._size = size
         instance._mtime = mtime
@@ -192,12 +175,44 @@ 
         Since the dirstate-v1 format is frozen, the signature of this function
         is not expected to change, unlike the __init__ one.
         """
-        instance = cls()
-        instance._state = state
-        instance._mode = mode
-        instance._size = size
-        instance._mtime = mtime
-        return instance
+        if state == b'm':
+            return cls.new_merged()
+        elif state == b'a':
+            return cls.new_added()
+        elif state == b'r':
+            instance = cls()
+            instance._wc_tracked = False
+            if size == NONNORMAL:
+                instance._merged = True
+                instance._p1_tracked = (
+                    True  # might not be True because of rename ?
+                )
+                instance._p2_tracked = (
+                    True  # might not be True because of rename ?
+                )
+            elif size == FROM_P2:
+                instance._clean_p2 = True
+                instance._p1_tracked = (
+                    False  # We actually don't know (file history)
+                )
+                instance._p2_tracked = True
+            else:
+                instance._p1_tracked = True
+            return instance
+        elif state == b'n':
+            if size == FROM_P2:
+                return cls.new_from_p2()
+            elif size == NONNORMAL:
+                return cls.new_possibly_dirty()
+            elif mtime == AMBIGUOUS_TIME:
+                instance = cls.new_normal(mode, size, 42)
+                instance._mtime = None
+                instance._possibly_dirty = True
+                return instance
+            else:
+                return cls.new_normal(mode, size, mtime)
+        else:
+            raise RuntimeError(b'unknown state: %s' % state)
 
     def set_possibly_dirty(self):
         """Mark a file as "possibly dirty"
@@ -205,7 +220,7 @@ 
         This means the next status call will have to actually check its content
         to make sure it is correct.
         """
-        self._mtime = AMBIGUOUS_TIME
+        self._possibly_dirty = True
 
     def set_untracked(self):
         """mark a file as untracked in the working copy
@@ -213,15 +228,10 @@ 
         This will ultimately be called by command like `hg remove`.
         """
         # backup the previous state (useful for merge)
-        size = 0
-        if self.merged:  # merge
-            size = NONNORMAL
-        elif self.from_p2:
-            size = FROM_P2
-        self._state = b'r'
-        self._mode = 0
-        self._size = size
-        self._mtime = 0
+        self._wc_tracked = False
+        self._mode = None
+        self._size = None
+        self._mtime = None
 
     @property
     def mode(self):
@@ -319,19 +329,91 @@ 
 
     def v1_state(self):
         """return a "state" suitable for v1 serialization"""
-        return self._state
+        if not (self._p1_tracked or self._p2_tracked or self._wc_tracked):
+            # the object has no state to record, this is -currently-
+            # unsupported
+            raise RuntimeError('untracked item')
+        elif not self._wc_tracked:
+            return b'r'
+        elif self._merged:
+            return b'm'
+        elif not (self._p1_tracked or self._p2_tracked) and self._wc_tracked:
+            return b'a'
+        elif self._clean_p2 and self._wc_tracked:
+            return b'n'
+        elif not self._p1_tracked and self._p2_tracked and self._wc_tracked:
+            return b'n'
+        elif self._possibly_dirty:
+            return b'n'
+        elif self._wc_tracked:
+            return b'n'
+        else:
+            raise RuntimeError('unreachable')
 
     def v1_mode(self):
         """return a "mode" suitable for v1 serialization"""
+        if self._mode is None:
+            return 0
         return self._mode
 
     def v1_size(self):
         """return a "size" suitable for v1 serialization"""
-        return self._size
+        if not (self._p1_tracked or self._p2_tracked or self._wc_tracked):
+            # the object has no state to record, this is -currently-
+            # unsupported
+            raise RuntimeError('untracked item')
+        elif not self._wc_tracked:
+            # File was deleted
+            if self._merged:
+                return NONNORMAL
+            elif self._clean_p2:
+                return FROM_P2
+            else:
+                return 0
+        elif self._merged:
+            return FROM_P2
+        elif not (self._p1_tracked or self._p2_tracked) and self._wc_tracked:
+            # Added
+            return NONNORMAL
+        elif self._clean_p2 and self._wc_tracked:
+            return FROM_P2
+        elif not self._p1_tracked and self._p2_tracked and self._wc_tracked:
+            return FROM_P2
+        elif self._possibly_dirty:
+            if self._size is None:
+                return NONNORMAL
+            else:
+                return self._size
+        elif self._wc_tracked:
+            return self._size
+        else:
+            raise RuntimeError('unreachable')
 
     def v1_mtime(self):
         """return a "mtime" suitable for v1 serialization"""
-        return self._mtime
+        if not (self._p1_tracked or self._p2_tracked or self._wc_tracked):
+            # the object has no state to record, this is -currently-
+            # unsupported
+            raise RuntimeError('untracked item')
+        elif not self._wc_tracked:
+            return 0
+        elif self._possibly_dirty:
+            return AMBIGUOUS_TIME
+        elif self._merged:
+            return AMBIGUOUS_TIME
+        elif not (self._p1_tracked or self._p2_tracked) and self._wc_tracked:
+            return AMBIGUOUS_TIME
+        elif self._clean_p2 and self._wc_tracked:
+            return AMBIGUOUS_TIME
+        elif not self._p1_tracked and self._p2_tracked and self._wc_tracked:
+            return AMBIGUOUS_TIME
+        elif self._wc_tracked:
+            if self._mtime is None:
+                return 0
+            else:
+                return self._mtime
+        else:
+            raise RuntimeError('unreachable')
 
     def need_delay(self, now):
         """True if the stored mtime would be ambiguous with the current time"""