##// END OF EJS Templates
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
dirstate-tree: Remove DirstateMap::iter_node_data_mut In an upcoming changeset we want DirstateMap to be able to work directly with nodes in their "on disk" representation, without always allocating corresponding in-memory data structures. Nodes would have two possible representations: one immutable "on disk" refering to the bytes buffer of the contents of the .hg/dirstate file, and one mutable with HashMap like the curren data structure. These nodes would have copy-on-write semantics: when an immutable node would need to be mutated, instead we allocate new mutable node for it and its ancestors. A mutable iterator of the entire tree would still be possible, but it would become much more expensive since we’d need to allocate mutable nodes for everything. Instead, remove this iterator. It was only used to clear ambiguous mtimes while serializing the `DirstateMap`. Instead clearing and serialization are now two separate passes. Clearing first uses an immutable iterator to collect the paths of nodes that need to be cleared, then accesses only those nodes mutably. Differential Revision: https://phab.mercurial-scm.org/D10744

File last commit:

r48121:73f23e76 default
r48121:73f23e76 default
Show More
dirstate_map.rs
639 lines | 20.4 KiB | application/rls-services+xml | RustLexer
Simon Sapin
dirstate-tree: Serialize to disk...
r47872 use bytes_cast::BytesCast;
Simon Sapin
dirstate-tree: Add #[timed] attribute to `status` and `DirstateMap::read`...
r47888 use micro_timer::timed;
Simon Sapin
dirstate-tree: Borrow copy source paths from the "on disk" bytes...
r47895 use std::borrow::Cow;
Simon Sapin
dirstate-tree: Use HashMap instead of BTreeMap...
r47889 use std::convert::TryInto;
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 use std::path::PathBuf;
Simon Sapin
dirstate-v2: Change the on-disk format to be tree-shaped...
r48058 use super::on_disk;
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 use super::path_with_basename::WithBasename;
Simon Sapin
dirstate-tree: Serialize to disk...
r47872 use crate::dirstate::parsers::pack_entry;
use crate::dirstate::parsers::packed_entry_size;
Simon Sapin
dirstate-tree: Add parsing only dirstate parents from disk...
r47868 use crate::dirstate::parsers::parse_dirstate_entries;
Simon Sapin
rust: Add a Timestamp struct instead of abusing Duration...
r47871 use crate::dirstate::parsers::Timestamp;
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
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
dirstate-tree: Use HashMap instead of BTreeMap...
r47889 use crate::FastHashMap;
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 use crate::PatternFileWarning;
use crate::StateMapIter;
use crate::StatusError;
use crate::StatusOptions;
Simon Sapin
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
r47893 pub struct DirstateMap<'on_disk> {
/// Contents of the `.hg/dirstate` file
Simon Sapin
dirstate-v2: Change the on-disk format to be tree-shaped...
r48058 pub(super) on_disk: &'on_disk [u8],
Simon Sapin
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
r47893
Simon Sapin
dirstate-tree: Borrow copy source paths from the "on disk" bytes...
r47895 pub(super) root: ChildNodes<'on_disk>,
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873
/// Number of nodes anywhere in the tree that have `.entry.is_some()`.
Simon Sapin
dirstate-v2: Change the on-disk format to be tree-shaped...
r48058 pub(super) nodes_with_entry_count: u32,
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873
/// Number of nodes anywhere in the tree that have
/// `.copy_source.is_some()`.
Simon Sapin
dirstate-v2: Change the on-disk format to be tree-shaped...
r48058 pub(super) nodes_with_copy_source_count: u32,
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
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
dirstate-tree: Extract into a method sorting children of a given node...
r48057 /// names. However `HashMap` would waste time always re-hashing the same
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 /// string prefix.
Simon Sapin
dirstate-tree: Extract into a method sorting children of a given node...
r48057 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
Simon Sapin
dirstate-tree: Borrow copy source paths from the "on disk" bytes...
r47895 pub(super) type ChildNodes<'on_disk> =
Simon Sapin
dirstate-tree: Extract into a method sorting children of a given node...
r48057 FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>;
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 /// Represents a file or a directory
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 #[derive(Default)]
Simon Sapin
dirstate-tree: Borrow copy source paths from the "on disk" bytes...
r47895 pub(super) struct Node<'on_disk> {
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 /// `None` for directories
Simon Sapin
dirstate-tree: Add the new `status()` algorithm...
r47883 pub(super) entry: Option<DirstateEntry>,
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876
Simon Sapin
dirstate-tree: Borrow copy source paths from the "on disk" bytes...
r47895 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876
Simon Sapin
dirstate-tree: Borrow copy source paths from the "on disk" bytes...
r47895 pub(super) children: ChildNodes<'on_disk>,
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876
/// How many (non-inclusive) descendants of this node are tracked files
Simon Sapin
dirstate-v2: Change the on-disk format to be tree-shaped...
r48058 pub(super) tracked_descendants_count: u32,
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 }
Simon Sapin
dirstate-tree: Extract into a method sorting children of a given node...
r48057 impl<'on_disk> Node<'on_disk> {
Simon Sapin
dirstate-tree: Add the new `status()` algorithm...
r47883 pub(super) fn state(&self) -> Option<EntryState> {
self.entry.as_ref().map(|entry| entry.state)
}
Simon Sapin
dirstate-tree: Extract into a method sorting children of a given node...
r48057
pub(super) fn sorted<'tree>(
nodes: &'tree mut ChildNodes<'on_disk>,
) -> Vec<(&'tree NodeKey<'on_disk>, &'tree mut Self)> {
let mut vec: Vec<_> = nodes.iter_mut().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(|(path1, _), (path2, _)| path1.cmp(path2));
vec
}
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
r47893 impl<'on_disk> DirstateMap<'on_disk> {
Simon Sapin
dirstate-v2: Change the on-disk format to be tree-shaped...
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
dirstate-v2: Change the on-disk format when the requirement is enabled...
r48055 #[timed]
pub fn new_v2(
on_disk: &'on_disk [u8],
) -> Result<(Self, Option<DirstateParents>), DirstateError> {
Simon Sapin
dirstate-v2: Change the on-disk format to be tree-shaped...
r48058 on_disk::read(on_disk)
Simon Sapin
dirstate-v2: Change the on-disk format when the requirement is enabled...
r48055 }
#[timed]
pub fn new_v1(
Simon Sapin
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
r47893 on_disk: &'on_disk [u8],
) -> Result<(Self, Option<DirstateParents>), DirstateError> {
Simon Sapin
dirstate-v2: Change the on-disk format to be tree-shaped...
r48058 let mut map = Self::empty(on_disk);
Simon Sapin
dirstate-v2: Change the on-disk format when the requirement is enabled...
r48055 if map.on_disk.is_empty() {
return Ok((map, None));
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 }
Simon Sapin
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
r47893
let parents = parse_dirstate_entries(
Simon Sapin
dirstate-v2: Change the on-disk format when the requirement is enabled...
r48055 map.on_disk,
Simon Sapin
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
r47893 |path, entry, copy_source| {
let tracked = entry.state.is_tracked();
Simon Sapin
dirstate-tree: Borrow paths from the "on disk" bytes...
r47896 let node = Self::get_or_insert_node(
Simon Sapin
dirstate-v2: Change the on-disk format when the requirement is enabled...
r48055 &mut map.root,
Simon Sapin
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
r47893 path,
Simon Sapin
dirstate-tree: Borrow paths from the "on disk" bytes...
r47896 WithBasename::to_cow_borrowed,
Simon Sapin
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
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
dirstate-tree: Borrow copy source paths from the "on disk" bytes...
r47895 node.copy_source = copy_source.map(Cow::Borrowed);
Simon Sapin
dirstate-v2: Change the on-disk format when the requirement is enabled...
r48055 map.nodes_with_entry_count += 1;
Simon Sapin
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
r47893 if copy_source.is_some() {
Simon Sapin
dirstate-v2: Change the on-disk format when the requirement is enabled...
r48055 map.nodes_with_copy_source_count += 1
Simon Sapin
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
r47893 }
},
)?;
Simon Sapin
dirstate-v2: Change the on-disk format when the requirement is enabled...
r48055 let parents = Some(parents.clone());
Simon Sapin
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
r47893
Simon Sapin
dirstate-v2: Change the on-disk format when the requirement is enabled...
r48055 Ok((map, parents))
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 }
Simon Sapin
dirstate-tree: Add map `get` and `contains_key` methods...
r47869 fn get_node(&self, path: &HgPath) -> Option<&Node> {
let mut children = &self.root;
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;
children = &child.children;
} else {
return Some(child);
}
}
}
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 /// Returns a mutable reference to the node at `path` if it exists
///
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 /// This takes `root` instead of `&mut self` so that callers can mutate
/// other fields while the returned borrow is still valid
Simon Sapin
dirstate-tree: Add copy_map_insert and copy_map_remove...
r47874 fn get_node_mut<'tree>(
Simon Sapin
dirstate-tree: Borrow copy source paths from the "on disk" bytes...
r47895 root: &'tree mut ChildNodes<'on_disk>,
Simon Sapin
dirstate-tree: Add copy_map_insert and copy_map_remove...
r47874 path: &HgPath,
Simon Sapin
dirstate-tree: Borrow copy source paths from the "on disk" bytes...
r47895 ) -> Option<&'tree mut Node<'on_disk>> {
Simon Sapin
dirstate-tree: Add copy_map_insert and copy_map_remove...
r47874 let mut children = root;
let mut components = path.components();
let mut component =
components.next().expect("expected at least one components");
loop {
let child = children.get_mut(component)?;
if let Some(next_component) = components.next() {
component = next_component;
children = &mut child.children;
} else {
return Some(child);
}
}
}
Simon Sapin
dirstate-tree: Borrow paths from the "on disk" bytes...
r47896 fn get_or_insert_node<'tree, 'path>(
Simon Sapin
dirstate-tree: Borrow copy source paths from the "on disk" bytes...
r47895 root: &'tree mut ChildNodes<'on_disk>,
Simon Sapin
dirstate-tree: Borrow paths from the "on disk" bytes...
r47896 path: &'path HgPath,
to_cow: impl Fn(
WithBasename<&'path HgPath>,
) -> WithBasename<Cow<'on_disk, HgPath>>,
Simon Sapin
dirstate-tree: Fold "tracked descendants" counter update in main walk...
r47890 mut each_ancestor: impl FnMut(&mut Node),
Simon Sapin
dirstate-tree: Borrow copy source paths from the "on disk" bytes...
r47895 ) -> &'tree mut Node<'on_disk> {
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 let mut child_nodes = root;
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
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
dirstate-tree: Avoid BTreeMap double-lookup when inserting a dirstate entry...
r47886 // TODO: can we avoid allocating an owned key in cases where the
// map already contains that key, without introducing double
// lookup?
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 let child_node =
Simon Sapin
dirstate-tree: Borrow paths from the "on disk" bytes...
r47896 child_nodes.entry(to_cow(ancestor_path)).or_default();
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 if let Some(next) = inclusive_ancestor_paths.next() {
Simon Sapin
dirstate-tree: Fold "tracked descendants" counter update in main walk...
r47890 each_ancestor(child_node);
Simon Sapin
dirstate-tree: Implement DirstateMap::read...
r47867 ancestor_path = next;
child_nodes = &mut child_node.children;
} else {
return child_node;
}
}
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add tree traversal/iteration...
r47870
Simon Sapin
dirstate-tree: Fold "tracked descendants" counter update in main walk...
r47890 fn add_or_remove_file(
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 &mut self,
path: &HgPath,
Simon Sapin
dirstate-tree: Fold "tracked descendants" counter update in main walk...
r47890 old_state: EntryState,
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 new_entry: DirstateEntry,
) {
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 let tracked_count_increment =
Simon Sapin
dirstate-tree: Fold "tracked descendants" counter update in main walk...
r47890 match (old_state.is_tracked(), new_entry.state.is_tracked()) {
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 (false, true) => 1,
(true, false) => -1,
_ => 0,
};
Simon Sapin
dirstate-tree: Borrow paths from the "on disk" bytes...
r47896 let node = Self::get_or_insert_node(
Simon Sapin
dirstate-tree: Fold "tracked descendants" counter update in main walk...
r47890 &mut self.root,
path,
Simon Sapin
dirstate-tree: Borrow paths from the "on disk" bytes...
r47896 WithBasename::to_cow_owned,
Simon Sapin
dirstate-tree: Fold "tracked descendants" counter update in main walk...
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
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 }
Simon Sapin
dirstate-tree: Fold "tracked descendants" counter update in main walk...
r47890 node.entry = Some(new_entry)
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 }
Simon Sapin
dirstate-tree: Add tree traversal/iteration...
r47870 fn iter_nodes<'a>(
&'a self,
Simon Sapin
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
r48121 ) -> impl Iterator<Item = (&'a Cow<'on_disk, HgPath>, &'a Node)> + 'a {
Simon Sapin
dirstate-tree: Add tree traversal/iteration...
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();
let mut iter = self.root.iter();
std::iter::from_fn(move || {
while let Some((key, child_node)) = iter.next() {
// Pseudo-recursion
let new_iter = child_node.children.iter();
let old_iter = std::mem::replace(&mut iter, new_iter);
Simon Sapin
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
r48121 let key = key.full_path();
Simon Sapin
dirstate-tree: Add tree traversal/iteration...
r47870 stack.push((key, child_node, old_iter));
}
// Found the end of a `children.iter()` iterator.
if let Some((key, child_node, next_iter)) = stack.pop() {
// "Return" from pseudo-recursion by restoring state from the
// explicit stack
iter = next_iter;
Some((key, child_node))
} else {
// Reached the bottom of the stack, we’re done
None
}
})
}
Simon Sapin
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
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
dirstate-tree: Add tree traversal/iteration...
r47870 }
Simon Sapin
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
r48121 }
Simon Sapin
dirstate-tree: Add tree traversal/iteration...
r47870 }
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
r47893 impl<'on_disk> super::dispatch::DirstateMapMethods for DirstateMap<'on_disk> {
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 fn clear(&mut self) {
Simon Sapin
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
r47873 self.root.clear();
self.nodes_with_entry_count = 0;
self.nodes_with_copy_source_count = 0;
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn add_file(
&mut self,
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 filename: &HgPath,
Simon Sapin
dirstate-tree: Fold "tracked descendants" counter update in main walk...
r47890 old_state: EntryState,
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 entry: DirstateEntry,
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 ) -> Result<(), DirstateMapError> {
Simon Sapin
dirstate-tree: Fold "tracked descendants" counter update in main walk...
r47890 self.add_or_remove_file(filename, old_state, entry);
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 Ok(())
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn remove_file(
&mut self,
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 filename: &HgPath,
Simon Sapin
dirstate-tree: Fold "tracked descendants" counter update in main walk...
r47890 old_state: EntryState,
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 size: i32,
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 ) -> Result<(), DirstateMapError> {
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 let entry = DirstateEntry {
state: EntryState::Removed,
mode: 0,
size,
mtime: 0,
};
Simon Sapin
dirstate-tree: Fold "tracked descendants" counter update in main walk...
r47890 self.add_or_remove_file(filename, old_state, entry);
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 Ok(())
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn drop_file(
&mut self,
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 filename: &HgPath,
Simon Sapin
dirstate-tree: Fold "tracked descendants" counter update in main walk...
r47890 old_state: EntryState,
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 ) -> Result<bool, DirstateMapError> {
Simon Sapin
dirstate-tree: Refactor DirstateMap::drop_file to be recursive...
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();
let node = nodes.get_mut(first_path_component)?;
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
dirstate-tree: Fold "tracked descendants" counter update in main walk...
r47890 }
Simon Sapin
dirstate-tree: Refactor DirstateMap::drop_file to be recursive...
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
dirstate-tree: Remove newly-empty nodes after removing a `DirstateEntry`...
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()
{
nodes.remove(first_path_component);
Simon Sapin
dirstate-tree: Refactor DirstateMap::drop_file to be recursive...
r47963 }
Some(dropped)
}
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877
Simon Sapin
dirstate-tree: Refactor DirstateMap::drop_file to be recursive...
r47963 if let Some(dropped) = recur(&mut self.root, filename) {
if dropped.had_entry {
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 self.nodes_with_entry_count -= 1
}
Simon Sapin
dirstate-tree: Refactor DirstateMap::drop_file to be recursive...
r47963 if dropped.had_copy_source {
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 self.nodes_with_copy_source_count -= 1
}
Simon Sapin
dirstate-tree: Refactor DirstateMap::drop_file to be recursive...
r47963 Ok(dropped.had_entry)
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 } else {
Simon Sapin
dirstate-tree: Refactor DirstateMap::drop_file to be recursive...
r47963 debug_assert!(!old_state.is_tracked());
Simon Sapin
dirstate-tree: Add add_file, remove_file, and drop_file...
r47877 Ok(false)
}
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add clear_ambiguous_times in the new DirstateMap...
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
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
r48121 entry.clear_ambiguous_mtime(now);
Simon Sapin
dirstate-tree: Add clear_ambiguous_times in the new DirstateMap...
r47875 }
}
}
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool {
self.get_node(key)
.and_then(|node| node.entry.as_ref())
.map_or(false, DirstateEntry::is_non_normal)
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
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
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn non_normal_or_other_parent_paths(
&mut self,
Simon Sapin
rust: Use `&HgPath` instead of `&HgPathBuf` in may APIs...
r47894 ) -> Box<dyn Iterator<Item = &HgPath> + '_> {
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 Box::new(self.iter_nodes().filter_map(|(path, node)| {
node.entry
.as_ref()
.filter(|entry| {
entry.is_non_normal() || entry.is_from_other_parent()
})
Simon Sapin
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
r48121 .map(|_| &**path)
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 }))
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn set_non_normal_other_parent_entries(&mut self, _force: bool) {
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
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
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn iter_non_normal_paths(
&mut self,
Simon Sapin
rust: Use `&HgPath` instead of `&HgPathBuf` in may APIs...
r47894 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 self.iter_non_normal_paths_panic()
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn iter_non_normal_paths_panic(
&self,
Simon Sapin
rust: Use `&HgPath` instead of `&HgPathBuf` in may APIs...
r47894 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 Box::new(self.iter_nodes().filter_map(|(path, node)| {
node.entry
.as_ref()
.filter(|entry| entry.is_non_normal())
Simon Sapin
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
r48121 .map(|_| &**path)
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 }))
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn iter_other_parent_paths(
&mut self,
Simon Sapin
rust: Use `&HgPath` instead of `&HgPathBuf` in may APIs...
r47894 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 Box::new(self.iter_nodes().filter_map(|(path, node)| {
node.entry
.as_ref()
.filter(|entry| entry.is_from_other_parent())
Simon Sapin
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
r48121 .map(|_| &**path)
Simon Sapin
dirstate-tree: Add "non normal" and "from other parent" sets...
r47878 }))
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn has_tracked_dir(
&mut self,
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 directory: &HgPath,
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 ) -> Result<bool, DirstateMapError> {
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
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.
Ok(node.entry.is_none() && node.tracked_descendants_count > 0)
} else {
Ok(false)
}
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn has_dir(
&mut self,
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 directory: &HgPath,
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 ) -> Result<bool, DirstateMapError> {
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
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.
Ok(node.entry.is_none())
} else {
Ok(false)
}
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-v2: Change the on-disk format when the requirement is enabled...
r48055 #[timed]
fn pack_v1(
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 &mut self,
Simon Sapin
dirstate-tree: Serialize to disk...
r47872 parents: DirstateParents,
now: Timestamp,
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 ) -> Result<Vec<u8>, DirstateError> {
Simon Sapin
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
r48121 let now: i32 = now.0.try_into().expect("time overflow");
let mut ambiguous_mtimes = Vec::new();
Simon Sapin
dirstate-tree: Serialize to disk...
r47872 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
// reallocations
let mut size = parents.as_bytes().len();
for (path, node) in self.iter_nodes() {
Simon Sapin
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
r48121 if let Some(entry) = &node.entry {
Simon Sapin
dirstate-tree: Serialize to disk...
r47872 size += packed_entry_size(
Simon Sapin
rust: Use `&HgPath` instead of `&HgPathBuf` in may APIs...
r47894 path,
node.copy_source.as_ref().map(|p| &**p),
Simon Sapin
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
r48121 );
if entry.mtime_is_ambiguous(now) {
ambiguous_mtimes.push(path.clone())
}
Simon Sapin
dirstate-tree: Serialize to disk...
r47872 }
}
Simon Sapin
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
r48121 self.clear_known_ambiguous_mtimes(&ambiguous_mtimes);
Simon Sapin
dirstate-tree: Serialize to disk...
r47872
let mut packed = Vec::with_capacity(size);
packed.extend(parents.as_bytes());
Simon Sapin
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
r48121 for (path, node) in self.iter_nodes() {
if let Some(entry) = &node.entry {
Simon Sapin
dirstate-tree: Serialize to disk...
r47872 pack_entry(
Simon Sapin
rust: Use `&HgPath` instead of `&HgPathBuf` in may APIs...
r47894 path,
Simon Sapin
dirstate-tree: Serialize to disk...
r47872 entry,
Simon Sapin
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
r48121 node.copy_source.as_ref().map(|p| &**p),
Simon Sapin
dirstate-tree: Serialize to disk...
r47872 &mut packed,
);
}
}
Ok(packed)
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-v2: Change the on-disk format when the requirement is enabled...
r48055 #[timed]
fn pack_v2(
&mut self,
parents: DirstateParents,
now: Timestamp,
) -> Result<Vec<u8>, DirstateError> {
Simon Sapin
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
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();
for (path, node) in self.iter_nodes() {
if let Some(entry) = &node.entry {
if entry.mtime_is_ambiguous(now) {
paths.push(path.clone())
}
}
}
// Borrow of `self` ends here since we collect cloned paths
self.clear_known_ambiguous_mtimes(&paths);
on_disk::write(self, parents)
Simon Sapin
dirstate-v2: Change the on-disk format when the requirement is enabled...
r48055 }
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 // Do nothing, this `DirstateMap` does not a separate `all_dirs` that
// needs to be recomputed
Ok(())
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
Simon Sapin
dirstate-tree: Add has_dir and has_tracked_dir...
r47876 // Do nothing, this `DirstateMap` does not a separate `dirs` that needs
// to be recomputed
Ok(())
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn status<'a>(
Simon Sapin
dirstate-tree: Give to `status()` mutable access to the `DirstateMap`...
r47882 &'a mut self,
matcher: &'a (dyn Matcher + Sync),
root_dir: PathBuf,
ignore_files: Vec<PathBuf>,
options: StatusOptions,
Simon Sapin
rust: Move "lookup" a.k.a. "unsure" paths into `DirstateStatus` struct...
r47880 ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>
{
Simon Sapin
dirstate-tree: Give to `status()` mutable access to the `DirstateMap`...
r47882 super::status::status(self, matcher, root_dir, ignore_files, options)
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn copy_map_len(&self) -> usize {
Simon Sapin
dirstate-v2: Change the on-disk format to be tree-shaped...
r48058 self.nodes_with_copy_source_count as usize
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn copy_map_iter(&self) -> CopyMapIter<'_> {
Simon Sapin
dirstate-tree: Add tree traversal/iteration...
r47870 Box::new(self.iter_nodes().filter_map(|(path, node)| {
node.copy_source
.as_ref()
Simon Sapin
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
r48121 .map(|copy_source| (&**path, &**copy_source))
Simon Sapin
dirstate-tree: Add tree traversal/iteration...
r47870 }))
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add map `get` and `contains_key` methods...
r47869 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
if let Some(node) = self.get_node(key) {
node.copy_source.is_some()
} else {
false
}
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
rust: Use `&HgPath` instead of `&HgPathBuf` in may APIs...
r47894 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPath> {
self.get_node(key)?.copy_source.as_ref().map(|p| &**p)
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add copy_map_insert and copy_map_remove...
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
dirstate-tree: Borrow copy source paths from the "on disk" bytes...
r47895 node.copy_source.take().map(Cow::into_owned)
Simon Sapin
dirstate-tree: Add copy_map_insert and copy_map_remove...
r47874 })
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn copy_map_insert(
&mut self,
Simon Sapin
dirstate-tree: Add copy_map_insert and copy_map_remove...
r47874 key: HgPathBuf,
value: HgPathBuf,
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 ) -> Option<HgPathBuf> {
Simon Sapin
dirstate-tree: Borrow paths from the "on disk" bytes...
r47896 let node = Self::get_or_insert_node(
&mut self.root,
&key,
WithBasename::to_cow_owned,
|_ancestor| {},
);
Simon Sapin
dirstate-tree: Add copy_map_insert and copy_map_remove...
r47874 if node.copy_source.is_none() {
self.nodes_with_copy_source_count += 1
}
Simon Sapin
dirstate-tree: Borrow copy source paths from the "on disk" bytes...
r47895 node.copy_source.replace(value.into()).map(Cow::into_owned)
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn len(&self) -> usize {
Simon Sapin
dirstate-v2: Change the on-disk format to be tree-shaped...
r48058 self.nodes_with_entry_count as usize
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add map `get` and `contains_key` methods...
r47869 fn contains_key(&self, key: &HgPath) -> bool {
self.get(key).is_some()
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
Simon Sapin
dirstate-tree: Add map `get` and `contains_key` methods...
r47869 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
self.get_node(key)?.entry.as_ref()
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
fn iter(&self) -> StateMapIter<'_> {
Simon Sapin
dirstate-tree: Add tree traversal/iteration...
r47870 Box::new(self.iter_nodes().filter_map(|(path, node)| {
Simon Sapin
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
r48121 node.entry.as_ref().map(|entry| (&**path, entry))
Simon Sapin
dirstate-tree: Add tree traversal/iteration...
r47870 }))
Simon Sapin
dirstate-tree: Empty shell for a second Rust DirstateMap implementation...
r47865 }
}