[Updated] D11686: dirstate: add a concept of "fallback" flags to dirstate item

marmoute (Pierre-Yves David) phabricator at mercurial-scm.org
Tue Oct 19 11:50:32 UTC 2021


marmoute updated this revision to Diff 30891.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D11686?vs=30885&id=30891

BRANCH
  default

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D11686/new/

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

AFFECTED FILES
  mercurial/cext/parsers.c
  mercurial/cext/util.h
  mercurial/dirstate.py
  mercurial/pure/parsers.py
  rust/hg-core/src/dirstate/entry.rs
  rust/hg-cpython/src/dirstate/item.rs

CHANGE DETAILS

diff --git a/rust/hg-cpython/src/dirstate/item.rs b/rust/hg-cpython/src/dirstate/item.rs
--- a/rust/hg-cpython/src/dirstate/item.rs
+++ b/rust/hg-cpython/src/dirstate/item.rs
@@ -1,4 +1,5 @@
 use cpython::exc;
+use cpython::ObjectProtocol;
 use cpython::PyBytes;
 use cpython::PyErr;
 use cpython::PyNone;
@@ -62,6 +63,70 @@
     }
 
     @property
+    def has_fallback_exec(&self) -> PyResult<bool> {
+        match self.entry(py).get().get_fallback_exec() {
+            Some(_) => Ok(true),
+            None => Ok(false),
+        }
+    }
+
+    @property
+    def fallback_exec(&self) -> PyResult<Option<bool>> {
+        match self.entry(py).get().get_fallback_exec() {
+            Some(exec) => Ok(Some(exec)),
+            None => Ok(None),
+        }
+    }
+
+    @fallback_exec.setter
+    def set_fallback_exec(&self, value: Option<PyObject>) -> PyResult<()> {
+        match value {
+            None => {self.entry(py).get().set_fallback_exec(None);},
+            Some(value) => {
+            if value.is_none(py) {
+                self.entry(py).get().set_fallback_exec(None);
+            } else {
+                self.entry(py).get().set_fallback_exec(
+                    Some(value.is_true(py)?)
+                );
+            }},
+        }
+        Ok(())
+    }
+
+    @property
+    def has_fallback_symlink(&self) -> PyResult<bool> {
+        match self.entry(py).get().get_fallback_symlink() {
+            Some(_) => Ok(true),
+            None => Ok(false),
+        }
+    }
+
+    @property
+    def fallback_symlink(&self) -> PyResult<Option<bool>> {
+        match self.entry(py).get().get_fallback_symlink() {
+            Some(symlink) => Ok(Some(symlink)),
+            None => Ok(None),
+        }
+    }
+
+    @fallback_symlink.setter
+    def set_fallback_symlink(&self, value: Option<PyObject>) -> PyResult<()> {
+        match value {
+            None => {self.entry(py).get().set_fallback_symlink(None);},
+            Some(value) => {
+            if value.is_none(py) {
+                self.entry(py).get().set_fallback_symlink(None);
+            } else {
+                self.entry(py).get().set_fallback_symlink(
+                    Some(value.is_true(py)?)
+                );
+            }},
+        }
+        Ok(())
+    }
+
+    @property
     def tracked(&self) -> PyResult<bool> {
         Ok(self.entry(py).get().tracked())
     }
diff --git a/rust/hg-core/src/dirstate/entry.rs b/rust/hg-core/src/dirstate/entry.rs
--- a/rust/hg-core/src/dirstate/entry.rs
+++ b/rust/hg-core/src/dirstate/entry.rs
@@ -29,6 +29,10 @@
         const WDIR_TRACKED = 1 << 0;
         const P1_TRACKED = 1 << 1;
         const P2_INFO = 1 << 2;
+        const HAS_FALLBACK_EXEC = 1 << 3;
+        const FALLBACK_EXEC = 1 << 4;
+        const HAS_FALLBACK_SYMLINK = 1 << 5;
+        const FALLBACK_SYMLINK = 1 << 6;
     }
 }
 
@@ -421,6 +425,52 @@
         self.v1_mtime()
     }
 
+    pub fn get_fallback_exec(&self) -> Option<bool> {
+        if self.flags.contains(Flags::HAS_FALLBACK_EXEC) {
+            Some(self.flags.contains(Flags::FALLBACK_EXEC))
+        } else {
+            None
+        }
+    }
+
+    pub fn set_fallback_exec(&mut self, value: Option<bool>) {
+        match value {
+            None => {
+                self.flags.remove(Flags::HAS_FALLBACK_EXEC);
+                self.flags.remove(Flags::FALLBACK_EXEC);
+            }
+            Some(exec) => {
+                self.flags.insert(Flags::HAS_FALLBACK_EXEC);
+                if exec {
+                    self.flags.insert(Flags::FALLBACK_EXEC);
+                }
+            }
+        }
+    }
+
+    pub fn get_fallback_symlink(&self) -> Option<bool> {
+        if self.flags.contains(Flags::HAS_FALLBACK_SYMLINK) {
+            Some(self.flags.contains(Flags::FALLBACK_SYMLINK))
+        } else {
+            None
+        }
+    }
+
+    pub fn set_fallback_symlink(&mut self, value: Option<bool>) {
+        match value {
+            None => {
+                self.flags.remove(Flags::HAS_FALLBACK_SYMLINK);
+                self.flags.remove(Flags::FALLBACK_SYMLINK);
+            }
+            Some(symlink) => {
+                self.flags.insert(Flags::HAS_FALLBACK_SYMLINK);
+                if symlink {
+                    self.flags.insert(Flags::FALLBACK_SYMLINK);
+                }
+            }
+        }
+    }
+
     pub fn drop_merge_data(&mut self) {
         if self.flags.contains(Flags::P2_INFO) {
             self.flags.remove(Flags::P2_INFO);
diff --git a/mercurial/pure/parsers.py b/mercurial/pure/parsers.py
--- a/mercurial/pure/parsers.py
+++ b/mercurial/pure/parsers.py
@@ -96,6 +96,8 @@
     _mode = attr.ib()
     _size = attr.ib()
     _mtime = attr.ib()
+    _fallback_exec = attr.ib()
+    _fallback_symlink = attr.ib()
 
     def __init__(
         self,
@@ -110,6 +112,9 @@
         self._p1_tracked = p1_tracked
         self._p2_info = p2_info
 
+        self._fallback_exec = None
+        self._fallback_symlink = None
+
         self._mode = None
         self._size = None
         self._mtime = None
@@ -282,6 +287,85 @@
         return self.v1_state()
 
     @property
+    def has_fallback_exec(self):
+        """True if "fallback" information are available for the "exec" bit
+
+        Fallback information can be stored in the dirstate to keep track of
+        filesystem attribute tracked by Mercurial when the underlying file
+        system or operating system does not support that property, (e.g.
+        Windows).
+
+        Not all version of the dirstate on-disk storage support preserving this
+        information.
+        """
+        return self._fallback_exec is not None
+
+    @property
+    def fallback_exec(self):
+        """ "fallback" information for the executable bit
+
+        True if the file should be considered executable when we cannot get
+        this information from the files system. False if it should be
+        considered non-executable.
+
+        See has_fallback_exec for details."""
+        return self._fallback_exec
+
+    @fallback_exec.setter
+    def set_fallback_exec(self, value):
+        """control "fallback" executable bit
+
+        Set to:
+        - True if the file should be considered executable,
+        - False if the file should be considered non-executable,
+        - None if we do not have valid fallback data.
+
+        See has_fallback_exec for details."""
+        if value is None:
+            self._fallback_exec = None
+        else:
+            self._fallback_exec = bool(value)
+
+    @property
+    def has_fallback_symlink(self):
+        """True if "fallback" information are available for symlink status
+
+        Fallback information can be stored in the dirstate to keep track of
+        filesystem attribute tracked by Mercurial when the underlying file
+        system or operating system does not support that property, (e.g.
+        Windows).
+
+        Not all version of the dirstate on-disk storage support preserving this
+        information."""
+        return self._fallback_symlink is not None
+
+    @property
+    def fallback_symlink(self):
+        """ "fallback" information for symlink status
+
+        True if the file should be considered executable when we cannot get
+        this information from the files system. False if it should be
+        considered non-executable.
+
+        See has_fallback_exec for details."""
+        return self._fallback_symlink
+
+    @fallback_symlink.setter
+    def set_fallback_symlink(self, value):
+        """control "fallback" symlink status
+
+        Set to:
+        - True if the file should be considered a symlink,
+        - False if the file should be considered not a symlink,
+        - None if we do not have valid fallback data.
+
+        See has_fallback_symlink for details."""
+        if value is None:
+            self._fallback_symlink = None
+        else:
+            self._fallback_symlink = bool(value)
+
+    @property
     def tracked(self):
         """True is the file is tracked in the working copy"""
         return self._wc_tracked
diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -259,7 +259,11 @@
             def f(x):
                 if os.path.islink(self._join(x)):
                     return b'l'
-                if b'x' in fallback(x):
+                entry = self.get_entry(x)
+                if entry.has_fallback_exec:
+                    if entry.fallback_exec:
+                        return b'x'
+                elif b'x' in fallback(x):
                     return b'x'
                 return b''
 
@@ -269,13 +273,28 @@
             def f(x):
                 if b'l' in fallback(x):
                     return b'l'
+                entry = self.get_entry(x)
+                if entry.has_fallback_symlink:
+                    if entry.fallback_symlink:
+                        return b'l'
                 if util.isexec(self._join(x)):
                     return b'x'
                 return b''
 
             return f
         else:
-            return fallback
+
+            def f(x):
+                entry = self.get_entry(x)
+                if entry.has_fallback_symlink:
+                    if entry.fallback_symlink:
+                        return b'l'
+                if entry.has_fallback_exec:
+                    if entry.fallback_exec:
+                        return b'x'
+                    elif entry.has_fallback_symlink:
+                        return b''
+                return fallback(x)
 
     @propertycache
     def _cwd(self):
diff --git a/mercurial/cext/util.h b/mercurial/cext/util.h
--- a/mercurial/cext/util.h
+++ b/mercurial/cext/util.h
@@ -42,6 +42,10 @@
 static const int dirstate_flag_expected_state_is_modified = 1 << 8;
 static const int dirstate_flag_all_unknown_recorded = 1 << 9;
 static const int dirstate_flag_all_ignored_recorded = 1 << 10;
+static const int dirstate_flag_fallback_exec = 1 << 11;
+static const int dirstate_flag_has_fallback_exec = 1 << 12;
+static const int dirstate_flag_fallback_symlink = 1 << 13;
+static const int dirstate_flag_has_fallback_symlink = 1 << 14;
 
 extern PyTypeObject dirstateItemType;
 #define dirstate_tuple_check(op) (Py_TYPE(op) == &dirstateItemType)
diff --git a/mercurial/cext/parsers.c b/mercurial/cext/parsers.c
--- a/mercurial/cext/parsers.c
+++ b/mercurial/cext/parsers.c
@@ -188,6 +188,17 @@
 	}
 }
 
+static inline bool dirstate_item_c_has_fallback_exec(dirstateItemObject *self)
+{
+	return (bool)self->flags & dirstate_flag_has_fallback_exec;
+}
+
+static inline bool
+dirstate_item_c_has_fallback_symlink(dirstateItemObject *self)
+{
+	return (bool)self->flags & dirstate_flag_has_fallback_symlink;
+}
+
 static inline int dirstate_item_c_v1_mode(dirstateItemObject *self)
 {
 	if (self->flags & dirstate_flag_has_meaningful_data) {
@@ -498,6 +509,83 @@
 	return PyBytes_FromStringAndSize(&state, 1);
 };
 
+static PyObject *dirstate_item_get_has_fallback_exec(dirstateItemObject *self)
+{
+	if (dirstate_item_c_has_fallback_exec(self)) {
+		Py_RETURN_TRUE;
+	} else {
+		Py_RETURN_FALSE;
+	}
+};
+
+static PyObject *dirstate_item_get_fallback_exec(dirstateItemObject *self)
+{
+	if (dirstate_item_c_has_fallback_exec(self)) {
+		if (self->flags & dirstate_flag_fallback_exec) {
+			Py_RETURN_TRUE;
+		} else {
+			Py_RETURN_FALSE;
+		}
+	} else {
+		Py_RETURN_NONE;
+	}
+};
+
+static int dirstate_item_set_fallback_exec(dirstateItemObject *self,
+                                           PyObject *value)
+{
+	if ((value == Py_None) || (value == NULL)) {
+		self->flags &= ~dirstate_flag_has_fallback_exec;
+	} else {
+		self->flags |= dirstate_flag_has_fallback_exec;
+		if (PyObject_IsTrue(value)) {
+			self->flags |= dirstate_flag_fallback_exec;
+		} else {
+			self->flags &= ~dirstate_flag_fallback_exec;
+		}
+	}
+	return 0;
+};
+
+static PyObject *
+dirstate_item_get_has_fallback_symlink(dirstateItemObject *self)
+{
+	if (dirstate_item_c_has_fallback_symlink(self)) {
+		Py_RETURN_TRUE;
+	} else {
+		Py_RETURN_FALSE;
+	}
+};
+
+static PyObject *dirstate_item_get_fallback_symlink(dirstateItemObject *self)
+{
+	if (dirstate_item_c_has_fallback_symlink(self)) {
+		if (self->flags & dirstate_flag_fallback_symlink) {
+			Py_RETURN_TRUE;
+		} else {
+			Py_RETURN_FALSE;
+		}
+	} else {
+		Py_RETURN_NONE;
+	}
+};
+
+static int dirstate_item_set_fallback_symlink(dirstateItemObject *self,
+                                              PyObject *value)
+{
+	if ((value == Py_None) || (value == NULL)) {
+		self->flags &= ~dirstate_flag_has_fallback_symlink;
+	} else {
+		self->flags |= dirstate_flag_has_fallback_symlink;
+		if (PyObject_IsTrue(value)) {
+			self->flags |= dirstate_flag_fallback_symlink;
+		} else {
+			self->flags &= ~dirstate_flag_fallback_symlink;
+		}
+	}
+	return 0;
+};
+
 static PyObject *dirstate_item_get_tracked(dirstateItemObject *self)
 {
 	if (dirstate_item_c_tracked(self)) {
@@ -588,6 +676,14 @@
     {"size", (getter)dirstate_item_get_size, NULL, "size", NULL},
     {"mtime", (getter)dirstate_item_get_mtime, NULL, "mtime", NULL},
     {"state", (getter)dirstate_item_get_state, NULL, "state", NULL},
+    {"has_fallback_exec", (getter)dirstate_item_get_has_fallback_exec, NULL,
+     "has_fallback_exec", NULL},
+    {"fallback_exec", (getter)dirstate_item_get_fallback_exec,
+     (setter)dirstate_item_set_fallback_exec, "fallback_exec", NULL},
+    {"has_fallback_symlink", (getter)dirstate_item_get_has_fallback_symlink,
+     NULL, "has_fallback_symlink", NULL},
+    {"fallback_symlink", (getter)dirstate_item_get_fallback_symlink,
+     (setter)dirstate_item_set_fallback_symlink, "fallback_symlink", NULL},
     {"tracked", (getter)dirstate_item_get_tracked, NULL, "tracked", NULL},
     {"p1_tracked", (getter)dirstate_item_get_p1_tracked, NULL, "p1_tracked",
      NULL},



To: marmoute, #hg-reviewers, Alphare
Cc: Alphare, mharbison72, mercurial-patches
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.mercurial-scm.org/pipermail/mercurial-patches/attachments/20211019/d277f7d4/attachment-0002.html>


More information about the Mercurial-patches mailing list