mercurial/pure/parsers.py
changeset 48138 38488d488ec1
parent 48113 e2da3ec94169
child 48142 fb3b41d583c2
--- a/mercurial/pure/parsers.py	Sat Oct 02 11:39:57 2021 +0200
+++ b/mercurial/pure/parsers.py	Fri Oct 01 20:35:30 2021 +0200
@@ -53,36 +53,32 @@
     # about file tracking
     - wc_tracked: is the file tracked by the working copy
     - p1_tracked: is the file tracked in working copy first parent
-    - p2_tracked: is the file tracked in working copy second parent
-
-    # about what possible merge action related to this file
-    - clean_p1: merge picked the file content from p1
-    - clean_p2: merge picked the file content from p2
-    - merged: file gather changes from both side.
+    - p2_info: the file has been involved in some merge operation. Either
+               because it was actually merged, or because the p2 version was
+               ahead, or because some renamed moved it there. In either case
+               `hg status` will want it displayed as modified.
 
     # about the file state expected from p1 manifest:
     - mode: the file mode in p1
     - size: the file size in p1
 
+    These value can be set to None, which mean we don't have a meaningful value
+    to compare with. Either because we don't really care about them as there
+    `status` is known without having to look at the disk or because we don't
+    know these right now and a full comparison will be needed to find out if
+    the file is clean.
+
     # about the file state on disk last time we saw it:
     - mtime: the last known clean mtime for the file.
 
-    The last three item (mode, size and mtime) can be None if no meaningful (or
-    trusted) value exists.
-
+    This value can be set to None if no cachable state exist. Either because we
+    do not care (see previous section) or because we could not cache something
+    yet.
     """
 
     _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()
+    _p2_info = attr.ib()
     _mode = attr.ib()
     _size = attr.ib()
     _mtime = attr.ib()
@@ -91,32 +87,25 @@
         self,
         wc_tracked=False,
         p1_tracked=False,
-        p2_tracked=False,
-        merged=False,
-        clean_p1=False,
-        clean_p2=False,
-        possibly_dirty=False,
+        p2_info=False,
+        has_meaningful_data=True,
+        has_meaningful_mtime=True,
         parentfiledata=None,
     ):
-        if merged and (clean_p1 or clean_p2):
-            msg = b'`merged` argument incompatible with `clean_p1`/`clean_p2`'
-            raise error.ProgrammingError(msg)
-
-        assert not (merged and not p1_tracked)
         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
+        self._p2_info = p2_info
+
+        self._mode = None
+        self._size = None
+        self._mtime = None
         if parentfiledata is None:
-            self._mode = None
-            self._size = None
-            self._mtime = None
-        else:
+            has_meaningful_mtime = False
+            has_meaningful_data = False
+        if has_meaningful_data:
             self._mode = parentfiledata[0]
             self._size = parentfiledata[1]
+        if has_meaningful_mtime:
             self._mtime = parentfiledata[2]
 
     @classmethod
@@ -125,11 +114,7 @@
 
         Should eventually be removed
         """
-        instance = cls()
-        instance._wc_tracked = True
-        instance._p1_tracked = False
-        instance._p2_tracked = False
-        return instance
+        return cls(wc_tracked=True)
 
     @classmethod
     def new_merged(cls):
@@ -137,12 +122,7 @@
 
         Should eventually be removed
         """
-        instance = cls()
-        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
+        return cls(wc_tracked=True, p1_tracked=True, p2_info=True)
 
     @classmethod
     def new_from_p2(cls):
@@ -150,12 +130,7 @@
 
         Should eventually be removed
         """
-        instance = cls()
-        instance._wc_tracked = True
-        instance._p1_tracked = False  # might actually be True
-        instance._p2_tracked = True
-        instance._clean_p2 = True
-        return instance
+        return cls(wc_tracked=True, p2_info=True)
 
     @classmethod
     def new_possibly_dirty(cls):
@@ -163,11 +138,7 @@
 
         Should eventually be removed
         """
-        instance = cls()
-        instance._wc_tracked = True
-        instance._p1_tracked = True
-        instance._possibly_dirty = True
-        return instance
+        return cls(wc_tracked=True, p1_tracked=True)
 
     @classmethod
     def new_normal(cls, mode, size, mtime):
@@ -177,13 +148,11 @@
         """
         assert size != FROM_P2
         assert size != NONNORMAL
-        instance = cls()
-        instance._wc_tracked = True
-        instance._p1_tracked = True
-        instance._mode = mode
-        instance._size = size
-        instance._mtime = mtime
-        return instance
+        return cls(
+            wc_tracked=True,
+            p1_tracked=True,
+            parentfiledata=(mode, size, mtime),
+        )
 
     @classmethod
     def from_v1_data(cls, state, mode, size, mtime):
@@ -197,25 +166,16 @@
         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 ?
-                )
+                p1_tracked = True
+                p2_info = True
             elif size == FROM_P2:
-                instance._clean_p2 = True
-                instance._p1_tracked = (
-                    False  # We actually don't know (file history)
-                )
-                instance._p2_tracked = True
+                p1_tracked = False
+                p2_info = True
             else:
-                instance._p1_tracked = True
-            return instance
+                p1_tracked = True
+                p2_info = False
+            return cls(p1_tracked=p1_tracked, p2_info=p2_info)
         elif state == b'n':
             if size == FROM_P2:
                 return cls.new_from_p2()
@@ -224,7 +184,6 @@
             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)
@@ -237,7 +196,7 @@
         This means the next status call will have to actually check its content
         to make sure it is correct.
         """
-        self._possibly_dirty = True
+        self._mtime = None
 
     def set_clean(self, mode, size, mtime):
         """mark a file as "clean" cancelling potential "possibly dirty call"
@@ -249,10 +208,6 @@
         """
         self._wc_tracked = True
         self._p1_tracked = True
-        self._p2_tracked = False  # this might be wrong
-        self._merged = False
-        self._clean_p2 = False
-        self._possibly_dirty = False
         self._mode = mode
         self._size = size
         self._mtime = mtime
@@ -263,11 +218,11 @@
         This will ultimately be called by command like `hg add`.
         """
         self._wc_tracked = True
-        # `set_tracked` is replacing various `normallookup` call. So we set
-        # "possibly dirty" to stay on the safe side.
+        # `set_tracked` is replacing various `normallookup` call. So we mark
+        # the files as needing lookup
         #
         # Consider dropping this in the future in favor of something less broad.
-        self._possibly_dirty = True
+        self._mtime = None
 
     def set_untracked(self):
         """mark a file as untracked in the working copy
@@ -284,18 +239,11 @@
 
         This is to be call by the dirstatemap code when the second parent is dropped
         """
-        if not (self.merged or self.from_p2):
-            return
-        self._p1_tracked = self.merged  # why is this not already properly set ?
-
-        self._merged = False
-        self._clean_p1 = False
-        self._clean_p2 = False
-        self._p2_tracked = False
-        self._possibly_dirty = True
-        self._mode = None
-        self._size = None
-        self._mtime = None
+        if self._p2_info:
+            self._p2_info = False
+            self._mode = None
+            self._size = None
+            self._mtime = None
 
     @property
     def mode(self):
@@ -334,23 +282,21 @@
     @property
     def any_tracked(self):
         """True is the file is tracked anywhere (wc or parents)"""
-        return self._wc_tracked or self._p1_tracked or self._p2_tracked
+        return self._wc_tracked or self._p1_tracked or self._p2_info
 
     @property
     def added(self):
         """True if the file has been added"""
-        return self._wc_tracked and not (self._p1_tracked or self._p2_tracked)
+        return self._wc_tracked and not (self._p1_tracked or self._p2_info)
 
     @property
     def maybe_clean(self):
         """True if the file has a chance to be in the "clean" state"""
         if not self._wc_tracked:
             return False
-        elif self.added:
+        elif not self._p1_tracked:
             return False
-        elif self._merged:
-            return False
-        elif self._clean_p2:
+        elif self._p2_info:
             return False
         return True
 
@@ -360,7 +306,7 @@
 
         Should only be set if a merge is in progress in the dirstate
         """
-        return self._wc_tracked and self._merged
+        return self._wc_tracked and self._p1_tracked and self._p2_info
 
     @property
     def from_p2(self):
@@ -370,18 +316,16 @@
 
         Should only be set if a merge is in progress in the dirstate
         """
-        if not self._wc_tracked:
-            return False
-        return self._clean_p2
+        return self._wc_tracked and (not self._p1_tracked) and self._p2_info
 
     @property
     def removed(self):
         """True if the file has been removed"""
-        return not self._wc_tracked and (self._p1_tracked or self._p2_tracked)
+        return not self._wc_tracked and (self._p1_tracked or self._p2_info)
 
     def v1_state(self):
         """return a "state" suitable for v1 serialization"""
-        if not (self._p1_tracked or self._p2_tracked or self._wc_tracked):
+        if not self.any_tracked:
             # the object has no state to record, this is -currently-
             # unsupported
             raise RuntimeError('untracked item')
@@ -404,9 +348,9 @@
             # the object has no state to record, this is -currently-
             # unsupported
             raise RuntimeError('untracked item')
-        elif self.removed and self._merged:
+        elif self.removed and self._p1_tracked and self._p2_info:
             return NONNORMAL
-        elif self.removed and self._clean_p2:
+        elif self.removed and self._p2_info:
             return FROM_P2
         elif self.removed:
             return 0
@@ -416,8 +360,8 @@
             return NONNORMAL
         elif self.from_p2:
             return FROM_P2
-        elif self._possibly_dirty:
-            return self._size if self._size is not None else NONNORMAL
+        elif self._size is None:
+            return NONNORMAL
         else:
             return self._size
 
@@ -429,16 +373,14 @@
             raise RuntimeError('untracked item')
         elif self.removed:
             return 0
-        elif self._possibly_dirty:
-            return AMBIGUOUS_TIME
-        elif self.merged:
+        elif self._mtime is None:
             return AMBIGUOUS_TIME
-        elif self.added:
+        elif self._p2_info:
             return AMBIGUOUS_TIME
-        elif self.from_p2:
+        elif not self._p1_tracked:
             return AMBIGUOUS_TIME
         else:
-            return self._mtime if self._mtime is not None else 0
+            return self._mtime
 
     def need_delay(self, now):
         """True if the stored mtime would be ambiguous with the current time"""