rust/hg-core/src/dirstate_tree/dirstate_map.rs
changeset 47348 a4de570e61fa
parent 47347 73ddcedeaadf
child 47349 7138c863d0a1
--- a/rust/hg-core/src/dirstate_tree/dirstate_map.rs	Fri May 28 20:07:27 2021 +0200
+++ b/rust/hg-core/src/dirstate_tree/dirstate_map.rs	Thu May 27 18:40:54 2021 +0200
@@ -295,18 +295,13 @@
         }
     }
 
-    pub(super) fn has_entry(&self) -> bool {
-        match self {
-            NodeRef::InMemory(_path, node) => node.entry.is_some(),
-            NodeRef::OnDisk(node) => node.has_entry(),
-        }
-    }
-
     pub(super) fn entry(
         &self,
     ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
         match self {
-            NodeRef::InMemory(_path, node) => Ok(node.entry),
+            NodeRef::InMemory(_path, node) => {
+                Ok(node.data.as_entry().copied())
+            }
             NodeRef::OnDisk(node) => node.entry(),
         }
     }
@@ -316,7 +311,7 @@
     ) -> Result<Option<EntryState>, DirstateV2ParseError> {
         match self {
             NodeRef::InMemory(_path, node) => {
-                Ok(node.entry.as_ref().map(|entry| entry.state))
+                Ok(node.data.as_entry().map(|entry| entry.state))
             }
             NodeRef::OnDisk(node) => node.state(),
         }
@@ -333,8 +328,7 @@
 /// Represents a file or a directory
 #[derive(Default)]
 pub(super) struct Node<'on_disk> {
-    /// `None` for directories
-    pub(super) entry: Option<DirstateEntry>,
+    pub(super) data: NodeData,
 
     pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
 
@@ -344,6 +338,34 @@
     pub(super) tracked_descendants_count: u32,
 }
 
+pub(super) enum NodeData {
+    Entry(DirstateEntry),
+    CachedDirectory { mtime: on_disk::Timestamp },
+    None,
+}
+
+impl Default for NodeData {
+    fn default() -> Self {
+        NodeData::None
+    }
+}
+
+impl NodeData {
+    fn has_entry(&self) -> bool {
+        match self {
+            NodeData::Entry(_) => true,
+            _ => false,
+        }
+    }
+
+    fn as_entry(&self) -> Option<&DirstateEntry> {
+        match self {
+            NodeData::Entry(entry) => Some(entry),
+            _ => None,
+        }
+    }
+}
+
 impl<'on_disk> DirstateMap<'on_disk> {
     pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
         Self {
@@ -386,14 +408,14 @@
                     },
                 )?;
                 assert!(
-                    node.entry.is_none(),
+                    !node.data.has_entry(),
                     "duplicate dirstate entry in read"
                 );
                 assert!(
                     node.copy_source.is_none(),
                     "duplicate dirstate entry in read"
                 );
-                node.entry = Some(*entry);
+                node.data = NodeData::Entry(*entry);
                 node.copy_source = copy_source.map(Cow::Borrowed);
                 map.nodes_with_entry_count += 1;
                 if copy_source.is_some() {
@@ -519,10 +541,10 @@
                 }
             },
         )?;
-        if node.entry.is_none() {
+        if !node.data.has_entry() {
             self.nodes_with_entry_count += 1
         }
-        node.entry = Some(new_entry);
+        node.data = NodeData::Entry(new_entry);
         Ok(())
     }
 
@@ -587,7 +609,7 @@
                 &mut self.root,
                 path.as_ref(),
             )? {
-                if let Some(entry) = node.entry.as_mut() {
+                if let NodeData::Entry(entry) = &mut node.data {
                     entry.clear_mtime();
                 }
             }
@@ -703,18 +725,22 @@
                     return Ok(None);
                 }
             } else {
+                let had_entry = node.data.has_entry();
+                if had_entry {
+                    node.data = NodeData::None
+                }
                 dropped = Dropped {
                     was_tracked: node
-                        .entry
-                        .as_ref()
+                        .data
+                        .as_entry()
                         .map_or(false, |entry| entry.state.is_tracked()),
-                    had_entry: node.entry.take().is_some(),
+                    had_entry,
                     had_copy_source: node.copy_source.take().is_some(),
                 };
             }
             // After recursion, for both leaf (rest_of_path is None) nodes and
             // parent nodes, remove a node if it just became empty.
-            if node.entry.is_none()
+            if !node.data.has_entry()
                 && node.copy_source.is_none()
                 && node.children.is_empty()
             {
@@ -746,7 +772,7 @@
             if let Some(node) =
                 Self::get_node_mut(self.on_disk, &mut self.root, &filename)?
             {
-                if let Some(entry) = node.entry.as_mut() {
+                if let NodeData::Entry(entry) = &mut node.data {
                     entry.clear_ambiguous_mtime(now);
                 }
             }
@@ -815,7 +841,8 @@
         if let Some(node) = self.get_node(directory)? {
             // A node without a `DirstateEntry` was created to hold child
             // nodes, and is therefore a directory.
-            Ok(!node.has_entry() && node.tracked_descendants_count() > 0)
+            let state = node.state()?;
+            Ok(state.is_none() && node.tracked_descendants_count() > 0)
         } else {
             Ok(false)
         }
@@ -825,7 +852,7 @@
         if let Some(node) = self.get_node(directory)? {
             // A node without a `DirstateEntry` was created to hold child
             // nodes, and is therefore a directory.
-            Ok(!node.has_entry())
+            Ok(node.state()?.is_none())
         } else {
             Ok(false)
         }