dirstate_map.rs
773 lines
| 23.9 KiB
| application/rls-services+xml
|
RustLexer
Simon Sapin
|
r47872 | use bytes_cast::BytesCast; | ||
Simon Sapin
|
r47888 | use micro_timer::timed; | ||
Simon Sapin
|
r47895 | use std::borrow::Cow; | ||
Simon Sapin
|
r47889 | use std::convert::TryInto; | ||
Simon Sapin
|
r47865 | use std::path::PathBuf; | ||
Simon Sapin
|
r48058 | use super::on_disk; | ||
Simon Sapin
|
r47867 | use super::path_with_basename::WithBasename; | ||
Simon Sapin
|
r47872 | use crate::dirstate::parsers::pack_entry; | ||
use crate::dirstate::parsers::packed_entry_size; | ||||
Simon Sapin
|
r47868 | use crate::dirstate::parsers::parse_dirstate_entries; | ||
Simon Sapin
|
r47871 | use crate::dirstate::parsers::Timestamp; | ||
Simon Sapin
|
r47865 | use crate::matchers::Matcher; | ||
use crate::utils::hg_path::{HgPath, HgPathBuf}; | ||||
use crate::CopyMapIter; | ||||
use crate::DirstateEntry; | ||||
use crate::DirstateError; | ||||
use crate::DirstateMapError; | ||||
use crate::DirstateParents; | ||||
use crate::DirstateStatus; | ||||
use crate::EntryState; | ||||
Simon Sapin
|
r47889 | use crate::FastHashMap; | ||
Simon Sapin
|
r47865 | use crate::PatternFileWarning; | ||
use crate::StateMapIter; | ||||
use crate::StatusError; | ||||
use crate::StatusOptions; | ||||
Simon Sapin
|
r47893 | pub struct DirstateMap<'on_disk> { | ||
/// Contents of the `.hg/dirstate` file | ||||
Simon Sapin
|
r48058 | pub(super) on_disk: &'on_disk [u8], | ||
Simon Sapin
|
r47893 | |||
Simon Sapin
|
r47895 | pub(super) root: ChildNodes<'on_disk>, | ||
Simon Sapin
|
r47873 | |||
/// Number of nodes anywhere in the tree that have `.entry.is_some()`. | ||||
Simon Sapin
|
r48058 | pub(super) nodes_with_entry_count: u32, | ||
Simon Sapin
|
r47873 | |||
/// Number of nodes anywhere in the tree that have | ||||
/// `.copy_source.is_some()`. | ||||
Simon Sapin
|
r48058 | pub(super) nodes_with_copy_source_count: u32, | ||
Simon Sapin
|
r47867 | } | ||
/// Using a plain `HgPathBuf` of the full path from the repository root as a | ||||
/// map key would also work: all paths in a given map have the same parent | ||||
/// path, so comparing full paths gives the same result as comparing base | ||||
Simon Sapin
|
r48057 | /// names. However `HashMap` would waste time always re-hashing the same | ||
Simon Sapin
|
r47867 | /// string prefix. | ||
Simon Sapin
|
r48057 | pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>; | ||
Simon Sapin
|
r48124 | |||
pub(super) enum ChildNodes<'on_disk> { | ||||
InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>), | ||||
} | ||||
pub(super) enum ChildNodesRef<'tree, 'on_disk> { | ||||
InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>), | ||||
} | ||||
pub(super) enum NodeRef<'tree, 'on_disk> { | ||||
InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>), | ||||
} | ||||
impl Default for ChildNodes<'_> { | ||||
fn default() -> Self { | ||||
ChildNodes::InMemory(Default::default()) | ||||
} | ||||
} | ||||
impl<'on_disk> ChildNodes<'on_disk> { | ||||
pub(super) fn as_ref<'tree>( | ||||
&'tree self, | ||||
) -> ChildNodesRef<'tree, 'on_disk> { | ||||
match self { | ||||
ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes), | ||||
} | ||||
} | ||||
pub(super) fn is_empty(&self) -> bool { | ||||
match self { | ||||
ChildNodes::InMemory(nodes) => nodes.is_empty(), | ||||
} | ||||
} | ||||
pub(super) fn make_mut( | ||||
&mut self, | ||||
) -> &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>> { | ||||
match self { | ||||
ChildNodes::InMemory(nodes) => nodes, | ||||
} | ||||
} | ||||
} | ||||
impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> { | ||||
pub(super) fn get( | ||||
&self, | ||||
base_name: &HgPath, | ||||
) -> Option<NodeRef<'tree, 'on_disk>> { | ||||
match self { | ||||
ChildNodesRef::InMemory(nodes) => nodes | ||||
.get_key_value(base_name) | ||||
.map(|(k, v)| NodeRef::InMemory(k, v)), | ||||
} | ||||
} | ||||
/// Iterate in undefined order | ||||
pub(super) fn iter( | ||||
&self, | ||||
) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> { | ||||
match self { | ||||
ChildNodesRef::InMemory(nodes) => { | ||||
nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)) | ||||
} | ||||
} | ||||
} | ||||
/// Iterate in parallel in undefined order | ||||
pub(super) fn par_iter( | ||||
&self, | ||||
) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>> | ||||
{ | ||||
use rayon::prelude::*; | ||||
match self { | ||||
ChildNodesRef::InMemory(nodes) => { | ||||
nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)) | ||||
} | ||||
} | ||||
} | ||||
pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> { | ||||
match self { | ||||
ChildNodesRef::InMemory(nodes) => { | ||||
let mut vec: Vec<_> = nodes | ||||
.iter() | ||||
.map(|(k, v)| NodeRef::InMemory(k, v)) | ||||
.collect(); | ||||
// `sort_unstable_by_key` doesn’t allow keys borrowing from the | ||||
// value: https://github.com/rust-lang/rust/issues/34162 | ||||
vec.sort_unstable_by(|a, b| a.base_name().cmp(b.base_name())); | ||||
vec | ||||
} | ||||
} | ||||
} | ||||
} | ||||
impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> { | ||||
pub(super) fn full_path(&self) -> &'tree HgPath { | ||||
match self { | ||||
NodeRef::InMemory(path, _node) => path.full_path(), | ||||
} | ||||
} | ||||
/// Returns a `Cow` that can borrow 'on_disk but is detached from 'tree | ||||
pub(super) fn full_path_cow(&self) -> Cow<'on_disk, HgPath> { | ||||
match self { | ||||
NodeRef::InMemory(path, _node) => path.full_path().clone(), | ||||
} | ||||
} | ||||
pub(super) fn base_name(&self) -> &'tree HgPath { | ||||
match self { | ||||
NodeRef::InMemory(path, _node) => path.base_name(), | ||||
} | ||||
} | ||||
pub(super) fn children(&self) -> ChildNodesRef<'tree, 'on_disk> { | ||||
match self { | ||||
NodeRef::InMemory(_path, node) => node.children.as_ref(), | ||||
} | ||||
} | ||||
pub(super) fn copy_source(&self) -> Option<&'tree HgPath> { | ||||
match self { | ||||
NodeRef::InMemory(_path, node) => { | ||||
node.copy_source.as_ref().map(|s| &**s) | ||||
} | ||||
} | ||||
} | ||||
pub(super) fn has_entry(&self) -> bool { | ||||
match self { | ||||
NodeRef::InMemory(_path, node) => node.entry.is_some(), | ||||
} | ||||
} | ||||
pub(super) fn entry(&self) -> Option<DirstateEntry> { | ||||
match self { | ||||
NodeRef::InMemory(_path, node) => node.entry, | ||||
} | ||||
} | ||||
pub(super) fn state(&self) -> Option<EntryState> { | ||||
match self { | ||||
NodeRef::InMemory(_path, node) => { | ||||
node.entry.as_ref().map(|entry| entry.state) | ||||
} | ||||
} | ||||
} | ||||
pub(super) fn tracked_descendants_count(&self) -> u32 { | ||||
match self { | ||||
NodeRef::InMemory(_path, node) => node.tracked_descendants_count, | ||||
} | ||||
} | ||||
} | ||||
Simon Sapin
|
r47867 | |||
Simon Sapin
|
r47876 | /// Represents a file or a directory | ||
Simon Sapin
|
r47867 | #[derive(Default)] | ||
Simon Sapin
|
r47895 | pub(super) struct Node<'on_disk> { | ||
Simon Sapin
|
r47876 | /// `None` for directories | ||
Simon Sapin
|
r47883 | pub(super) entry: Option<DirstateEntry>, | ||
Simon Sapin
|
r47876 | |||
Simon Sapin
|
r47895 | pub(super) copy_source: Option<Cow<'on_disk, HgPath>>, | ||
Simon Sapin
|
r47876 | |||
Simon Sapin
|
r47895 | pub(super) children: ChildNodes<'on_disk>, | ||
Simon Sapin
|
r47876 | |||
/// How many (non-inclusive) descendants of this node are tracked files | ||||
Simon Sapin
|
r48058 | pub(super) tracked_descendants_count: u32, | ||
Simon Sapin
|
r47876 | } | ||
Simon Sapin
|
r47893 | impl<'on_disk> DirstateMap<'on_disk> { | ||
Simon Sapin
|
r48058 | pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self { | ||
Self { | ||||
on_disk, | ||||
root: ChildNodes::default(), | ||||
nodes_with_entry_count: 0, | ||||
nodes_with_copy_source_count: 0, | ||||
} | ||||
} | ||||
Simon Sapin
|
r48055 | #[timed] | ||
pub fn new_v2( | ||||
on_disk: &'on_disk [u8], | ||||
) -> Result<(Self, Option<DirstateParents>), DirstateError> { | ||||
Simon Sapin
|
r48058 | on_disk::read(on_disk) | ||
Simon Sapin
|
r48055 | } | ||
#[timed] | ||||
pub fn new_v1( | ||||
Simon Sapin
|
r47893 | on_disk: &'on_disk [u8], | ||
) -> Result<(Self, Option<DirstateParents>), DirstateError> { | ||||
Simon Sapin
|
r48058 | let mut map = Self::empty(on_disk); | ||
Simon Sapin
|
r48055 | if map.on_disk.is_empty() { | ||
return Ok((map, None)); | ||||
Simon Sapin
|
r47867 | } | ||
Simon Sapin
|
r47893 | |||
let parents = parse_dirstate_entries( | ||||
Simon Sapin
|
r48055 | map.on_disk, | ||
Simon Sapin
|
r47893 | |path, entry, copy_source| { | ||
let tracked = entry.state.is_tracked(); | ||||
Simon Sapin
|
r47896 | let node = Self::get_or_insert_node( | ||
Simon Sapin
|
r48055 | &mut map.root, | ||
Simon Sapin
|
r47893 | path, | ||
Simon Sapin
|
r47896 | WithBasename::to_cow_borrowed, | ||
Simon Sapin
|
r47893 | |ancestor| { | ||
if tracked { | ||||
ancestor.tracked_descendants_count += 1 | ||||
} | ||||
}, | ||||
); | ||||
assert!( | ||||
node.entry.is_none(), | ||||
"duplicate dirstate entry in read" | ||||
); | ||||
assert!( | ||||
node.copy_source.is_none(), | ||||
"duplicate dirstate entry in read" | ||||
); | ||||
node.entry = Some(*entry); | ||||
Simon Sapin
|
r47895 | node.copy_source = copy_source.map(Cow::Borrowed); | ||
Simon Sapin
|
r48055 | map.nodes_with_entry_count += 1; | ||
Simon Sapin
|
r47893 | if copy_source.is_some() { | ||
Simon Sapin
|
r48055 | map.nodes_with_copy_source_count += 1 | ||
Simon Sapin
|
r47893 | } | ||
}, | ||||
)?; | ||||
Simon Sapin
|
r48055 | let parents = Some(parents.clone()); | ||
Simon Sapin
|
r47893 | |||
Simon Sapin
|
r48055 | Ok((map, parents)) | ||
Simon Sapin
|
r47867 | } | ||
Simon Sapin
|
r48124 | fn get_node<'tree>( | ||
&'tree self, | ||||
path: &HgPath, | ||||
) -> Option<NodeRef<'tree, 'on_disk>> { | ||||
let mut children = self.root.as_ref(); | ||||
Simon Sapin
|
r47869 | let mut components = path.components(); | ||
let mut component = | ||||
components.next().expect("expected at least one components"); | ||||
loop { | ||||
let child = children.get(component)?; | ||||
if let Some(next_component) = components.next() { | ||||
component = next_component; | ||||
Simon Sapin
|
r48124 | children = child.children(); | ||
Simon Sapin
|
r47869 | } else { | ||
return Some(child); | ||||
} | ||||
} | ||||
} | ||||
Simon Sapin
|
r47876 | /// Returns a mutable reference to the node at `path` if it exists | ||
/// | ||||
Simon Sapin
|
r47873 | /// This takes `root` instead of `&mut self` so that callers can mutate | ||
/// other fields while the returned borrow is still valid | ||||
Simon Sapin
|
r47874 | fn get_node_mut<'tree>( | ||
Simon Sapin
|
r47895 | root: &'tree mut ChildNodes<'on_disk>, | ||
Simon Sapin
|
r47874 | path: &HgPath, | ||
Simon Sapin
|
r47895 | ) -> Option<&'tree mut Node<'on_disk>> { | ||
Simon Sapin
|
r47874 | let mut children = root; | ||
let mut components = path.components(); | ||||
let mut component = | ||||
components.next().expect("expected at least one components"); | ||||
loop { | ||||
Simon Sapin
|
r48124 | let child = children.make_mut().get_mut(component)?; | ||
Simon Sapin
|
r47874 | if let Some(next_component) = components.next() { | ||
component = next_component; | ||||
children = &mut child.children; | ||||
} else { | ||||
return Some(child); | ||||
} | ||||
} | ||||
} | ||||
Simon Sapin
|
r47896 | fn get_or_insert_node<'tree, 'path>( | ||
Simon Sapin
|
r47895 | root: &'tree mut ChildNodes<'on_disk>, | ||
Simon Sapin
|
r47896 | path: &'path HgPath, | ||
to_cow: impl Fn( | ||||
WithBasename<&'path HgPath>, | ||||
) -> WithBasename<Cow<'on_disk, HgPath>>, | ||||
Simon Sapin
|
r47890 | mut each_ancestor: impl FnMut(&mut Node), | ||
Simon Sapin
|
r47895 | ) -> &'tree mut Node<'on_disk> { | ||
Simon Sapin
|
r47873 | let mut child_nodes = root; | ||
Simon Sapin
|
r47867 | let mut inclusive_ancestor_paths = | ||
WithBasename::inclusive_ancestors_of(path); | ||||
let mut ancestor_path = inclusive_ancestor_paths | ||||
.next() | ||||
.expect("expected at least one inclusive ancestor"); | ||||
loop { | ||||
Simon Sapin
|
r47886 | // TODO: can we avoid allocating an owned key in cases where the | ||
// map already contains that key, without introducing double | ||||
// lookup? | ||||
Simon Sapin
|
r48124 | let child_node = child_nodes | ||
.make_mut() | ||||
.entry(to_cow(ancestor_path)) | ||||
.or_default(); | ||||
Simon Sapin
|
r47867 | if let Some(next) = inclusive_ancestor_paths.next() { | ||
Simon Sapin
|
r47890 | each_ancestor(child_node); | ||
Simon Sapin
|
r47867 | ancestor_path = next; | ||
child_nodes = &mut child_node.children; | ||||
} else { | ||||
return child_node; | ||||
} | ||||
} | ||||
Simon Sapin
|
r47865 | } | ||
Simon Sapin
|
r47870 | |||
Simon Sapin
|
r47890 | fn add_or_remove_file( | ||
Simon Sapin
|
r47873 | &mut self, | ||
path: &HgPath, | ||||
Simon Sapin
|
r47890 | old_state: EntryState, | ||
Simon Sapin
|
r47873 | new_entry: DirstateEntry, | ||
) { | ||||
Simon Sapin
|
r47876 | let tracked_count_increment = | ||
Simon Sapin
|
r47890 | match (old_state.is_tracked(), new_entry.state.is_tracked()) { | ||
Simon Sapin
|
r47876 | (false, true) => 1, | ||
(true, false) => -1, | ||||
_ => 0, | ||||
}; | ||||
Simon Sapin
|
r47896 | let node = Self::get_or_insert_node( | ||
Simon Sapin
|
r47890 | &mut self.root, | ||
path, | ||||
Simon Sapin
|
r47896 | WithBasename::to_cow_owned, | ||
Simon Sapin
|
r47890 | |ancestor| { | ||
// We can’t use `+= increment` because the counter is unsigned, | ||||
// and we want debug builds to detect accidental underflow | ||||
// through zero | ||||
match tracked_count_increment { | ||||
1 => ancestor.tracked_descendants_count += 1, | ||||
-1 => ancestor.tracked_descendants_count -= 1, | ||||
_ => {} | ||||
} | ||||
}, | ||||
); | ||||
if node.entry.is_none() { | ||||
self.nodes_with_entry_count += 1 | ||||
Simon Sapin
|
r47873 | } | ||
Simon Sapin
|
r47890 | node.entry = Some(new_entry) | ||
Simon Sapin
|
r47873 | } | ||
Simon Sapin
|
r48124 | fn iter_nodes<'tree>( | ||
&'tree self, | ||||
) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> + 'tree { | ||||
Simon Sapin
|
r47870 | // Depth first tree traversal. | ||
// | ||||
// If we could afford internal iteration and recursion, | ||||
// this would look like: | ||||
// | ||||
// ``` | ||||
// fn traverse_children( | ||||
// children: &ChildNodes, | ||||
// each: &mut impl FnMut(&Node), | ||||
// ) { | ||||
// for child in children.values() { | ||||
// traverse_children(&child.children, each); | ||||
// each(child); | ||||
// } | ||||
// } | ||||
// ``` | ||||
// | ||||
// However we want an external iterator and therefore can’t use the | ||||
// call stack. Use an explicit stack instead: | ||||
let mut stack = Vec::new(); | ||||
Simon Sapin
|
r48124 | let mut iter = self.root.as_ref().iter(); | ||
Simon Sapin
|
r47870 | std::iter::from_fn(move || { | ||
Simon Sapin
|
r48124 | while let Some(child_node) = iter.next() { | ||
Simon Sapin
|
r47870 | // Pseudo-recursion | ||
Simon Sapin
|
r48124 | let new_iter = child_node.children().iter(); | ||
Simon Sapin
|
r47870 | let old_iter = std::mem::replace(&mut iter, new_iter); | ||
Simon Sapin
|
r48124 | stack.push((child_node, old_iter)); | ||
Simon Sapin
|
r47870 | } | ||
// Found the end of a `children.iter()` iterator. | ||||
Simon Sapin
|
r48124 | if let Some((child_node, next_iter)) = stack.pop() { | ||
Simon Sapin
|
r47870 | // "Return" from pseudo-recursion by restoring state from the | ||
// explicit stack | ||||
iter = next_iter; | ||||
Simon Sapin
|
r48124 | Some(child_node) | ||
Simon Sapin
|
r47870 | } else { | ||
// Reached the bottom of the stack, we’re done | ||||
None | ||||
} | ||||
}) | ||||
} | ||||
Simon Sapin
|
r48121 | fn clear_known_ambiguous_mtimes(&mut self, paths: &[impl AsRef<HgPath>]) { | ||
for path in paths { | ||||
if let Some(node) = | ||||
Self::get_node_mut(&mut self.root, path.as_ref()) | ||||
{ | ||||
if let Some(entry) = node.entry.as_mut() { | ||||
entry.clear_mtime(); | ||||
} | ||||
Simon Sapin
|
r47870 | } | ||
Simon Sapin
|
r48121 | } | ||
Simon Sapin
|
r47870 | } | ||
Simon Sapin
|
r47865 | } | ||
Simon Sapin
|
r47893 | impl<'on_disk> super::dispatch::DirstateMapMethods for DirstateMap<'on_disk> { | ||
Simon Sapin
|
r47865 | fn clear(&mut self) { | ||
Simon Sapin
|
r48124 | self.root = Default::default(); | ||
Simon Sapin
|
r47873 | self.nodes_with_entry_count = 0; | ||
self.nodes_with_copy_source_count = 0; | ||||
Simon Sapin
|
r47865 | } | ||
fn add_file( | ||||
&mut self, | ||||
Simon Sapin
|
r47877 | filename: &HgPath, | ||
Simon Sapin
|
r47890 | old_state: EntryState, | ||
Simon Sapin
|
r47877 | entry: DirstateEntry, | ||
Simon Sapin
|
r47865 | ) -> Result<(), DirstateMapError> { | ||
Simon Sapin
|
r47890 | self.add_or_remove_file(filename, old_state, entry); | ||
Simon Sapin
|
r47877 | Ok(()) | ||
Simon Sapin
|
r47865 | } | ||
fn remove_file( | ||||
&mut self, | ||||
Simon Sapin
|
r47877 | filename: &HgPath, | ||
Simon Sapin
|
r47890 | old_state: EntryState, | ||
Simon Sapin
|
r47877 | size: i32, | ||
Simon Sapin
|
r47865 | ) -> Result<(), DirstateMapError> { | ||
Simon Sapin
|
r47877 | let entry = DirstateEntry { | ||
state: EntryState::Removed, | ||||
mode: 0, | ||||
size, | ||||
mtime: 0, | ||||
}; | ||||
Simon Sapin
|
r47890 | self.add_or_remove_file(filename, old_state, entry); | ||
Simon Sapin
|
r47877 | Ok(()) | ||
Simon Sapin
|
r47865 | } | ||
fn drop_file( | ||||
&mut self, | ||||
Simon Sapin
|
r47877 | filename: &HgPath, | ||
Simon Sapin
|
r47890 | old_state: EntryState, | ||
Simon Sapin
|
r47865 | ) -> Result<bool, DirstateMapError> { | ||
Simon Sapin
|
r47963 | struct Dropped { | ||
was_tracked: bool, | ||||
had_entry: bool, | ||||
had_copy_source: bool, | ||||
} | ||||
fn recur(nodes: &mut ChildNodes, path: &HgPath) -> Option<Dropped> { | ||||
let (first_path_component, rest_of_path) = | ||||
path.split_first_component(); | ||||
Simon Sapin
|
r48124 | let node = nodes.make_mut().get_mut(first_path_component)?; | ||
Simon Sapin
|
r47963 | let dropped; | ||
if let Some(rest) = rest_of_path { | ||||
dropped = recur(&mut node.children, rest)?; | ||||
if dropped.was_tracked { | ||||
node.tracked_descendants_count -= 1; | ||||
Simon Sapin
|
r47890 | } | ||
Simon Sapin
|
r47963 | } else { | ||
dropped = Dropped { | ||||
was_tracked: node | ||||
.entry | ||||
.as_ref() | ||||
.map_or(false, |entry| entry.state.is_tracked()), | ||||
had_entry: node.entry.take().is_some(), | ||||
had_copy_source: node.copy_source.take().is_some(), | ||||
}; | ||||
Simon Sapin
|
r47964 | } | ||
// 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() | ||||
&& node.copy_source.is_none() | ||||
&& node.children.is_empty() | ||||
{ | ||||
Simon Sapin
|
r48124 | nodes.make_mut().remove(first_path_component); | ||
Simon Sapin
|
r47963 | } | ||
Some(dropped) | ||||
} | ||||
Simon Sapin
|
r47877 | |||
Simon Sapin
|
r47963 | if let Some(dropped) = recur(&mut self.root, filename) { | ||
if dropped.had_entry { | ||||
Simon Sapin
|
r47877 | self.nodes_with_entry_count -= 1 | ||
} | ||||
Simon Sapin
|
r47963 | if dropped.had_copy_source { | ||
Simon Sapin
|
r47877 | self.nodes_with_copy_source_count -= 1 | ||
} | ||||
Simon Sapin
|
r47963 | Ok(dropped.had_entry) | ||
Simon Sapin
|
r47877 | } else { | ||
Simon Sapin
|
r47963 | debug_assert!(!old_state.is_tracked()); | ||
Simon Sapin
|
r47877 | Ok(false) | ||
} | ||||
Simon Sapin
|
r47865 | } | ||
Simon Sapin
|
r47875 | fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) { | ||
for filename in filenames { | ||||
if let Some(node) = Self::get_node_mut(&mut self.root, &filename) { | ||||
if let Some(entry) = node.entry.as_mut() { | ||||
Simon Sapin
|
r48121 | entry.clear_ambiguous_mtime(now); | ||
Simon Sapin
|
r47875 | } | ||
} | ||||
} | ||||
Simon Sapin
|
r47865 | } | ||
Simon Sapin
|
r47878 | fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool { | ||
self.get_node(key) | ||||
Simon Sapin
|
r48124 | .and_then(|node| node.entry()) | ||
.map_or(false, |entry| entry.is_non_normal()) | ||||
Simon Sapin
|
r47865 | } | ||
Simon Sapin
|
r47878 | fn non_normal_entries_remove(&mut self, _key: &HgPath) { | ||
// Do nothing, this `DirstateMap` does not have a separate "non normal | ||||
// entries" set that need to be kept up to date | ||||
Simon Sapin
|
r47865 | } | ||
fn non_normal_or_other_parent_paths( | ||||
&mut self, | ||||
Simon Sapin
|
r47894 | ) -> Box<dyn Iterator<Item = &HgPath> + '_> { | ||
Simon Sapin
|
r48124 | Box::new(self.iter_nodes().filter_map(|node| { | ||
node.entry() | ||||
Simon Sapin
|
r47878 | .filter(|entry| { | ||
entry.is_non_normal() || entry.is_from_other_parent() | ||||
}) | ||||
Simon Sapin
|
r48124 | .map(|_| node.full_path()) | ||
Simon Sapin
|
r47878 | })) | ||
Simon Sapin
|
r47865 | } | ||
fn set_non_normal_other_parent_entries(&mut self, _force: bool) { | ||||
Simon Sapin
|
r47878 | // Do nothing, this `DirstateMap` does not have a separate "non normal | ||
// entries" and "from other parent" sets that need to be recomputed | ||||
Simon Sapin
|
r47865 | } | ||
fn iter_non_normal_paths( | ||||
&mut self, | ||||
Simon Sapin
|
r47894 | ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> { | ||
Simon Sapin
|
r47878 | self.iter_non_normal_paths_panic() | ||
Simon Sapin
|
r47865 | } | ||
fn iter_non_normal_paths_panic( | ||||
&self, | ||||
Simon Sapin
|
r47894 | ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> { | ||
Simon Sapin
|
r48124 | Box::new(self.iter_nodes().filter_map(|node| { | ||
node.entry() | ||||
Simon Sapin
|
r47878 | .filter(|entry| entry.is_non_normal()) | ||
Simon Sapin
|
r48124 | .map(|_| node.full_path()) | ||
Simon Sapin
|
r47878 | })) | ||
Simon Sapin
|
r47865 | } | ||
fn iter_other_parent_paths( | ||||
&mut self, | ||||
Simon Sapin
|
r47894 | ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> { | ||
Simon Sapin
|
r48124 | Box::new(self.iter_nodes().filter_map(|node| { | ||
node.entry() | ||||
Simon Sapin
|
r47878 | .filter(|entry| entry.is_from_other_parent()) | ||
Simon Sapin
|
r48124 | .map(|_| node.full_path()) | ||
Simon Sapin
|
r47878 | })) | ||
Simon Sapin
|
r47865 | } | ||
fn has_tracked_dir( | ||||
&mut self, | ||||
Simon Sapin
|
r47876 | directory: &HgPath, | ||
Simon Sapin
|
r47865 | ) -> Result<bool, DirstateMapError> { | ||
Simon Sapin
|
r47876 | if let Some(node) = self.get_node(directory) { | ||
// A node without a `DirstateEntry` was created to hold child | ||||
// nodes, and is therefore a directory. | ||||
Simon Sapin
|
r48124 | Ok(!node.has_entry() && node.tracked_descendants_count() > 0) | ||
Simon Sapin
|
r47876 | } else { | ||
Ok(false) | ||||
} | ||||
Simon Sapin
|
r47865 | } | ||
fn has_dir( | ||||
&mut self, | ||||
Simon Sapin
|
r47876 | directory: &HgPath, | ||
Simon Sapin
|
r47865 | ) -> Result<bool, DirstateMapError> { | ||
Simon Sapin
|
r47876 | if let Some(node) = self.get_node(directory) { | ||
// A node without a `DirstateEntry` was created to hold child | ||||
// nodes, and is therefore a directory. | ||||
Simon Sapin
|
r48124 | Ok(!node.has_entry()) | ||
Simon Sapin
|
r47876 | } else { | ||
Ok(false) | ||||
} | ||||
Simon Sapin
|
r47865 | } | ||
Simon Sapin
|
r48055 | #[timed] | ||
fn pack_v1( | ||||
Simon Sapin
|
r47865 | &mut self, | ||
Simon Sapin
|
r47872 | parents: DirstateParents, | ||
now: Timestamp, | ||||
Simon Sapin
|
r47865 | ) -> Result<Vec<u8>, DirstateError> { | ||
Simon Sapin
|
r48121 | let now: i32 = now.0.try_into().expect("time overflow"); | ||
let mut ambiguous_mtimes = Vec::new(); | ||||
Simon Sapin
|
r47872 | // Optizimation (to be measured?): pre-compute size to avoid `Vec` | ||
// reallocations | ||||
let mut size = parents.as_bytes().len(); | ||||
Simon Sapin
|
r48124 | for node in self.iter_nodes() { | ||
if let Some(entry) = node.entry() { | ||||
size += | ||||
packed_entry_size(node.full_path(), node.copy_source()); | ||||
Simon Sapin
|
r48121 | if entry.mtime_is_ambiguous(now) { | ||
Simon Sapin
|
r48124 | ambiguous_mtimes.push(node.full_path_cow()) | ||
Simon Sapin
|
r48121 | } | ||
Simon Sapin
|
r47872 | } | ||
} | ||||
Simon Sapin
|
r48121 | self.clear_known_ambiguous_mtimes(&ambiguous_mtimes); | ||
Simon Sapin
|
r47872 | |||
let mut packed = Vec::with_capacity(size); | ||||
packed.extend(parents.as_bytes()); | ||||
Simon Sapin
|
r48124 | for node in self.iter_nodes() { | ||
if let Some(entry) = node.entry() { | ||||
Simon Sapin
|
r47872 | pack_entry( | ||
Simon Sapin
|
r48124 | node.full_path(), | ||
&entry, | ||||
node.copy_source(), | ||||
Simon Sapin
|
r47872 | &mut packed, | ||
); | ||||
} | ||||
} | ||||
Ok(packed) | ||||
Simon Sapin
|
r47865 | } | ||
Simon Sapin
|
r48055 | #[timed] | ||
fn pack_v2( | ||||
&mut self, | ||||
parents: DirstateParents, | ||||
now: Timestamp, | ||||
) -> Result<Vec<u8>, DirstateError> { | ||||
Simon Sapin
|
r48121 | // TODO:Â how do we want to handle this in 2038? | ||
let now: i32 = now.0.try_into().expect("time overflow"); | ||||
let mut paths = Vec::new(); | ||||
Simon Sapin
|
r48124 | for node in self.iter_nodes() { | ||
if let Some(entry) = node.entry() { | ||||
Simon Sapin
|
r48121 | if entry.mtime_is_ambiguous(now) { | ||
Simon Sapin
|
r48124 | paths.push(node.full_path_cow()) | ||
Simon Sapin
|
r48121 | } | ||
} | ||||
} | ||||
// Borrow of `self` ends here since we collect cloned paths | ||||
self.clear_known_ambiguous_mtimes(&paths); | ||||
on_disk::write(self, parents) | ||||
Simon Sapin
|
r48055 | } | ||
Simon Sapin
|
r47865 | fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> { | ||
Simon Sapin
|
r47876 | // Do nothing, this `DirstateMap` does not a separate `all_dirs` that | ||
// needs to be recomputed | ||||
Ok(()) | ||||
Simon Sapin
|
r47865 | } | ||
fn set_dirs(&mut self) -> Result<(), DirstateMapError> { | ||||
Simon Sapin
|
r47876 | // Do nothing, this `DirstateMap` does not a separate `dirs` that needs | ||
// to be recomputed | ||||
Ok(()) | ||||
Simon Sapin
|
r47865 | } | ||
fn status<'a>( | ||||
Simon Sapin
|
r47882 | &'a mut self, | ||
matcher: &'a (dyn Matcher + Sync), | ||||
root_dir: PathBuf, | ||||
ignore_files: Vec<PathBuf>, | ||||
options: StatusOptions, | ||||
Simon Sapin
|
r47880 | ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError> | ||
{ | ||||
Simon Sapin
|
r47882 | super::status::status(self, matcher, root_dir, ignore_files, options) | ||
Simon Sapin
|
r47865 | } | ||
fn copy_map_len(&self) -> usize { | ||||
Simon Sapin
|
r48058 | self.nodes_with_copy_source_count as usize | ||
Simon Sapin
|
r47865 | } | ||
fn copy_map_iter(&self) -> CopyMapIter<'_> { | ||||
Simon Sapin
|
r48124 | Box::new(self.iter_nodes().filter_map(|node| { | ||
node.copy_source() | ||||
.map(|copy_source| (node.full_path(), copy_source)) | ||||
Simon Sapin
|
r47870 | })) | ||
Simon Sapin
|
r47865 | } | ||
Simon Sapin
|
r47869 | fn copy_map_contains_key(&self, key: &HgPath) -> bool { | ||
if let Some(node) = self.get_node(key) { | ||||
Simon Sapin
|
r48124 | node.copy_source().is_some() | ||
Simon Sapin
|
r47869 | } else { | ||
false | ||||
} | ||||
Simon Sapin
|
r47865 | } | ||
Simon Sapin
|
r47894 | fn copy_map_get(&self, key: &HgPath) -> Option<&HgPath> { | ||
Simon Sapin
|
r48124 | self.get_node(key)?.copy_source() | ||
Simon Sapin
|
r47865 | } | ||
Simon Sapin
|
r47874 | fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> { | ||
let count = &mut self.nodes_with_copy_source_count; | ||||
Self::get_node_mut(&mut self.root, key).and_then(|node| { | ||||
if node.copy_source.is_some() { | ||||
*count -= 1 | ||||
} | ||||
Simon Sapin
|
r47895 | node.copy_source.take().map(Cow::into_owned) | ||
Simon Sapin
|
r47874 | }) | ||
Simon Sapin
|
r47865 | } | ||
fn copy_map_insert( | ||||
&mut self, | ||||
Simon Sapin
|
r47874 | key: HgPathBuf, | ||
value: HgPathBuf, | ||||
Simon Sapin
|
r47865 | ) -> Option<HgPathBuf> { | ||
Simon Sapin
|
r47896 | let node = Self::get_or_insert_node( | ||
&mut self.root, | ||||
&key, | ||||
WithBasename::to_cow_owned, | ||||
|_ancestor| {}, | ||||
); | ||||
Simon Sapin
|
r47874 | if node.copy_source.is_none() { | ||
self.nodes_with_copy_source_count += 1 | ||||
} | ||||
Simon Sapin
|
r47895 | node.copy_source.replace(value.into()).map(Cow::into_owned) | ||
Simon Sapin
|
r47865 | } | ||
fn len(&self) -> usize { | ||||
Simon Sapin
|
r48058 | self.nodes_with_entry_count as usize | ||
Simon Sapin
|
r47865 | } | ||
Simon Sapin
|
r47869 | fn contains_key(&self, key: &HgPath) -> bool { | ||
self.get(key).is_some() | ||||
Simon Sapin
|
r47865 | } | ||
Simon Sapin
|
r48123 | fn get(&self, key: &HgPath) -> Option<DirstateEntry> { | ||
Simon Sapin
|
r48124 | self.get_node(key)?.entry() | ||
Simon Sapin
|
r47865 | } | ||
fn iter(&self) -> StateMapIter<'_> { | ||||
Simon Sapin
|
r48124 | Box::new(self.iter_nodes().filter_map(|node| { | ||
node.entry().map(|entry| (node.full_path(), entry)) | ||||
Simon Sapin
|
r47870 | })) | ||
Simon Sapin
|
r47865 | } | ||
} | ||||