rust/hg-core/src/dirstate_tree/dirstate_map.rs
branchstable
changeset 49366 288de6f5d724
parent 49147 10b9f11daf15
parent 49337 6cd249556e20
child 49373 f8ec7b16c98f
equal deleted inserted replaced
49364:e8ea403b1c46 49366:288de6f5d724
     9 use super::path_with_basename::WithBasename;
     9 use super::path_with_basename::WithBasename;
    10 use crate::dirstate::parsers::pack_entry;
    10 use crate::dirstate::parsers::pack_entry;
    11 use crate::dirstate::parsers::packed_entry_size;
    11 use crate::dirstate::parsers::packed_entry_size;
    12 use crate::dirstate::parsers::parse_dirstate_entries;
    12 use crate::dirstate::parsers::parse_dirstate_entries;
    13 use crate::dirstate::CopyMapIter;
    13 use crate::dirstate::CopyMapIter;
       
    14 use crate::dirstate::DirstateV2Data;
       
    15 use crate::dirstate::ParentFileData;
    14 use crate::dirstate::StateMapIter;
    16 use crate::dirstate::StateMapIter;
    15 use crate::dirstate::TruncatedTimestamp;
    17 use crate::dirstate::TruncatedTimestamp;
    16 use crate::dirstate::SIZE_FROM_OTHER_PARENT;
       
    17 use crate::dirstate::SIZE_NON_NORMAL;
       
    18 use crate::matchers::Matcher;
    18 use crate::matchers::Matcher;
    19 use crate::utils::hg_path::{HgPath, HgPathBuf};
    19 use crate::utils::hg_path::{HgPath, HgPathBuf};
    20 use crate::DirstateEntry;
    20 use crate::DirstateEntry;
    21 use crate::DirstateError;
    21 use crate::DirstateError;
       
    22 use crate::DirstateMapError;
    22 use crate::DirstateParents;
    23 use crate::DirstateParents;
    23 use crate::DirstateStatus;
    24 use crate::DirstateStatus;
    24 use crate::EntryState;
    25 use crate::FastHashbrownMap as FastHashMap;
    25 use crate::FastHashMap;
       
    26 use crate::PatternFileWarning;
    26 use crate::PatternFileWarning;
    27 use crate::StatusError;
    27 use crate::StatusError;
    28 use crate::StatusOptions;
    28 use crate::StatusOptions;
    29 
    29 
    30 /// Append to an existing data file if the amount of unreachable data (not used
    30 /// Append to an existing data file if the amount of unreachable data (not used
    36 pub enum DirstateVersion {
    36 pub enum DirstateVersion {
    37     V1,
    37     V1,
    38     V2,
    38     V2,
    39 }
    39 }
    40 
    40 
       
    41 #[derive(Debug)]
    41 pub struct DirstateMap<'on_disk> {
    42 pub struct DirstateMap<'on_disk> {
    42     /// Contents of the `.hg/dirstate` file
    43     /// Contents of the `.hg/dirstate` file
    43     pub(super) on_disk: &'on_disk [u8],
    44     pub(super) on_disk: &'on_disk [u8],
    44 
    45 
    45     pub(super) root: ChildNodes<'on_disk>,
    46     pub(super) root: ChildNodes<'on_disk>,
    71 /// string prefix.
    72 /// string prefix.
    72 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
    73 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
    73 
    74 
    74 /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned
    75 /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned
    75 /// for on-disk nodes that don’t actually have a `Cow` to borrow.
    76 /// for on-disk nodes that don’t actually have a `Cow` to borrow.
       
    77 #[derive(Debug)]
    76 pub(super) enum BorrowedPath<'tree, 'on_disk> {
    78 pub(super) enum BorrowedPath<'tree, 'on_disk> {
    77     InMemory(&'tree HgPathBuf),
    79     InMemory(&'tree HgPathBuf),
    78     OnDisk(&'on_disk HgPath),
    80     OnDisk(&'on_disk HgPath),
    79 }
    81 }
    80 
    82 
       
    83 #[derive(Debug)]
    81 pub(super) enum ChildNodes<'on_disk> {
    84 pub(super) enum ChildNodes<'on_disk> {
    82     InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
    85     InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
    83     OnDisk(&'on_disk [on_disk::Node]),
    86     OnDisk(&'on_disk [on_disk::Node]),
    84 }
    87 }
    85 
    88 
       
    89 #[derive(Debug)]
    86 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
    90 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
    87     InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
    91     InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
    88     OnDisk(&'on_disk [on_disk::Node]),
    92     OnDisk(&'on_disk [on_disk::Node]),
    89 }
    93 }
    90 
    94 
       
    95 #[derive(Debug)]
    91 pub(super) enum NodeRef<'tree, 'on_disk> {
    96 pub(super) enum NodeRef<'tree, 'on_disk> {
    92     InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
    97     InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
    93     OnDisk(&'on_disk on_disk::Node),
    98     OnDisk(&'on_disk on_disk::Node),
    94 }
    99 }
    95 
   100 
   351             }
   356             }
   352             NodeRef::OnDisk(node) => node.entry(),
   357             NodeRef::OnDisk(node) => node.entry(),
   353         }
   358         }
   354     }
   359     }
   355 
   360 
   356     pub(super) fn state(
       
   357         &self,
       
   358     ) -> Result<Option<EntryState>, DirstateV2ParseError> {
       
   359         Ok(self.entry()?.map(|e| e.state()))
       
   360     }
       
   361 
       
   362     pub(super) fn cached_directory_mtime(
   361     pub(super) fn cached_directory_mtime(
   363         &self,
   362         &self,
   364     ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
   363     ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
   365         match self {
   364         match self {
   366             NodeRef::InMemory(_path, node) => Ok(match node.data {
   365             NodeRef::InMemory(_path, node) => Ok(match node.data {
   387         }
   386         }
   388     }
   387     }
   389 }
   388 }
   390 
   389 
   391 /// Represents a file or a directory
   390 /// Represents a file or a directory
   392 #[derive(Default)]
   391 #[derive(Default, Debug)]
   393 pub(super) struct Node<'on_disk> {
   392 pub(super) struct Node<'on_disk> {
   394     pub(super) data: NodeData,
   393     pub(super) data: NodeData,
   395 
   394 
   396     pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
   395     pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
   397 
   396 
   403     /// How many (non-inclusive) descendants of this node have an entry whose
   402     /// How many (non-inclusive) descendants of this node have an entry whose
   404     /// state is "tracked".
   403     /// state is "tracked".
   405     pub(super) tracked_descendants_count: u32,
   404     pub(super) tracked_descendants_count: u32,
   406 }
   405 }
   407 
   406 
       
   407 #[derive(Debug)]
   408 pub(super) enum NodeData {
   408 pub(super) enum NodeData {
   409     Entry(DirstateEntry),
   409     Entry(DirstateEntry),
   410     CachedDirectory { mtime: TruncatedTimestamp },
   410     CachedDirectory { mtime: TruncatedTimestamp },
   411     None,
   411     None,
   412 }
   412 }
   424             _ => false,
   424             _ => false,
   425         }
   425         }
   426     }
   426     }
   427 
   427 
   428     fn as_entry(&self) -> Option<&DirstateEntry> {
   428     fn as_entry(&self) -> Option<&DirstateEntry> {
       
   429         match self {
       
   430             NodeData::Entry(entry) => Some(entry),
       
   431             _ => None,
       
   432         }
       
   433     }
       
   434 
       
   435     fn as_entry_mut(&mut self) -> Option<&mut DirstateEntry> {
   429         match self {
   436         match self {
   430             NodeData::Entry(entry) => Some(entry),
   437             NodeData::Entry(entry) => Some(entry),
   431             _ => None,
   438             _ => None,
   432         }
   439         }
   433     }
   440     }
   470         }
   477         }
   471 
   478 
   472         let parents = parse_dirstate_entries(
   479         let parents = parse_dirstate_entries(
   473             map.on_disk,
   480             map.on_disk,
   474             |path, entry, copy_source| {
   481             |path, entry, copy_source| {
   475                 let tracked = entry.state().is_tracked();
   482                 let tracked = entry.tracked();
   476                 let node = Self::get_or_insert_node(
   483                 let node = Self::get_or_insert_node_inner(
   477                     map.on_disk,
   484                     map.on_disk,
   478                     &mut map.unreachable_bytes,
   485                     &mut map.unreachable_bytes,
   479                     &mut map.root,
   486                     &mut map.root,
   480                     path,
   487                     path,
   481                     WithBasename::to_cow_borrowed,
   488                     WithBasename::to_cow_borrowed,
   538         }
   545         }
   539     }
   546     }
   540 
   547 
   541     /// Returns a mutable reference to the node at `path` if it exists
   548     /// Returns a mutable reference to the node at `path` if it exists
   542     ///
   549     ///
       
   550     /// `each_ancestor` is a callback that is called for each ancestor node
       
   551     /// when descending the tree. It is used to keep the different counters
       
   552     /// of the `DirstateMap` up-to-date.
       
   553     fn get_node_mut<'tree>(
       
   554         &'tree mut self,
       
   555         path: &HgPath,
       
   556         each_ancestor: impl FnMut(&mut Node),
       
   557     ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
       
   558         Self::get_node_mut_inner(
       
   559             self.on_disk,
       
   560             &mut self.unreachable_bytes,
       
   561             &mut self.root,
       
   562             path,
       
   563             each_ancestor,
       
   564         )
       
   565     }
       
   566 
       
   567     /// Lower-level version of `get_node_mut`.
       
   568     ///
   543     /// This takes `root` instead of `&mut self` so that callers can mutate
   569     /// This takes `root` instead of `&mut self` so that callers can mutate
   544     /// other fields while the returned borrow is still valid
   570     /// other fields while the returned borrow is still valid.
   545     fn get_node_mut<'tree>(
   571     ///
       
   572     /// `each_ancestor` is a callback that is called for each ancestor node
       
   573     /// when descending the tree. It is used to keep the different counters
       
   574     /// of the `DirstateMap` up-to-date.
       
   575     fn get_node_mut_inner<'tree>(
   546         on_disk: &'on_disk [u8],
   576         on_disk: &'on_disk [u8],
   547         unreachable_bytes: &mut u32,
   577         unreachable_bytes: &mut u32,
   548         root: &'tree mut ChildNodes<'on_disk>,
   578         root: &'tree mut ChildNodes<'on_disk>,
   549         path: &HgPath,
   579         path: &HgPath,
       
   580         mut each_ancestor: impl FnMut(&mut Node),
   550     ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
   581     ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
   551         let mut children = root;
   582         let mut children = root;
   552         let mut components = path.components();
   583         let mut components = path.components();
   553         let mut component =
   584         let mut component =
   554             components.next().expect("expected at least one components");
   585             components.next().expect("expected at least one components");
   556             if let Some(child) = children
   587             if let Some(child) = children
   557                 .make_mut(on_disk, unreachable_bytes)?
   588                 .make_mut(on_disk, unreachable_bytes)?
   558                 .get_mut(component)
   589                 .get_mut(component)
   559             {
   590             {
   560                 if let Some(next_component) = components.next() {
   591                 if let Some(next_component) = components.next() {
       
   592                     each_ancestor(child);
   561                     component = next_component;
   593                     component = next_component;
   562                     children = &mut child.children;
   594                     children = &mut child.children;
   563                 } else {
   595                 } else {
   564                     return Ok(Some(child));
   596                     return Ok(Some(child));
   565                 }
   597                 }
   567                 return Ok(None);
   599                 return Ok(None);
   568             }
   600             }
   569         }
   601         }
   570     }
   602     }
   571 
   603 
   572     pub(super) fn get_or_insert<'tree, 'path>(
   604     /// Get a mutable reference to the node at `path`, creating it if it does
       
   605     /// not exist.
       
   606     ///
       
   607     /// `each_ancestor` is a callback that is called for each ancestor node
       
   608     /// when descending the tree. It is used to keep the different counters
       
   609     /// of the `DirstateMap` up-to-date.
       
   610     fn get_or_insert_node<'tree, 'path>(
   573         &'tree mut self,
   611         &'tree mut self,
   574         path: &HgPath,
   612         path: &'path HgPath,
       
   613         each_ancestor: impl FnMut(&mut Node),
   575     ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
   614     ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
   576         Self::get_or_insert_node(
   615         Self::get_or_insert_node_inner(
   577             self.on_disk,
   616             self.on_disk,
   578             &mut self.unreachable_bytes,
   617             &mut self.unreachable_bytes,
   579             &mut self.root,
   618             &mut self.root,
   580             path,
   619             path,
   581             WithBasename::to_cow_owned,
   620             WithBasename::to_cow_owned,
   582             |_| {},
   621             each_ancestor,
   583         )
   622         )
   584     }
   623     }
   585 
   624 
   586     fn get_or_insert_node<'tree, 'path>(
   625     /// Lower-level version of `get_or_insert_node_inner`, which is used when
       
   626     /// parsing disk data to remove allocations for new nodes.
       
   627     fn get_or_insert_node_inner<'tree, 'path>(
   587         on_disk: &'on_disk [u8],
   628         on_disk: &'on_disk [u8],
   588         unreachable_bytes: &mut u32,
   629         unreachable_bytes: &mut u32,
   589         root: &'tree mut ChildNodes<'on_disk>,
   630         root: &'tree mut ChildNodes<'on_disk>,
   590         path: &'path HgPath,
   631         path: &'path HgPath,
   591         to_cow: impl Fn(
   632         to_cow: impl Fn(
   598             WithBasename::inclusive_ancestors_of(path);
   639             WithBasename::inclusive_ancestors_of(path);
   599         let mut ancestor_path = inclusive_ancestor_paths
   640         let mut ancestor_path = inclusive_ancestor_paths
   600             .next()
   641             .next()
   601             .expect("expected at least one inclusive ancestor");
   642             .expect("expected at least one inclusive ancestor");
   602         loop {
   643         loop {
   603             // TODO: can we avoid allocating an owned key in cases where the
   644             let (_, child_node) = child_nodes
   604             // map already contains that key, without introducing double
       
   605             // lookup?
       
   606             let child_node = child_nodes
       
   607                 .make_mut(on_disk, unreachable_bytes)?
   645                 .make_mut(on_disk, unreachable_bytes)?
   608                 .entry(to_cow(ancestor_path))
   646                 .raw_entry_mut()
   609                 .or_default();
   647                 .from_key(ancestor_path.base_name())
       
   648                 .or_insert_with(|| (to_cow(ancestor_path), Node::default()));
   610             if let Some(next) = inclusive_ancestor_paths.next() {
   649             if let Some(next) = inclusive_ancestor_paths.next() {
   611                 each_ancestor(child_node);
   650                 each_ancestor(child_node);
   612                 ancestor_path = next;
   651                 ancestor_path = next;
   613                 child_nodes = &mut child_node.children;
   652                 child_nodes = &mut child_node.children;
   614             } else {
   653             } else {
   615                 return Ok(child_node);
   654                 return Ok(child_node);
   616             }
   655             }
   617         }
   656         }
   618     }
   657     }
   619 
   658 
   620     fn add_or_remove_file(
   659     fn reset_state(
       
   660         &mut self,
       
   661         filename: &HgPath,
       
   662         old_entry_opt: Option<DirstateEntry>,
       
   663         wc_tracked: bool,
       
   664         p1_tracked: bool,
       
   665         p2_info: bool,
       
   666         has_meaningful_mtime: bool,
       
   667         parent_file_data_opt: Option<ParentFileData>,
       
   668     ) -> Result<(), DirstateError> {
       
   669         let (had_entry, was_tracked) = match old_entry_opt {
       
   670             Some(old_entry) => (true, old_entry.tracked()),
       
   671             None => (false, false),
       
   672         };
       
   673         let node = self.get_or_insert_node(filename, |ancestor| {
       
   674             if !had_entry {
       
   675                 ancestor.descendants_with_entry_count += 1;
       
   676             }
       
   677             if was_tracked {
       
   678                 if !wc_tracked {
       
   679                     ancestor.tracked_descendants_count = ancestor
       
   680                         .tracked_descendants_count
       
   681                         .checked_sub(1)
       
   682                         .expect("tracked count to be >= 0");
       
   683                 }
       
   684             } else {
       
   685                 if wc_tracked {
       
   686                     ancestor.tracked_descendants_count += 1;
       
   687                 }
       
   688             }
       
   689         })?;
       
   690 
       
   691         let v2_data = if let Some(parent_file_data) = parent_file_data_opt {
       
   692             DirstateV2Data {
       
   693                 wc_tracked,
       
   694                 p1_tracked,
       
   695                 p2_info,
       
   696                 mode_size: parent_file_data.mode_size,
       
   697                 mtime: if has_meaningful_mtime {
       
   698                     parent_file_data.mtime
       
   699                 } else {
       
   700                     None
       
   701                 },
       
   702                 ..Default::default()
       
   703             }
       
   704         } else {
       
   705             DirstateV2Data {
       
   706                 wc_tracked,
       
   707                 p1_tracked,
       
   708                 p2_info,
       
   709                 ..Default::default()
       
   710             }
       
   711         };
       
   712         node.data = NodeData::Entry(DirstateEntry::from_v2_data(v2_data));
       
   713         if !had_entry {
       
   714             self.nodes_with_entry_count += 1;
       
   715         }
       
   716         Ok(())
       
   717     }
       
   718 
       
   719     fn set_tracked(
       
   720         &mut self,
       
   721         filename: &HgPath,
       
   722         old_entry_opt: Option<DirstateEntry>,
       
   723     ) -> Result<bool, DirstateV2ParseError> {
       
   724         let was_tracked = old_entry_opt.map_or(false, |e| e.tracked());
       
   725         let had_entry = old_entry_opt.is_some();
       
   726         let tracked_count_increment = if was_tracked { 0 } else { 1 };
       
   727         let mut new = false;
       
   728 
       
   729         let node = self.get_or_insert_node(filename, |ancestor| {
       
   730             if !had_entry {
       
   731                 ancestor.descendants_with_entry_count += 1;
       
   732             }
       
   733 
       
   734             ancestor.tracked_descendants_count += tracked_count_increment;
       
   735         })?;
       
   736         if let Some(old_entry) = old_entry_opt {
       
   737             let mut e = old_entry.clone();
       
   738             if e.tracked() {
       
   739                 // XXX
       
   740                 // This is probably overkill for more case, but we need this to
       
   741                 // fully replace the `normallookup` call with `set_tracked`
       
   742                 // one. Consider smoothing this in the future.
       
   743                 e.set_possibly_dirty();
       
   744             } else {
       
   745                 new = true;
       
   746                 e.set_tracked();
       
   747             }
       
   748             node.data = NodeData::Entry(e)
       
   749         } else {
       
   750             node.data = NodeData::Entry(DirstateEntry::new_tracked());
       
   751             self.nodes_with_entry_count += 1;
       
   752             new = true;
       
   753         };
       
   754         Ok(new)
       
   755     }
       
   756 
       
   757     /// Set a node as untracked in the dirstate.
       
   758     ///
       
   759     /// It is the responsibility of the caller to remove the copy source and/or
       
   760     /// the entry itself if appropriate.
       
   761     ///
       
   762     /// # Panics
       
   763     ///
       
   764     /// Panics if the node does not exist.
       
   765     fn set_untracked(
       
   766         &mut self,
       
   767         filename: &HgPath,
       
   768         old_entry: DirstateEntry,
       
   769     ) -> Result<(), DirstateV2ParseError> {
       
   770         let node = self
       
   771             .get_node_mut(filename, |ancestor| {
       
   772                 ancestor.tracked_descendants_count = ancestor
       
   773                     .tracked_descendants_count
       
   774                     .checked_sub(1)
       
   775                     .expect("tracked_descendants_count should be >= 0");
       
   776             })?
       
   777             .expect("node should exist");
       
   778         let mut new_entry = old_entry.clone();
       
   779         new_entry.set_untracked();
       
   780         node.data = NodeData::Entry(new_entry);
       
   781         Ok(())
       
   782     }
       
   783 
       
   784     /// Set a node as clean in the dirstate.
       
   785     ///
       
   786     /// It is the responsibility of the caller to remove the copy source.
       
   787     ///
       
   788     /// # Panics
       
   789     ///
       
   790     /// Panics if the node does not exist.
       
   791     fn set_clean(
       
   792         &mut self,
       
   793         filename: &HgPath,
       
   794         old_entry: DirstateEntry,
       
   795         mode: u32,
       
   796         size: u32,
       
   797         mtime: TruncatedTimestamp,
       
   798     ) -> Result<(), DirstateError> {
       
   799         let node = self
       
   800             .get_node_mut(filename, |ancestor| {
       
   801                 if !old_entry.tracked() {
       
   802                     ancestor.tracked_descendants_count += 1;
       
   803                 }
       
   804             })?
       
   805             .expect("node should exist");
       
   806         let mut new_entry = old_entry.clone();
       
   807         new_entry.set_clean(mode, size, mtime);
       
   808         node.data = NodeData::Entry(new_entry);
       
   809         Ok(())
       
   810     }
       
   811 
       
   812     /// Set a node as possibly dirty in the dirstate.
       
   813     ///
       
   814     /// # Panics
       
   815     ///
       
   816     /// Panics if the node does not exist.
       
   817     fn set_possibly_dirty(
       
   818         &mut self,
       
   819         filename: &HgPath,
       
   820     ) -> Result<(), DirstateError> {
       
   821         let node = self
       
   822             .get_node_mut(filename, |_ancestor| {})?
       
   823             .expect("node should exist");
       
   824         let entry = node.data.as_entry_mut().expect("entry should exist");
       
   825         entry.set_possibly_dirty();
       
   826         node.data = NodeData::Entry(*entry);
       
   827         Ok(())
       
   828     }
       
   829 
       
   830     /// Clears the cached mtime for the (potential) folder at `path`.
       
   831     pub(super) fn clear_cached_mtime(
   621         &mut self,
   832         &mut self,
   622         path: &HgPath,
   833         path: &HgPath,
   623         old_state: Option<EntryState>,
       
   624         new_entry: DirstateEntry,
       
   625     ) -> Result<(), DirstateV2ParseError> {
   834     ) -> Result<(), DirstateV2ParseError> {
   626         let had_entry = old_state.is_some();
   835         let node = match self.get_node_mut(path, |_ancestor| {})? {
   627         let was_tracked = old_state.map_or(false, |s| s.is_tracked());
   836             Some(node) => node,
   628         let tracked_count_increment =
   837             None => return Ok(()),
   629             match (was_tracked, new_entry.state().is_tracked()) {
   838         };
   630                 (false, true) => 1,
   839         if let NodeData::CachedDirectory { .. } = &node.data {
   631                 (true, false) => -1,
   840             node.data = NodeData::None
   632                 _ => 0,
   841         }
   633             };
   842         Ok(())
   634 
   843     }
   635         let node = Self::get_or_insert_node(
   844 
   636             self.on_disk,
   845     /// Sets the cached mtime for the (potential) folder at `path`.
   637             &mut self.unreachable_bytes,
   846     pub(super) fn set_cached_mtime(
   638             &mut self.root,
   847         &mut self,
   639             path,
   848         path: &HgPath,
   640             WithBasename::to_cow_owned,
   849         mtime: TruncatedTimestamp,
   641             |ancestor| {
   850     ) -> Result<(), DirstateV2ParseError> {
   642                 if !had_entry {
   851         let node = match self.get_node_mut(path, |_ancestor| {})? {
   643                     ancestor.descendants_with_entry_count += 1;
   852             Some(node) => node,
   644                 }
   853             None => return Ok(()),
   645 
   854         };
   646                 // We can’t use `+= increment` because the counter is unsigned,
   855         match &node.data {
   647                 // and we want debug builds to detect accidental underflow
   856             NodeData::Entry(_) => {} // Don’t overwrite an entry
   648                 // through zero
   857             NodeData::CachedDirectory { .. } | NodeData::None => {
   649                 match tracked_count_increment {
   858                 node.data = NodeData::CachedDirectory { mtime }
   650                     1 => ancestor.tracked_descendants_count += 1,
   859             }
   651                     -1 => ancestor.tracked_descendants_count -= 1,
   860         }
   652                     _ => {}
       
   653                 }
       
   654             },
       
   655         )?;
       
   656         if !had_entry {
       
   657             self.nodes_with_entry_count += 1
       
   658         }
       
   659         node.data = NodeData::Entry(new_entry);
       
   660         Ok(())
   861         Ok(())
   661     }
   862     }
   662 
   863 
   663     fn iter_nodes<'tree>(
   864     fn iter_nodes<'tree>(
   664         &'tree self,
   865         &'tree self,
   745             map.nodes_with_entry_count = 0;
   946             map.nodes_with_entry_count = 0;
   746             map.nodes_with_copy_source_count = 0;
   947             map.nodes_with_copy_source_count = 0;
   747         });
   948         });
   748     }
   949     }
   749 
   950 
   750     pub fn set_entry(
   951     pub fn set_tracked(
   751         &mut self,
   952         &mut self,
   752         filename: &HgPath,
   953         filename: &HgPath,
   753         entry: DirstateEntry,
   954     ) -> Result<bool, DirstateV2ParseError> {
   754     ) -> Result<(), DirstateV2ParseError> {
   955         let old_entry_opt = self.get(filename)?;
       
   956         self.with_dmap_mut(|map| map.set_tracked(filename, old_entry_opt))
       
   957     }
       
   958 
       
   959     pub fn set_untracked(
       
   960         &mut self,
       
   961         filename: &HgPath,
       
   962     ) -> Result<bool, DirstateError> {
       
   963         let old_entry_opt = self.get(filename)?;
       
   964         match old_entry_opt {
       
   965             None => Ok(false),
       
   966             Some(old_entry) => {
       
   967                 if !old_entry.tracked() {
       
   968                     // `DirstateMap::set_untracked` is not a noop if
       
   969                     // already not tracked as it will decrement the
       
   970                     // tracked counters while going down.
       
   971                     return Ok(true);
       
   972                 }
       
   973                 if old_entry.added() {
       
   974                     // Untracking an "added" entry will just result in a
       
   975                     // worthless entry (and other parts of the code will
       
   976                     // complain about it), just drop it entirely.
       
   977                     self.drop_entry_and_copy_source(filename)?;
       
   978                     return Ok(true);
       
   979                 }
       
   980                 if !old_entry.p2_info() {
       
   981                     self.copy_map_remove(filename)?;
       
   982                 }
       
   983 
       
   984                 self.with_dmap_mut(|map| {
       
   985                     map.set_untracked(filename, old_entry)?;
       
   986                     Ok(true)
       
   987                 })
       
   988             }
       
   989         }
       
   990     }
       
   991 
       
   992     pub fn set_clean(
       
   993         &mut self,
       
   994         filename: &HgPath,
       
   995         mode: u32,
       
   996         size: u32,
       
   997         mtime: TruncatedTimestamp,
       
   998     ) -> Result<(), DirstateError> {
       
   999         let old_entry = match self.get(filename)? {
       
  1000             None => {
       
  1001                 return Err(
       
  1002                     DirstateMapError::PathNotFound(filename.into()).into()
       
  1003                 )
       
  1004             }
       
  1005             Some(e) => e,
       
  1006         };
       
  1007         self.copy_map_remove(filename)?;
   755         self.with_dmap_mut(|map| {
  1008         self.with_dmap_mut(|map| {
   756             map.get_or_insert(&filename)?.data = NodeData::Entry(entry);
  1009             map.set_clean(filename, old_entry, mode, size, mtime)
   757             Ok(())
       
   758         })
  1010         })
   759     }
  1011     }
   760 
  1012 
   761     pub fn add_file(
  1013     pub fn set_possibly_dirty(
   762         &mut self,
       
   763         filename: &HgPath,
       
   764         entry: DirstateEntry,
       
   765     ) -> Result<(), DirstateError> {
       
   766         let old_state = self.get(filename)?.map(|e| e.state());
       
   767         self.with_dmap_mut(|map| {
       
   768             Ok(map.add_or_remove_file(filename, old_state, entry)?)
       
   769         })
       
   770     }
       
   771 
       
   772     pub fn remove_file(
       
   773         &mut self,
       
   774         filename: &HgPath,
       
   775         in_merge: bool,
       
   776     ) -> Result<(), DirstateError> {
       
   777         let old_entry_opt = self.get(filename)?;
       
   778         let old_state = old_entry_opt.map(|e| e.state());
       
   779         let mut size = 0;
       
   780         if in_merge {
       
   781             // XXX we should not be able to have 'm' state and 'FROM_P2' if not
       
   782             // during a merge. So I (marmoute) am not sure we need the
       
   783             // conditionnal at all. Adding double checking this with assert
       
   784             // would be nice.
       
   785             if let Some(old_entry) = old_entry_opt {
       
   786                 // backup the previous state
       
   787                 if old_entry.state() == EntryState::Merged {
       
   788                     size = SIZE_NON_NORMAL;
       
   789                 } else if old_entry.state() == EntryState::Normal
       
   790                     && old_entry.size() == SIZE_FROM_OTHER_PARENT
       
   791                 {
       
   792                     // other parent
       
   793                     size = SIZE_FROM_OTHER_PARENT;
       
   794                 }
       
   795             }
       
   796         }
       
   797         if size == 0 {
       
   798             self.copy_map_remove(filename)?;
       
   799         }
       
   800         self.with_dmap_mut(|map| {
       
   801             let entry = DirstateEntry::new_removed(size);
       
   802             Ok(map.add_or_remove_file(filename, old_state, entry)?)
       
   803         })
       
   804     }
       
   805 
       
   806     pub fn drop_entry_and_copy_source(
       
   807         &mut self,
  1014         &mut self,
   808         filename: &HgPath,
  1015         filename: &HgPath,
   809     ) -> Result<(), DirstateError> {
  1016     ) -> Result<(), DirstateError> {
   810         let was_tracked = self
  1017         if self.get(filename)?.is_none() {
   811             .get(filename)?
  1018             return Err(DirstateMapError::PathNotFound(filename.into()).into());
   812             .map_or(false, |e| e.state().is_tracked());
  1019         }
       
  1020         self.with_dmap_mut(|map| map.set_possibly_dirty(filename))
       
  1021     }
       
  1022 
       
  1023     pub fn reset_state(
       
  1024         &mut self,
       
  1025         filename: &HgPath,
       
  1026         wc_tracked: bool,
       
  1027         p1_tracked: bool,
       
  1028         p2_info: bool,
       
  1029         has_meaningful_mtime: bool,
       
  1030         parent_file_data_opt: Option<ParentFileData>,
       
  1031     ) -> Result<(), DirstateError> {
       
  1032         if !(p1_tracked || p2_info || wc_tracked) {
       
  1033             self.drop_entry_and_copy_source(filename)?;
       
  1034             return Ok(());
       
  1035         }
       
  1036         self.copy_map_remove(filename)?;
       
  1037         let old_entry_opt = self.get(filename)?;
       
  1038         self.with_dmap_mut(|map| {
       
  1039             map.reset_state(
       
  1040                 filename,
       
  1041                 old_entry_opt,
       
  1042                 wc_tracked,
       
  1043                 p1_tracked,
       
  1044                 p2_info,
       
  1045                 has_meaningful_mtime,
       
  1046                 parent_file_data_opt,
       
  1047             )
       
  1048         })
       
  1049     }
       
  1050 
       
  1051     pub fn drop_entry_and_copy_source(
       
  1052         &mut self,
       
  1053         filename: &HgPath,
       
  1054     ) -> Result<(), DirstateError> {
       
  1055         let was_tracked = self.get(filename)?.map_or(false, |e| e.tracked());
   813         struct Dropped {
  1056         struct Dropped {
   814             was_tracked: bool,
  1057             was_tracked: bool,
   815             had_entry: bool,
  1058             had_entry: bool,
   816             had_copy_source: bool,
  1059             had_copy_source: bool,
   817         }
  1060         }
   939     ) -> Result<bool, DirstateError> {
  1182     ) -> Result<bool, DirstateError> {
   940         self.with_dmap_mut(|map| {
  1183         self.with_dmap_mut(|map| {
   941             if let Some(node) = map.get_node(directory)? {
  1184             if let Some(node) = map.get_node(directory)? {
   942                 // A node without a `DirstateEntry` was created to hold child
  1185                 // A node without a `DirstateEntry` was created to hold child
   943                 // nodes, and is therefore a directory.
  1186                 // nodes, and is therefore a directory.
   944                 let state = node.state()?;
  1187                 let is_dir = node.entry()?.is_none();
   945                 Ok(state.is_none() && node.tracked_descendants_count() > 0)
  1188                 Ok(is_dir && node.tracked_descendants_count() > 0)
   946             } else {
  1189             } else {
   947                 Ok(false)
  1190                 Ok(false)
   948             }
  1191             }
   949         })
  1192         })
   950     }
  1193     }
   955     ) -> Result<bool, DirstateError> {
  1198     ) -> Result<bool, DirstateError> {
   956         self.with_dmap_mut(|map| {
  1199         self.with_dmap_mut(|map| {
   957             if let Some(node) = map.get_node(directory)? {
  1200             if let Some(node) = map.get_node(directory)? {
   958                 // A node without a `DirstateEntry` was created to hold child
  1201                 // A node without a `DirstateEntry` was created to hold child
   959                 // nodes, and is therefore a directory.
  1202                 // nodes, and is therefore a directory.
   960                 let state = node.state()?;
  1203                 let is_dir = node.entry()?.is_none();
   961                 Ok(state.is_none() && node.descendants_with_entry_count() > 0)
  1204                 Ok(is_dir && node.descendants_with_entry_count() > 0)
   962             } else {
  1205             } else {
   963                 Ok(false)
  1206                 Ok(false)
   964             }
  1207             }
   965         })
  1208         })
   966     }
  1209     }
  1086         key: &HgPath,
  1329         key: &HgPath,
  1087     ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
  1330     ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
  1088         self.with_dmap_mut(|map| {
  1331         self.with_dmap_mut(|map| {
  1089             let count = &mut map.nodes_with_copy_source_count;
  1332             let count = &mut map.nodes_with_copy_source_count;
  1090             let unreachable_bytes = &mut map.unreachable_bytes;
  1333             let unreachable_bytes = &mut map.unreachable_bytes;
  1091             Ok(DirstateMap::get_node_mut(
  1334             Ok(DirstateMap::get_node_mut_inner(
  1092                 map.on_disk,
  1335                 map.on_disk,
  1093                 unreachable_bytes,
  1336                 unreachable_bytes,
  1094                 &mut map.root,
  1337                 &mut map.root,
  1095                 key,
  1338                 key,
       
  1339                 |_ancestor| {},
  1096             )?
  1340             )?
  1097             .and_then(|node| {
  1341             .and_then(|node| {
  1098                 if let Some(source) = &node.copy_source {
  1342                 if let Some(source) = &node.copy_source {
  1099                     *count -= 1;
  1343                     *count = count
       
  1344                         .checked_sub(1)
       
  1345                         .expect("nodes_with_copy_source_count should be >= 0");
  1100                     DirstateMap::count_dropped_path(unreachable_bytes, source);
  1346                     DirstateMap::count_dropped_path(unreachable_bytes, source);
  1101                 }
  1347                 }
  1102                 node.copy_source.take().map(Cow::into_owned)
  1348                 node.copy_source.take().map(Cow::into_owned)
  1103             }))
  1349             }))
  1104         })
  1350         })
  1105     }
  1351     }
  1106 
  1352 
  1107     pub fn copy_map_insert(
  1353     pub fn copy_map_insert(
  1108         &mut self,
  1354         &mut self,
  1109         key: HgPathBuf,
  1355         key: &HgPath,
  1110         value: HgPathBuf,
  1356         value: &HgPath,
  1111     ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
  1357     ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
  1112         self.with_dmap_mut(|map| {
  1358         self.with_dmap_mut(|map| {
  1113             let node = DirstateMap::get_or_insert_node(
  1359             let node = map.get_or_insert_node(&key, |_ancestor| {})?;
  1114                 map.on_disk,
  1360             let had_copy_source = node.copy_source.is_none();
  1115                 &mut map.unreachable_bytes,
  1361             let old = node
  1116                 &mut map.root,
  1362                 .copy_source
  1117                 &key,
  1363                 .replace(value.to_owned().into())
  1118                 WithBasename::to_cow_owned,
  1364                 .map(Cow::into_owned);
  1119                 |_ancestor| {},
  1365             if had_copy_source {
  1120             )?;
       
  1121             if node.copy_source.is_none() {
       
  1122                 map.nodes_with_copy_source_count += 1
  1366                 map.nodes_with_copy_source_count += 1
  1123             }
  1367             }
  1124             Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
  1368             Ok(old)
  1125         })
  1369         })
  1126     }
  1370     }
  1127 
  1371 
  1128     pub fn len(&self) -> usize {
  1372     pub fn len(&self) -> usize {
  1129         let map = self.get_map();
  1373         let map = self.get_map();
  1180                 } else {
  1424                 } else {
  1181                     None
  1425                     None
  1182                 })
  1426                 })
  1183             },
  1427             },
  1184         )))
  1428         )))
       
  1429     }
       
  1430 
       
  1431     /// Only public because it needs to be exposed to the Python layer.
       
  1432     /// It is not the full `setparents` logic, only the parts that mutate the
       
  1433     /// entries.
       
  1434     pub fn setparents_fixup(
       
  1435         &mut self,
       
  1436     ) -> Result<Vec<(HgPathBuf, HgPathBuf)>, DirstateV2ParseError> {
       
  1437         // XXX
       
  1438         // All the copying and re-querying is quite inefficient, but this is
       
  1439         // still a lot better than doing it from Python.
       
  1440         //
       
  1441         // The better solution is to develop a mechanism for `iter_mut`,
       
  1442         // which will be a lot more involved: we're dealing with a lazy,
       
  1443         // append-mostly, tree-like data structure. This will do for now.
       
  1444         let mut copies = vec![];
       
  1445         let mut files_with_p2_info = vec![];
       
  1446         for res in self.iter() {
       
  1447             let (path, entry) = res?;
       
  1448             if entry.p2_info() {
       
  1449                 files_with_p2_info.push(path.to_owned())
       
  1450             }
       
  1451         }
       
  1452         self.with_dmap_mut(|map| {
       
  1453             for path in files_with_p2_info.iter() {
       
  1454                 let node = map.get_or_insert_node(path, |_| {})?;
       
  1455                 let entry =
       
  1456                     node.data.as_entry_mut().expect("entry should exist");
       
  1457                 entry.drop_merge_data();
       
  1458                 if let Some(source) = node.copy_source.take().as_deref() {
       
  1459                     copies.push((path.to_owned(), source.to_owned()));
       
  1460                 }
       
  1461             }
       
  1462             Ok(copies)
       
  1463         })
  1185     }
  1464     }
  1186 
  1465 
  1187     pub fn debug_iter(
  1466     pub fn debug_iter(
  1188         &self,
  1467         &self,
  1189         all: bool,
  1468         all: bool,
  1209             };
  1488             };
  1210             Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
  1489             Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
  1211         }))
  1490         }))
  1212     }
  1491     }
  1213 }
  1492 }
       
  1493 #[cfg(test)]
       
  1494 mod tests {
       
  1495     use super::*;
       
  1496 
       
  1497     /// Shortcut to return tracked descendants of a path.
       
  1498     /// Panics if the path does not exist.
       
  1499     fn tracked_descendants(map: &OwningDirstateMap, path: &[u8]) -> u32 {
       
  1500         let path = dbg!(HgPath::new(path));
       
  1501         let node = map.get_map().get_node(path);
       
  1502         node.unwrap().unwrap().tracked_descendants_count()
       
  1503     }
       
  1504 
       
  1505     /// Shortcut to return descendants with an entry.
       
  1506     /// Panics if the path does not exist.
       
  1507     fn descendants_with_an_entry(map: &OwningDirstateMap, path: &[u8]) -> u32 {
       
  1508         let path = dbg!(HgPath::new(path));
       
  1509         let node = map.get_map().get_node(path);
       
  1510         node.unwrap().unwrap().descendants_with_entry_count()
       
  1511     }
       
  1512 
       
  1513     fn assert_does_not_exist(map: &OwningDirstateMap, path: &[u8]) {
       
  1514         let path = dbg!(HgPath::new(path));
       
  1515         let node = map.get_map().get_node(path);
       
  1516         assert!(node.unwrap().is_none());
       
  1517     }
       
  1518 
       
  1519     /// Shortcut for path creation in tests
       
  1520     fn p(b: &[u8]) -> &HgPath {
       
  1521         HgPath::new(b)
       
  1522     }
       
  1523 
       
  1524     /// Test the very simple case a single tracked file
       
  1525     #[test]
       
  1526     fn test_tracked_descendants_simple() -> Result<(), DirstateError> {
       
  1527         let mut map = OwningDirstateMap::new_empty(vec![]);
       
  1528         assert_eq!(map.len(), 0);
       
  1529 
       
  1530         map.set_tracked(p(b"some/nested/path"))?;
       
  1531 
       
  1532         assert_eq!(map.len(), 1);
       
  1533         assert_eq!(tracked_descendants(&map, b"some"), 1);
       
  1534         assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
       
  1535         assert_eq!(tracked_descendants(&map, b"some/nested/path"), 0);
       
  1536 
       
  1537         map.set_untracked(p(b"some/nested/path"))?;
       
  1538         assert_eq!(map.len(), 0);
       
  1539         assert!(map.get_map().get_node(p(b"some"))?.is_none());
       
  1540 
       
  1541         Ok(())
       
  1542     }
       
  1543 
       
  1544     /// Test the simple case of all tracked, but multiple files
       
  1545     #[test]
       
  1546     fn test_tracked_descendants_multiple() -> Result<(), DirstateError> {
       
  1547         let mut map = OwningDirstateMap::new_empty(vec![]);
       
  1548 
       
  1549         map.set_tracked(p(b"some/nested/path"))?;
       
  1550         map.set_tracked(p(b"some/nested/file"))?;
       
  1551         // one layer without any files to test deletion cascade
       
  1552         map.set_tracked(p(b"some/other/nested/path"))?;
       
  1553         map.set_tracked(p(b"root_file"))?;
       
  1554         map.set_tracked(p(b"some/file"))?;
       
  1555         map.set_tracked(p(b"some/file2"))?;
       
  1556         map.set_tracked(p(b"some/file3"))?;
       
  1557 
       
  1558         assert_eq!(map.len(), 7);
       
  1559         assert_eq!(tracked_descendants(&map, b"some"), 6);
       
  1560         assert_eq!(tracked_descendants(&map, b"some/nested"), 2);
       
  1561         assert_eq!(tracked_descendants(&map, b"some/other"), 1);
       
  1562         assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
       
  1563         assert_eq!(tracked_descendants(&map, b"some/nested/path"), 0);
       
  1564 
       
  1565         map.set_untracked(p(b"some/nested/path"))?;
       
  1566         assert_eq!(map.len(), 6);
       
  1567         assert_eq!(tracked_descendants(&map, b"some"), 5);
       
  1568         assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
       
  1569         assert_eq!(tracked_descendants(&map, b"some/other"), 1);
       
  1570         assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
       
  1571 
       
  1572         map.set_untracked(p(b"some/nested/file"))?;
       
  1573         assert_eq!(map.len(), 5);
       
  1574         assert_eq!(tracked_descendants(&map, b"some"), 4);
       
  1575         assert_eq!(tracked_descendants(&map, b"some/other"), 1);
       
  1576         assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
       
  1577         assert_does_not_exist(&map, b"some_nested");
       
  1578 
       
  1579         map.set_untracked(p(b"some/other/nested/path"))?;
       
  1580         assert_eq!(map.len(), 4);
       
  1581         assert_eq!(tracked_descendants(&map, b"some"), 3);
       
  1582         assert_does_not_exist(&map, b"some/other");
       
  1583 
       
  1584         map.set_untracked(p(b"root_file"))?;
       
  1585         assert_eq!(map.len(), 3);
       
  1586         assert_eq!(tracked_descendants(&map, b"some"), 3);
       
  1587         assert_does_not_exist(&map, b"root_file");
       
  1588 
       
  1589         map.set_untracked(p(b"some/file"))?;
       
  1590         assert_eq!(map.len(), 2);
       
  1591         assert_eq!(tracked_descendants(&map, b"some"), 2);
       
  1592         assert_does_not_exist(&map, b"some/file");
       
  1593 
       
  1594         map.set_untracked(p(b"some/file2"))?;
       
  1595         assert_eq!(map.len(), 1);
       
  1596         assert_eq!(tracked_descendants(&map, b"some"), 1);
       
  1597         assert_does_not_exist(&map, b"some/file2");
       
  1598 
       
  1599         map.set_untracked(p(b"some/file3"))?;
       
  1600         assert_eq!(map.len(), 0);
       
  1601         assert_does_not_exist(&map, b"some/file3");
       
  1602 
       
  1603         Ok(())
       
  1604     }
       
  1605 
       
  1606     /// Check with a mix of tracked and non-tracked items
       
  1607     #[test]
       
  1608     fn test_tracked_descendants_different() -> Result<(), DirstateError> {
       
  1609         let mut map = OwningDirstateMap::new_empty(vec![]);
       
  1610 
       
  1611         // A file that was just added
       
  1612         map.set_tracked(p(b"some/nested/path"))?;
       
  1613         // This has no information, the dirstate should ignore it
       
  1614         map.reset_state(p(b"some/file"), false, false, false, false, None)?;
       
  1615         assert_does_not_exist(&map, b"some/file");
       
  1616 
       
  1617         // A file that was removed
       
  1618         map.reset_state(
       
  1619             p(b"some/nested/file"),
       
  1620             false,
       
  1621             true,
       
  1622             false,
       
  1623             false,
       
  1624             None,
       
  1625         )?;
       
  1626         assert!(!map.get(p(b"some/nested/file"))?.unwrap().tracked());
       
  1627         // Only present in p2
       
  1628         map.reset_state(p(b"some/file3"), false, false, true, false, None)?;
       
  1629         assert!(!map.get(p(b"some/file3"))?.unwrap().tracked());
       
  1630         // A file that was merged
       
  1631         map.reset_state(p(b"root_file"), true, true, true, false, None)?;
       
  1632         assert!(map.get(p(b"root_file"))?.unwrap().tracked());
       
  1633         // A file that is added, with info from p2
       
  1634         // XXX is that actually possible?
       
  1635         map.reset_state(p(b"some/file2"), true, false, true, false, None)?;
       
  1636         assert!(map.get(p(b"some/file2"))?.unwrap().tracked());
       
  1637         // A clean file
       
  1638         // One layer without any files to test deletion cascade
       
  1639         map.reset_state(
       
  1640             p(b"some/other/nested/path"),
       
  1641             true,
       
  1642             true,
       
  1643             false,
       
  1644             false,
       
  1645             None,
       
  1646         )?;
       
  1647         assert!(map.get(p(b"some/other/nested/path"))?.unwrap().tracked());
       
  1648 
       
  1649         assert_eq!(map.len(), 6);
       
  1650         assert_eq!(tracked_descendants(&map, b"some"), 3);
       
  1651         assert_eq!(descendants_with_an_entry(&map, b"some"), 5);
       
  1652         assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
       
  1653         assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
       
  1654         assert_eq!(tracked_descendants(&map, b"some/other/nested/path"), 0);
       
  1655         assert_eq!(
       
  1656             descendants_with_an_entry(&map, b"some/other/nested/path"),
       
  1657             0
       
  1658         );
       
  1659         assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
       
  1660         assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
       
  1661 
       
  1662         // might as well check this
       
  1663         map.set_untracked(p(b"path/does/not/exist"))?;
       
  1664         assert_eq!(map.len(), 6);
       
  1665 
       
  1666         map.set_untracked(p(b"some/other/nested/path"))?;
       
  1667         // It is set untracked but not deleted since it held other information
       
  1668         assert_eq!(map.len(), 6);
       
  1669         assert_eq!(tracked_descendants(&map, b"some"), 2);
       
  1670         assert_eq!(descendants_with_an_entry(&map, b"some"), 5);
       
  1671         assert_eq!(descendants_with_an_entry(&map, b"some/other"), 1);
       
  1672         assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
       
  1673         assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
       
  1674         assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
       
  1675 
       
  1676         map.set_untracked(p(b"some/nested/path"))?;
       
  1677         // It is set untracked *and* deleted since it was only added
       
  1678         assert_eq!(map.len(), 5);
       
  1679         assert_eq!(tracked_descendants(&map, b"some"), 1);
       
  1680         assert_eq!(descendants_with_an_entry(&map, b"some"), 4);
       
  1681         assert_eq!(tracked_descendants(&map, b"some/nested"), 0);
       
  1682         assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 1);
       
  1683         assert_does_not_exist(&map, b"some/nested/path");
       
  1684 
       
  1685         map.set_untracked(p(b"root_file"))?;
       
  1686         // Untracked but not deleted
       
  1687         assert_eq!(map.len(), 5);
       
  1688         assert!(map.get(p(b"root_file"))?.is_some());
       
  1689 
       
  1690         map.set_untracked(p(b"some/file2"))?;
       
  1691         assert_eq!(map.len(), 5);
       
  1692         assert_eq!(tracked_descendants(&map, b"some"), 0);
       
  1693         assert!(map.get(p(b"some/file2"))?.is_some());
       
  1694 
       
  1695         map.set_untracked(p(b"some/file3"))?;
       
  1696         assert_eq!(map.len(), 5);
       
  1697         assert_eq!(tracked_descendants(&map, b"some"), 0);
       
  1698         assert!(map.get(p(b"some/file3"))?.is_some());
       
  1699 
       
  1700         Ok(())
       
  1701     }
       
  1702 
       
  1703     /// Check that copies counter is correctly updated
       
  1704     #[test]
       
  1705     fn test_copy_source() -> Result<(), DirstateError> {
       
  1706         let mut map = OwningDirstateMap::new_empty(vec![]);
       
  1707 
       
  1708         // Clean file
       
  1709         map.reset_state(p(b"files/clean"), true, true, false, false, None)?;
       
  1710         // Merged file
       
  1711         map.reset_state(p(b"files/from_p2"), true, true, true, false, None)?;
       
  1712         // Removed file
       
  1713         map.reset_state(p(b"removed"), false, true, false, false, None)?;
       
  1714         // Added file
       
  1715         map.reset_state(p(b"files/added"), true, false, false, false, None)?;
       
  1716         // Add copy
       
  1717         map.copy_map_insert(p(b"files/clean"), p(b"clean_copy_source"))?;
       
  1718         assert_eq!(map.copy_map_len(), 1);
       
  1719 
       
  1720         // Copy override
       
  1721         map.copy_map_insert(p(b"files/clean"), p(b"other_clean_copy_source"))?;
       
  1722         assert_eq!(map.copy_map_len(), 1);
       
  1723 
       
  1724         // Multiple copies
       
  1725         map.copy_map_insert(p(b"removed"), p(b"removed_copy_source"))?;
       
  1726         assert_eq!(map.copy_map_len(), 2);
       
  1727 
       
  1728         map.copy_map_insert(p(b"files/added"), p(b"added_copy_source"))?;
       
  1729         assert_eq!(map.copy_map_len(), 3);
       
  1730 
       
  1731         // Added, so the entry is completely removed
       
  1732         map.set_untracked(p(b"files/added"))?;
       
  1733         assert_does_not_exist(&map, b"files/added");
       
  1734         assert_eq!(map.copy_map_len(), 2);
       
  1735 
       
  1736         // Removed, so the entry is kept around, so is its copy
       
  1737         map.set_untracked(p(b"removed"))?;
       
  1738         assert!(map.get(p(b"removed"))?.is_some());
       
  1739         assert_eq!(map.copy_map_len(), 2);
       
  1740 
       
  1741         // Clean, so the entry is kept around, but not its copy
       
  1742         map.set_untracked(p(b"files/clean"))?;
       
  1743         assert!(map.get(p(b"files/clean"))?.is_some());
       
  1744         assert_eq!(map.copy_map_len(), 1);
       
  1745 
       
  1746         map.copy_map_insert(p(b"files/from_p2"), p(b"from_p2_copy_source"))?;
       
  1747         assert_eq!(map.copy_map_len(), 2);
       
  1748 
       
  1749         // Info from p2, so its copy source info is kept around
       
  1750         map.set_untracked(p(b"files/from_p2"))?;
       
  1751         assert!(map.get(p(b"files/from_p2"))?.is_some());
       
  1752         assert_eq!(map.copy_map_len(), 2);
       
  1753 
       
  1754         Ok(())
       
  1755     }
       
  1756 
       
  1757     /// Test with "on disk" data. For the sake of this test, the "on disk" data
       
  1758     /// does not actually come from the disk, but it's opaque to the code being
       
  1759     /// tested.
       
  1760     #[test]
       
  1761     fn test_on_disk() -> Result<(), DirstateError> {
       
  1762         // First let's create some data to put "on disk"
       
  1763         let mut map = OwningDirstateMap::new_empty(vec![]);
       
  1764 
       
  1765         // A file that was just added
       
  1766         map.set_tracked(p(b"some/nested/added"))?;
       
  1767         map.copy_map_insert(p(b"some/nested/added"), p(b"added_copy_source"))?;
       
  1768 
       
  1769         // A file that was removed
       
  1770         map.reset_state(
       
  1771             p(b"some/nested/removed"),
       
  1772             false,
       
  1773             true,
       
  1774             false,
       
  1775             false,
       
  1776             None,
       
  1777         )?;
       
  1778         // Only present in p2
       
  1779         map.reset_state(
       
  1780             p(b"other/p2_info_only"),
       
  1781             false,
       
  1782             false,
       
  1783             true,
       
  1784             false,
       
  1785             None,
       
  1786         )?;
       
  1787         map.copy_map_insert(
       
  1788             p(b"other/p2_info_only"),
       
  1789             p(b"other/p2_info_copy_source"),
       
  1790         )?;
       
  1791         // A file that was merged
       
  1792         map.reset_state(p(b"merged"), true, true, true, false, None)?;
       
  1793         // A file that is added, with info from p2
       
  1794         // XXX is that actually possible?
       
  1795         map.reset_state(
       
  1796             p(b"other/added_with_p2"),
       
  1797             true,
       
  1798             false,
       
  1799             true,
       
  1800             false,
       
  1801             None,
       
  1802         )?;
       
  1803         // One layer without any files to test deletion cascade
       
  1804         // A clean file
       
  1805         map.reset_state(
       
  1806             p(b"some/other/nested/clean"),
       
  1807             true,
       
  1808             true,
       
  1809             false,
       
  1810             false,
       
  1811             None,
       
  1812         )?;
       
  1813 
       
  1814         let (packed, metadata, _should_append, _old_data_size) =
       
  1815             map.pack_v2(false)?;
       
  1816         let packed_len = packed.len();
       
  1817         assert!(packed_len > 0);
       
  1818 
       
  1819         // Recreate "from disk"
       
  1820         let mut map = OwningDirstateMap::new_v2(
       
  1821             packed,
       
  1822             packed_len,
       
  1823             metadata.as_bytes(),
       
  1824         )?;
       
  1825 
       
  1826         // Check that everything is accounted for
       
  1827         assert!(map.contains_key(p(b"some/nested/added"))?);
       
  1828         assert!(map.contains_key(p(b"some/nested/removed"))?);
       
  1829         assert!(map.contains_key(p(b"merged"))?);
       
  1830         assert!(map.contains_key(p(b"other/p2_info_only"))?);
       
  1831         assert!(map.contains_key(p(b"other/added_with_p2"))?);
       
  1832         assert!(map.contains_key(p(b"some/other/nested/clean"))?);
       
  1833         assert_eq!(
       
  1834             map.copy_map_get(p(b"some/nested/added"))?,
       
  1835             Some(p(b"added_copy_source"))
       
  1836         );
       
  1837         assert_eq!(
       
  1838             map.copy_map_get(p(b"other/p2_info_only"))?,
       
  1839             Some(p(b"other/p2_info_copy_source"))
       
  1840         );
       
  1841         assert_eq!(tracked_descendants(&map, b"some"), 2);
       
  1842         assert_eq!(descendants_with_an_entry(&map, b"some"), 3);
       
  1843         assert_eq!(tracked_descendants(&map, b"other"), 1);
       
  1844         assert_eq!(descendants_with_an_entry(&map, b"other"), 2);
       
  1845         assert_eq!(tracked_descendants(&map, b"some/other"), 1);
       
  1846         assert_eq!(descendants_with_an_entry(&map, b"some/other"), 1);
       
  1847         assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1);
       
  1848         assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1);
       
  1849         assert_eq!(tracked_descendants(&map, b"some/nested"), 1);
       
  1850         assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2);
       
  1851         assert_eq!(map.len(), 6);
       
  1852         assert_eq!(map.get_map().unreachable_bytes, 0);
       
  1853         assert_eq!(map.copy_map_len(), 2);
       
  1854 
       
  1855         // Shouldn't change anything since it's already not tracked
       
  1856         map.set_untracked(p(b"some/nested/removed"))?;
       
  1857         assert_eq!(map.get_map().unreachable_bytes, 0);
       
  1858 
       
  1859         match map.get_map().root {
       
  1860             ChildNodes::InMemory(_) => {
       
  1861                 panic!("root should not have been mutated")
       
  1862             }
       
  1863             _ => (),
       
  1864         }
       
  1865         // We haven't mutated enough (nothing, actually), we should still be in
       
  1866         // the append strategy
       
  1867         assert!(map.get_map().write_should_append());
       
  1868 
       
  1869         // But this mutates the structure, so there should be unreachable_bytes
       
  1870         assert!(map.set_untracked(p(b"some/nested/added"))?);
       
  1871         let unreachable_bytes = map.get_map().unreachable_bytes;
       
  1872         assert!(unreachable_bytes > 0);
       
  1873 
       
  1874         match map.get_map().root {
       
  1875             ChildNodes::OnDisk(_) => panic!("root should have been mutated"),
       
  1876             _ => (),
       
  1877         }
       
  1878 
       
  1879         // This should not mutate the structure either, since `root` has
       
  1880         // already been mutated along with its direct children.
       
  1881         map.set_untracked(p(b"merged"))?;
       
  1882         assert_eq!(map.get_map().unreachable_bytes, unreachable_bytes);
       
  1883 
       
  1884         match map.get_map().get_node(p(b"other/added_with_p2"))?.unwrap() {
       
  1885             NodeRef::InMemory(_, _) => {
       
  1886                 panic!("'other/added_with_p2' should not have been mutated")
       
  1887             }
       
  1888             _ => (),
       
  1889         }
       
  1890         // But this should, since it's in a different path
       
  1891         // than `<root>some/nested/add`
       
  1892         map.set_untracked(p(b"other/added_with_p2"))?;
       
  1893         assert!(map.get_map().unreachable_bytes > unreachable_bytes);
       
  1894 
       
  1895         match map.get_map().get_node(p(b"other/added_with_p2"))?.unwrap() {
       
  1896             NodeRef::OnDisk(_) => {
       
  1897                 panic!("'other/added_with_p2' should have been mutated")
       
  1898             }
       
  1899             _ => (),
       
  1900         }
       
  1901 
       
  1902         // We have rewritten most of the tree, we should create a new file
       
  1903         assert!(!map.get_map().write_should_append());
       
  1904 
       
  1905         Ok(())
       
  1906     }
       
  1907 }