diff --git a/mercurial/configitems.py b/mercurial/configitems.py --- a/mercurial/configitems.py +++ b/mercurial/configitems.py @@ -964,11 +964,6 @@ ) coreconfigitem( b'experimental', - b'dirstate-tree.in-memory', - default=False, -) -coreconfigitem( - b'experimental', b'editortmpinhg', default=False, ) diff --git a/mercurial/dirstatemap.py b/mercurial/dirstatemap.py --- a/mercurial/dirstatemap.py +++ b/mercurial/dirstatemap.py @@ -493,12 +493,6 @@ # for consistent view between _pl() and _read() invocations self._pendingmode = None - self._use_dirstate_tree = self._ui.configbool( - b"experimental", - b"dirstate-tree.in-memory", - False, - ) - def addfile( self, f, @@ -818,7 +812,7 @@ parents = self.docket.parents else: self._rustmap, parents = rustmod.DirstateMap.new_v1( - self._use_dirstate_tree, self._readdirstatefile() + self._readdirstatefile() ) if parents and not self._dirtyparents: diff --git a/mercurial/upgrade_utils/engine.py b/mercurial/upgrade_utils/engine.py --- a/mercurial/upgrade_utils/engine.py +++ b/mercurial/upgrade_utils/engine.py @@ -638,7 +638,6 @@ ) assert srcrepo.dirstate._use_dirstate_v2 == (old == b'v2') - srcrepo.dirstate._map._use_dirstate_tree = True srcrepo.dirstate._map.preload() srcrepo.dirstate._use_dirstate_v2 = new == b'v2' srcrepo.dirstate._map._use_dirstate_v2 = srcrepo.dirstate._use_dirstate_v2 diff --git a/rust/hg-core/src/dirstate.rs b/rust/hg-core/src/dirstate.rs --- a/rust/hg-core/src/dirstate.rs +++ b/rust/hg-core/src/dirstate.rs @@ -8,12 +8,10 @@ use crate::dirstate_tree::on_disk::DirstateV2ParseError; use crate::revlog::node::NULL_NODE; use crate::revlog::Node; -use crate::utils::hg_path::{HgPath, HgPathBuf}; -use crate::FastHashMap; +use crate::utils::hg_path::HgPath; use bytes_cast::BytesCast; pub mod dirs_multiset; -pub mod dirstate_map; pub mod entry; pub mod parsers; pub mod status; @@ -34,7 +32,6 @@ }; } -pub type StateMap = FastHashMap; pub type StateMapIter<'a> = Box< dyn Iterator< Item = Result<(&'a HgPath, DirstateEntry), DirstateV2ParseError>, @@ -42,7 +39,6 @@ + 'a, >; -pub type CopyMap = FastHashMap; pub type CopyMapIter<'a> = Box< dyn Iterator> + Send diff --git a/rust/hg-core/src/dirstate/dirs_multiset.rs b/rust/hg-core/src/dirstate/dirs_multiset.rs --- a/rust/hg-core/src/dirstate/dirs_multiset.rs +++ b/rust/hg-core/src/dirstate/dirs_multiset.rs @@ -216,7 +216,6 @@ #[cfg(test)] mod tests { use super::*; - use crate::StateMap; #[test] fn test_delete_path_path_not_found() { @@ -341,8 +340,8 @@ }; assert_eq!(expected, new); - let new = DirsMultiset::from_dirstate( - StateMap::default().into_iter().map(Ok), + let new = DirsMultiset::from_dirstate::<_, HgPathBuf>( + std::iter::empty(), false, ) .unwrap(); diff --git a/rust/hg-core/src/dirstate/dirstate_map.rs b/rust/hg-core/src/dirstate/dirstate_map.rs deleted file mode 100644 --- a/rust/hg-core/src/dirstate/dirstate_map.rs +++ /dev/null @@ -1,263 +0,0 @@ -// dirstate_map.rs -// -// Copyright 2019 Raphaël Gomès -// -// This software may be used and distributed according to the terms of the -// GNU General Public License version 2 or any later version. - -use crate::dirstate::parsers::Timestamp; -use crate::errors::HgError; -use crate::{ - dirstate::EntryState, - dirstate::SIZE_FROM_OTHER_PARENT, - dirstate::SIZE_NON_NORMAL, - pack_dirstate, parse_dirstate, - utils::hg_path::{HgPath, HgPathBuf}, - CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateParents, - StateMap, -}; -use micro_timer::timed; -use std::iter::FromIterator; -use std::ops::Deref; - -#[derive(Default)] -pub struct DirstateMap { - state_map: StateMap, - pub copy_map: CopyMap, - pub dirs: Option, - pub all_dirs: Option, -} - -/// Should only really be used in python interface code, for clarity -impl Deref for DirstateMap { - type Target = StateMap; - - fn deref(&self) -> &Self::Target { - &self.state_map - } -} - -impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap { - fn from_iter>( - iter: I, - ) -> Self { - Self { - state_map: iter.into_iter().collect(), - ..Self::default() - } - } -} - -impl DirstateMap { - pub fn new() -> Self { - Self::default() - } - - pub fn clear(&mut self) { - self.state_map = StateMap::default(); - self.copy_map.clear(); - } - - pub fn set_entry(&mut self, filename: &HgPath, entry: DirstateEntry) { - self.state_map.insert(filename.to_owned(), entry); - } - - /// Add a tracked file to the dirstate - pub fn add_file( - &mut self, - filename: &HgPath, - entry: DirstateEntry, - ) -> Result<(), DirstateError> { - let old_state = self.get(filename).map(|e| e.state()); - if old_state.is_none() || old_state == Some(EntryState::Removed) { - if let Some(ref mut dirs) = self.dirs { - dirs.add_path(filename)?; - } - } - if old_state.is_none() { - if let Some(ref mut all_dirs) = self.all_dirs { - all_dirs.add_path(filename)?; - } - } - self.state_map.insert(filename.to_owned(), entry.to_owned()); - Ok(()) - } - - /// Mark a file as removed in the dirstate. - /// - /// The `size` parameter is used to store sentinel values that indicate - /// the file's previous state. In the future, we should refactor this - /// to be more explicit about what that state is. - pub fn remove_file( - &mut self, - filename: &HgPath, - in_merge: bool, - ) -> Result<(), DirstateError> { - let old_entry_opt = self.get(filename); - let old_state = old_entry_opt.map(|e| e.state()); - let mut size = 0; - if in_merge { - // XXX we should not be able to have 'm' state and 'FROM_P2' if not - // during a merge. So I (marmoute) am not sure we need the - // conditionnal at all. Adding double checking this with assert - // would be nice. - if let Some(old_entry) = old_entry_opt { - // backup the previous state - if old_entry.state() == EntryState::Merged { - size = SIZE_NON_NORMAL; - } else if old_entry.state() == EntryState::Normal - && old_entry.size() == SIZE_FROM_OTHER_PARENT - { - // other parent - size = SIZE_FROM_OTHER_PARENT; - } - } - } - if old_state.is_some() && old_state != Some(EntryState::Removed) { - if let Some(ref mut dirs) = self.dirs { - dirs.delete_path(filename)?; - } - } - if old_state.is_none() { - if let Some(ref mut all_dirs) = self.all_dirs { - all_dirs.add_path(filename)?; - } - } - if size == 0 { - self.copy_map.remove(filename); - } - - self.state_map - .insert(filename.to_owned(), DirstateEntry::new_removed(size)); - Ok(()) - } - - /// Remove a file from the dirstate. - /// Returns `true` if the file was previously recorded. - pub fn drop_entry_and_copy_source( - &mut self, - filename: &HgPath, - ) -> Result<(), DirstateError> { - let old_state = self.get(filename).map(|e| e.state()); - let exists = self.state_map.remove(filename).is_some(); - - if exists { - if old_state != Some(EntryState::Removed) { - if let Some(ref mut dirs) = self.dirs { - dirs.delete_path(filename)?; - } - } - if let Some(ref mut all_dirs) = self.all_dirs { - all_dirs.delete_path(filename)?; - } - } - self.copy_map.remove(filename); - - Ok(()) - } - - /// Both of these setters and their uses appear to be the simplest way to - /// emulate a Python lazy property, but it is ugly and unidiomatic. - /// TODO One day, rewriting this struct using the typestate might be a - /// good idea. - pub fn set_all_dirs(&mut self) -> Result<(), DirstateError> { - if self.all_dirs.is_none() { - self.all_dirs = Some(DirsMultiset::from_dirstate( - self.state_map.iter().map(|(k, v)| Ok((k, *v))), - false, - )?); - } - Ok(()) - } - - pub fn set_dirs(&mut self) -> Result<(), DirstateError> { - if self.dirs.is_none() { - self.dirs = Some(DirsMultiset::from_dirstate( - self.state_map.iter().map(|(k, v)| Ok((k, *v))), - true, - )?); - } - Ok(()) - } - - pub fn has_tracked_dir( - &mut self, - directory: &HgPath, - ) -> Result { - self.set_dirs()?; - Ok(self.dirs.as_ref().unwrap().contains(directory)) - } - - pub fn has_dir( - &mut self, - directory: &HgPath, - ) -> Result { - self.set_all_dirs()?; - Ok(self.all_dirs.as_ref().unwrap().contains(directory)) - } - - #[timed] - pub fn read( - &mut self, - file_contents: &[u8], - ) -> Result, DirstateError> { - if file_contents.is_empty() { - return Ok(None); - } - - let (parents, entries, copies) = parse_dirstate(file_contents)?; - self.state_map.extend( - entries - .into_iter() - .map(|(path, entry)| (path.to_owned(), entry)), - ); - self.copy_map.extend( - copies - .into_iter() - .map(|(path, copy)| (path.to_owned(), copy.to_owned())), - ); - Ok(Some(parents.clone())) - } - - pub fn pack( - &mut self, - parents: DirstateParents, - now: Timestamp, - ) -> Result, HgError> { - pack_dirstate(&mut self.state_map, &self.copy_map, parents, now) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_dirs_multiset() { - let mut map = DirstateMap::new(); - assert!(map.dirs.is_none()); - assert!(map.all_dirs.is_none()); - - assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false); - assert!(map.all_dirs.is_some()); - assert!(map.dirs.is_none()); - - assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false); - assert!(map.dirs.is_some()); - } - - #[test] - fn test_add_file() { - let mut map = DirstateMap::new(); - - assert_eq!(0, map.len()); - - map.add_file( - HgPath::new(b"meh"), - DirstateEntry::from_v1_data(EntryState::Normal, 1337, 1337, 1337), - ) - .unwrap(); - - assert_eq!(1, map.len()); - } -} diff --git a/rust/hg-core/src/dirstate/parsers.rs b/rust/hg-core/src/dirstate/parsers.rs --- a/rust/hg-core/src/dirstate/parsers.rs +++ b/rust/hg-core/src/dirstate/parsers.rs @@ -5,14 +5,11 @@ use crate::errors::HgError; use crate::utils::hg_path::HgPath; -use crate::{ - dirstate::{CopyMap, EntryState, StateMap}, - DirstateEntry, DirstateParents, -}; +use crate::{dirstate::EntryState, DirstateEntry, DirstateParents}; use byteorder::{BigEndian, WriteBytesExt}; use bytes_cast::{unaligned, BytesCast}; use micro_timer::timed; -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; /// Parents are stored in the dirstate as byte hashes. pub const PARENT_SIZE: usize = 20; @@ -141,328 +138,3 @@ /// Seconds since the Unix epoch pub struct Timestamp(pub i64); - -pub fn pack_dirstate( - state_map: &mut StateMap, - copy_map: &CopyMap, - parents: DirstateParents, - now: Timestamp, -) -> Result, HgError> { - // TODO move away from i32 before 2038. - let now: i32 = now.0.try_into().expect("time overflow"); - - let expected_size: usize = state_map - .iter() - .map(|(filename, _)| { - packed_entry_size(filename, copy_map.get(filename).map(|p| &**p)) - }) - .sum(); - let expected_size = expected_size + PARENT_SIZE * 2; - - let mut packed = Vec::with_capacity(expected_size); - - packed.extend(parents.p1.as_bytes()); - packed.extend(parents.p2.as_bytes()); - - for (filename, entry) in state_map.iter_mut() { - entry.clear_ambiguous_mtime(now); - pack_entry( - filename, - entry, - copy_map.get(filename).map(|p| &**p), - &mut packed, - ) - } - - if packed.len() != expected_size { - return Err(HgError::CorruptedRepository(format!( - "bad dirstate size: {} != {}", - expected_size, - packed.len() - ))); - } - - Ok(packed) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{utils::hg_path::HgPathBuf, FastHashMap}; - use pretty_assertions::assert_eq; - - #[test] - fn test_pack_dirstate_empty() { - let mut state_map = StateMap::default(); - let copymap = FastHashMap::default(); - let parents = DirstateParents { - p1: b"12345678910111213141".into(), - p2: b"00000000000000000000".into(), - }; - let now = Timestamp(15000000); - let expected = b"1234567891011121314100000000000000000000".to_vec(); - - assert_eq!( - expected, - pack_dirstate(&mut state_map, ©map, parents, now).unwrap() - ); - - assert!(state_map.is_empty()) - } - #[test] - fn test_pack_dirstate_one_entry() { - let expected_state_map: StateMap = [( - HgPathBuf::from_bytes(b"f1"), - DirstateEntry::from_v1_data( - EntryState::Normal, - 0o644, - 0, - 791231220, - ), - )] - .iter() - .cloned() - .collect(); - let mut state_map = expected_state_map.clone(); - - let copymap = FastHashMap::default(); - let parents = DirstateParents { - p1: b"12345678910111213141".into(), - p2: b"00000000000000000000".into(), - }; - let now = Timestamp(15000000); - let expected = [ - 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49, - 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47, - 41, 58, 244, 0, 0, 0, 2, 102, 49, - ] - .to_vec(); - - assert_eq!( - expected, - pack_dirstate(&mut state_map, ©map, parents, now).unwrap() - ); - - assert_eq!(expected_state_map, state_map); - } - #[test] - fn test_pack_dirstate_one_entry_with_copy() { - let expected_state_map: StateMap = [( - HgPathBuf::from_bytes(b"f1"), - DirstateEntry::from_v1_data( - EntryState::Normal, - 0o644, - 0, - 791231220, - ), - )] - .iter() - .cloned() - .collect(); - let mut state_map = expected_state_map.clone(); - let mut copymap = FastHashMap::default(); - copymap.insert( - HgPathBuf::from_bytes(b"f1"), - HgPathBuf::from_bytes(b"copyname"), - ); - let parents = DirstateParents { - p1: b"12345678910111213141".into(), - p2: b"00000000000000000000".into(), - }; - let now = Timestamp(15000000); - let expected = [ - 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49, - 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47, - 41, 58, 244, 0, 0, 0, 11, 102, 49, 0, 99, 111, 112, 121, 110, 97, - 109, 101, - ] - .to_vec(); - - assert_eq!( - expected, - pack_dirstate(&mut state_map, ©map, parents, now).unwrap() - ); - assert_eq!(expected_state_map, state_map); - } - - #[test] - fn test_parse_pack_one_entry_with_copy() { - let mut state_map: StateMap = [( - HgPathBuf::from_bytes(b"f1"), - DirstateEntry::from_v1_data( - EntryState::Normal, - 0o644, - 0, - 791231220, - ), - )] - .iter() - .cloned() - .collect(); - let mut copymap = FastHashMap::default(); - copymap.insert( - HgPathBuf::from_bytes(b"f1"), - HgPathBuf::from_bytes(b"copyname"), - ); - let parents = DirstateParents { - p1: b"12345678910111213141".into(), - p2: b"00000000000000000000".into(), - }; - let now = Timestamp(15000000); - let result = - pack_dirstate(&mut state_map, ©map, parents.clone(), now) - .unwrap(); - - let (new_parents, entries, copies) = - parse_dirstate(result.as_slice()).unwrap(); - let new_state_map: StateMap = entries - .into_iter() - .map(|(path, entry)| (path.to_owned(), entry)) - .collect(); - let new_copy_map: CopyMap = copies - .into_iter() - .map(|(path, copy)| (path.to_owned(), copy.to_owned())) - .collect(); - - assert_eq!( - (&parents, state_map, copymap), - (new_parents, new_state_map, new_copy_map) - ) - } - - #[test] - fn test_parse_pack_multiple_entries_with_copy() { - let mut state_map: StateMap = [ - ( - HgPathBuf::from_bytes(b"f1"), - DirstateEntry::from_v1_data( - EntryState::Normal, - 0o644, - 0, - 791231220, - ), - ), - ( - HgPathBuf::from_bytes(b"f2"), - DirstateEntry::from_v1_data( - EntryState::Merged, - 0o777, - 1000, - 791231220, - ), - ), - ( - HgPathBuf::from_bytes(b"f3"), - DirstateEntry::from_v1_data( - EntryState::Removed, - 0o644, - 234553, - 791231220, - ), - ), - ( - HgPathBuf::from_bytes(b"f4\xF6"), - DirstateEntry::from_v1_data(EntryState::Added, 0o644, -1, -1), - ), - ] - .iter() - .cloned() - .collect(); - let mut copymap = FastHashMap::default(); - copymap.insert( - HgPathBuf::from_bytes(b"f1"), - HgPathBuf::from_bytes(b"copyname"), - ); - copymap.insert( - HgPathBuf::from_bytes(b"f4\xF6"), - HgPathBuf::from_bytes(b"copyname2"), - ); - let parents = DirstateParents { - p1: b"12345678910111213141".into(), - p2: b"00000000000000000000".into(), - }; - let now = Timestamp(15000000); - let result = - pack_dirstate(&mut state_map, ©map, parents.clone(), now) - .unwrap(); - - let (new_parents, entries, copies) = - parse_dirstate(result.as_slice()).unwrap(); - let new_state_map: StateMap = entries - .into_iter() - .map(|(path, entry)| (path.to_owned(), entry)) - .collect(); - let new_copy_map: CopyMap = copies - .into_iter() - .map(|(path, copy)| (path.to_owned(), copy.to_owned())) - .collect(); - - assert_eq!( - (&parents, state_map, copymap), - (new_parents, new_state_map, new_copy_map) - ) - } - - #[test] - /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4 - fn test_parse_pack_one_entry_with_copy_and_time_conflict() { - let mut state_map: StateMap = [( - HgPathBuf::from_bytes(b"f1"), - DirstateEntry::from_v1_data( - EntryState::Normal, - 0o644, - 0, - 15000000, - ), - )] - .iter() - .cloned() - .collect(); - let mut copymap = FastHashMap::default(); - copymap.insert( - HgPathBuf::from_bytes(b"f1"), - HgPathBuf::from_bytes(b"copyname"), - ); - let parents = DirstateParents { - p1: b"12345678910111213141".into(), - p2: b"00000000000000000000".into(), - }; - let now = Timestamp(15000000); - let result = - pack_dirstate(&mut state_map, ©map, parents.clone(), now) - .unwrap(); - - let (new_parents, entries, copies) = - parse_dirstate(result.as_slice()).unwrap(); - let new_state_map: StateMap = entries - .into_iter() - .map(|(path, entry)| (path.to_owned(), entry)) - .collect(); - let new_copy_map: CopyMap = copies - .into_iter() - .map(|(path, copy)| (path.to_owned(), copy.to_owned())) - .collect(); - - assert_eq!( - ( - &parents, - [( - HgPathBuf::from_bytes(b"f1"), - DirstateEntry::from_v1_data( - EntryState::Normal, - 0o644, - 0, - -1 - ) - )] - .iter() - .cloned() - .collect::(), - copymap, - ), - (new_parents, new_state_map, new_copy_map) - ) - } -} diff --git a/rust/hg-core/src/dirstate/status.rs b/rust/hg-core/src/dirstate/status.rs --- a/rust/hg-core/src/dirstate/status.rs +++ b/rust/hg-core/src/dirstate/status.rs @@ -10,33 +10,13 @@ //! and will only be triggered in narrow cases. use crate::dirstate_tree::on_disk::DirstateV2ParseError; -use crate::utils::path_auditor::PathAuditor; + use crate::{ - dirstate::SIZE_FROM_OTHER_PARENT, - filepatterns::PatternFileWarning, - matchers::{get_ignore_function, Matcher, VisitChildrenSet}, - utils::{ - files::{find_dirs, HgMetadata}, - hg_path::{ - hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf, - HgPathError, - }, - }, - CopyMap, DirstateEntry, DirstateMap, EntryState, FastHashMap, + utils::hg_path::{HgPath, HgPathError}, PatternError, }; -use lazy_static::lazy_static; -use micro_timer::timed; -use rayon::prelude::*; -use std::{ - borrow::Cow, - collections::HashSet, - fmt, - fs::{read_dir, DirEntry}, - io::ErrorKind, - ops::Deref, - path::{Path, PathBuf}, -}; + +use std::{borrow::Cow, fmt}; /// Wrong type of file from a `BadMatch` /// Note: a lot of those don't exist on all platforms. @@ -70,32 +50,6 @@ BadType(BadType), } -/// Enum used to dispatch new status entries into the right collections. -/// Is similar to `crate::EntryState`, but represents the transient state of -/// entries during the lifetime of a command. -#[derive(Debug, Copy, Clone)] -pub enum Dispatch { - Unsure, - Modified, - Added, - Removed, - Deleted, - Clean, - Unknown, - Ignored, - /// Empty dispatch, the file is not worth listing - None, - /// Was explicitly matched but cannot be found/accessed - Bad(BadMatch), - Directory { - /// True if the directory used to be a file in the dmap so we can say - /// that it's been removed. - was_file: bool, - }, -} - -type IoResult = std::io::Result; - /// `Box` is syntactic sugar for `Box`, so add /// an explicit lifetime here to not fight `'static` bounds "out of nowhere". pub type IgnoreFnType<'a> = @@ -105,135 +59,6 @@ /// the dirstate/explicit) paths, this comes up a lot. pub type HgPathCow<'a> = Cow<'a, HgPath>; -/// A path with its computed ``Dispatch`` information -type DispatchedPath<'a> = (HgPathCow<'a>, Dispatch); - -/// The conversion from `HgPath` to a real fs path failed. -/// `22` is the error code for "Invalid argument" -const INVALID_PATH_DISPATCH: Dispatch = Dispatch::Bad(BadMatch::OsError(22)); - -/// Dates and times that are outside the 31-bit signed range are compared -/// modulo 2^31. This should prevent hg from behaving badly with very large -/// files or corrupt dates while still having a high probability of detecting -/// changes. (issue2608) -/// TODO I haven't found a way of having `b` be `Into`, since `From` -/// is not defined for `i32`, and there is no `As` trait. This forces the -/// caller to cast `b` as `i32`. -fn mod_compare(a: i32, b: i32) -> bool { - a & i32::max_value() != b & i32::max_value() -} - -/// Return a sorted list containing information about the entries -/// in the directory. -/// -/// * `skip_dot_hg` - Return an empty vec if `path` contains a `.hg` directory -fn list_directory( - path: impl AsRef, - skip_dot_hg: bool, -) -> std::io::Result> { - let mut results = vec![]; - let entries = read_dir(path.as_ref())?; - - for entry in entries { - let entry = entry?; - let filename = os_string_to_hg_path_buf(entry.file_name())?; - let file_type = entry.file_type()?; - if skip_dot_hg && filename.as_bytes() == b".hg" && file_type.is_dir() { - return Ok(vec![]); - } else { - results.push((filename, entry)) - } - } - - results.sort_unstable_by_key(|e| e.0.clone()); - Ok(results) -} - -/// The file corresponding to the dirstate entry was found on the filesystem. -fn dispatch_found( - filename: impl AsRef, - entry: DirstateEntry, - metadata: HgMetadata, - copy_map: &CopyMap, - options: StatusOptions, -) -> Dispatch { - match entry.state() { - EntryState::Normal => { - let mode = entry.mode(); - let size = entry.size(); - let mtime = entry.mtime(); - - let HgMetadata { - st_mode, - st_size, - st_mtime, - .. - } = metadata; - - let size_changed = mod_compare(size, st_size as i32); - let mode_changed = - (mode ^ st_mode as i32) & 0o100 != 0o000 && options.check_exec; - let metadata_changed = size >= 0 && (size_changed || mode_changed); - let other_parent = size == SIZE_FROM_OTHER_PARENT; - - if metadata_changed - || other_parent - || copy_map.contains_key(filename.as_ref()) - { - if metadata.is_symlink() && size_changed { - // issue6456: Size returned may be longer due to encryption - // on EXT-4 fscrypt. TODO maybe only do it on EXT4? - Dispatch::Unsure - } else { - Dispatch::Modified - } - } else if mod_compare(mtime, st_mtime as i32) - || st_mtime == options.last_normal_time - { - // the file may have just been marked as normal and - // it may have changed in the same second without - // changing its size. This can happen if we quickly - // do multiple commits. Force lookup, so we don't - // miss such a racy file change. - Dispatch::Unsure - } else if options.list_clean { - Dispatch::Clean - } else { - Dispatch::None - } - } - EntryState::Merged => Dispatch::Modified, - EntryState::Added => Dispatch::Added, - EntryState::Removed => Dispatch::Removed, - } -} - -/// The file corresponding to this Dirstate entry is missing. -fn dispatch_missing(state: EntryState) -> Dispatch { - match state { - // File was removed from the filesystem during commands - EntryState::Normal | EntryState::Merged | EntryState::Added => { - Dispatch::Deleted - } - // File was removed, everything is normal - EntryState::Removed => Dispatch::Removed, - } -} - -fn dispatch_os_error(e: &std::io::Error) -> Dispatch { - Dispatch::Bad(BadMatch::OsError( - e.raw_os_error().expect("expected real OS error"), - )) -} - -lazy_static! { - static ref DEFAULT_WORK: HashSet<&'static HgPath> = { - let mut h = HashSet::new(); - h.insert(HgPath::new(b"")); - h - }; -} - #[derive(Debug, Copy, Clone)] pub struct StatusOptions { /// Remember the most recent modification timeslot for status, to make @@ -319,626 +144,3 @@ } } } - -/// Gives information about which files are changed in the working directory -/// and how, compared to the revision we're based on -pub struct Status<'a, M: ?Sized + Matcher + Sync> { - dmap: &'a DirstateMap, - pub(crate) matcher: &'a M, - root_dir: PathBuf, - pub(crate) options: StatusOptions, - ignore_fn: IgnoreFnType<'a>, -} - -impl<'a, M> Status<'a, M> -where - M: ?Sized + Matcher + Sync, -{ - pub fn new( - dmap: &'a DirstateMap, - matcher: &'a M, - root_dir: PathBuf, - ignore_files: Vec, - options: StatusOptions, - ) -> StatusResult<(Self, Vec)> { - // Needs to outlive `dir_ignore_fn` since it's captured. - - let (ignore_fn, warnings): (IgnoreFnType, _) = - if options.list_ignored || options.list_unknown { - get_ignore_function(ignore_files, &root_dir, &mut |_| {})? - } else { - (Box::new(|&_| true), vec![]) - }; - - Ok(( - Self { - dmap, - matcher, - root_dir, - options, - ignore_fn, - }, - warnings, - )) - } - - /// Is the path ignored? - pub fn is_ignored(&self, path: impl AsRef) -> bool { - (self.ignore_fn)(path.as_ref()) - } - - /// Is the path or one of its ancestors ignored? - pub fn dir_ignore(&self, dir: impl AsRef) -> bool { - // Only involve ignore mechanism if we're listing unknowns or ignored. - if self.options.list_ignored || self.options.list_unknown { - if self.is_ignored(&dir) { - true - } else { - for p in find_dirs(dir.as_ref()) { - if self.is_ignored(p) { - return true; - } - } - false - } - } else { - true - } - } - - /// Get stat data about the files explicitly specified by the matcher. - /// Returns a tuple of the directories that need to be traversed and the - /// files with their corresponding `Dispatch`. - /// TODO subrepos - #[timed] - pub fn walk_explicit( - &self, - traversed_sender: crossbeam_channel::Sender, - ) -> (Vec>, Vec>) { - self.matcher - .file_set() - .unwrap_or(&DEFAULT_WORK) - .par_iter() - .flat_map(|&filename| -> Option<_> { - // TODO normalization - let normalized = filename; - - let buf = match hg_path_to_path_buf(normalized) { - Ok(x) => x, - Err(_) => { - return Some(( - Cow::Borrowed(normalized), - INVALID_PATH_DISPATCH, - )) - } - }; - let target = self.root_dir.join(buf); - let st = target.symlink_metadata(); - let in_dmap = self.dmap.get(normalized); - match st { - Ok(meta) => { - let file_type = meta.file_type(); - return if file_type.is_file() || file_type.is_symlink() - { - if let Some(entry) = in_dmap { - return Some(( - Cow::Borrowed(normalized), - dispatch_found( - &normalized, - *entry, - HgMetadata::from_metadata(meta), - &self.dmap.copy_map, - self.options, - ), - )); - } - Some(( - Cow::Borrowed(normalized), - Dispatch::Unknown, - )) - } else if file_type.is_dir() { - if self.options.collect_traversed_dirs { - traversed_sender - .send(normalized.to_owned()) - .expect("receiver should outlive sender"); - } - Some(( - Cow::Borrowed(normalized), - Dispatch::Directory { - was_file: in_dmap.is_some(), - }, - )) - } else { - Some(( - Cow::Borrowed(normalized), - Dispatch::Bad(BadMatch::BadType( - // TODO do more than unknown - // Support for all `BadType` variant - // varies greatly between platforms. - // So far, no tests check the type and - // this should be good enough for most - // users. - BadType::Unknown, - )), - )) - }; - } - Err(_) => { - if let Some(entry) = in_dmap { - return Some(( - Cow::Borrowed(normalized), - dispatch_missing(entry.state()), - )); - } - } - }; - None - }) - .partition(|(_, dispatch)| match dispatch { - Dispatch::Directory { .. } => true, - _ => false, - }) - } - - /// Walk the working directory recursively to look for changes compared to - /// the current `DirstateMap`. - /// - /// This takes a mutable reference to the results to account for the - /// `extend` in timings - #[timed] - pub fn traverse( - &self, - path: impl AsRef, - old_results: &FastHashMap, Dispatch>, - results: &mut Vec>, - traversed_sender: crossbeam_channel::Sender, - ) { - // The traversal is done in parallel, so use a channel to gather - // entries. `crossbeam_channel::Sender` is `Sync`, while `mpsc::Sender` - // is not. - let (files_transmitter, files_receiver) = - crossbeam_channel::unbounded(); - - self.traverse_dir( - &files_transmitter, - path, - &old_results, - traversed_sender, - ); - - // Disconnect the channel so the receiver stops waiting - drop(files_transmitter); - - let new_results = files_receiver - .into_iter() - .par_bridge() - .map(|(f, d)| (Cow::Owned(f), d)); - - results.par_extend(new_results); - } - - /// Dispatch a single entry (file, folder, symlink...) found during - /// `traverse`. If the entry is a folder that needs to be traversed, it - /// will be handled in a separate thread. - fn handle_traversed_entry<'b>( - &'a self, - scope: &rayon::Scope<'b>, - files_sender: &'b crossbeam_channel::Sender<(HgPathBuf, Dispatch)>, - old_results: &'a FastHashMap, Dispatch>, - filename: HgPathBuf, - dir_entry: DirEntry, - traversed_sender: crossbeam_channel::Sender, - ) -> IoResult<()> - where - 'a: 'b, - { - let file_type = dir_entry.file_type()?; - let entry_option = self.dmap.get(&filename); - - if filename.as_bytes() == b".hg" { - // Could be a directory or a symlink - return Ok(()); - } - - if file_type.is_dir() { - self.handle_traversed_dir( - scope, - files_sender, - old_results, - entry_option, - filename, - traversed_sender, - ); - } else if file_type.is_file() || file_type.is_symlink() { - if let Some(entry) = entry_option { - if self.matcher.matches_everything() - || self.matcher.matches(&filename) - { - let metadata = dir_entry.metadata()?; - files_sender - .send(( - filename.to_owned(), - dispatch_found( - &filename, - *entry, - HgMetadata::from_metadata(metadata), - &self.dmap.copy_map, - self.options, - ), - )) - .unwrap(); - } - } else if (self.matcher.matches_everything() - || self.matcher.matches(&filename)) - && !self.is_ignored(&filename) - { - if (self.options.list_ignored - || self.matcher.exact_match(&filename)) - && self.dir_ignore(&filename) - { - if self.options.list_ignored { - files_sender - .send((filename.to_owned(), Dispatch::Ignored)) - .unwrap(); - } - } else if self.options.list_unknown { - files_sender - .send((filename.to_owned(), Dispatch::Unknown)) - .unwrap(); - } - } else if self.is_ignored(&filename) && self.options.list_ignored { - if self.matcher.matches(&filename) { - files_sender - .send((filename.to_owned(), Dispatch::Ignored)) - .unwrap(); - } - } - } else if let Some(entry) = entry_option { - // Used to be a file or a folder, now something else. - if self.matcher.matches_everything() - || self.matcher.matches(&filename) - { - files_sender - .send(( - filename.to_owned(), - dispatch_missing(entry.state()), - )) - .unwrap(); - } - } - - Ok(()) - } - - /// A directory was found in the filesystem and needs to be traversed - fn handle_traversed_dir<'b>( - &'a self, - scope: &rayon::Scope<'b>, - files_sender: &'b crossbeam_channel::Sender<(HgPathBuf, Dispatch)>, - old_results: &'a FastHashMap, Dispatch>, - entry_option: Option<&'a DirstateEntry>, - directory: HgPathBuf, - traversed_sender: crossbeam_channel::Sender, - ) where - 'a: 'b, - { - scope.spawn(move |_| { - // Nested `if` until `rust-lang/rust#53668` is stable - if let Some(entry) = entry_option { - // Used to be a file, is now a folder - if self.matcher.matches_everything() - || self.matcher.matches(&directory) - { - files_sender - .send(( - directory.to_owned(), - dispatch_missing(entry.state()), - )) - .unwrap(); - } - } - // Do we need to traverse it? - if !self.is_ignored(&directory) || self.options.list_ignored { - self.traverse_dir( - files_sender, - directory, - &old_results, - traversed_sender, - ) - } - }); - } - - /// Decides whether the directory needs to be listed, and if so handles the - /// entries in a separate thread. - fn traverse_dir( - &self, - files_sender: &crossbeam_channel::Sender<(HgPathBuf, Dispatch)>, - directory: impl AsRef, - old_results: &FastHashMap, Dispatch>, - traversed_sender: crossbeam_channel::Sender, - ) { - let directory = directory.as_ref(); - - if self.options.collect_traversed_dirs { - traversed_sender - .send(directory.to_owned()) - .expect("receiver should outlive sender"); - } - - let visit_entries = match self.matcher.visit_children_set(directory) { - VisitChildrenSet::Empty => return, - VisitChildrenSet::This | VisitChildrenSet::Recursive => None, - VisitChildrenSet::Set(set) => Some(set), - }; - let buf = match hg_path_to_path_buf(directory) { - Ok(b) => b, - Err(_) => { - files_sender - .send((directory.to_owned(), INVALID_PATH_DISPATCH)) - .expect("receiver should outlive sender"); - return; - } - }; - let dir_path = self.root_dir.join(buf); - - let skip_dot_hg = !directory.as_bytes().is_empty(); - let entries = match list_directory(dir_path, skip_dot_hg) { - Err(e) => { - files_sender - .send((directory.to_owned(), dispatch_os_error(&e))) - .expect("receiver should outlive sender"); - return; - } - Ok(entries) => entries, - }; - - rayon::scope(|scope| { - for (filename, dir_entry) in entries { - if let Some(ref set) = visit_entries { - if !set.contains(filename.deref()) { - continue; - } - } - // TODO normalize - let filename = if directory.is_empty() { - filename.to_owned() - } else { - directory.join(&filename) - }; - - if !old_results.contains_key(filename.deref()) { - match self.handle_traversed_entry( - scope, - files_sender, - old_results, - filename, - dir_entry, - traversed_sender.clone(), - ) { - Err(e) => { - files_sender - .send(( - directory.to_owned(), - dispatch_os_error(&e), - )) - .expect("receiver should outlive sender"); - } - Ok(_) => {} - } - } - } - }) - } - - /// Add the files in the dirstate to the results. - /// - /// This takes a mutable reference to the results to account for the - /// `extend` in timings - #[timed] - pub fn extend_from_dmap(&self, results: &mut Vec>) { - results.par_extend( - self.dmap - .par_iter() - .filter(|(path, _)| self.matcher.matches(path)) - .map(move |(filename, entry)| { - let filename: &HgPath = filename; - let filename_as_path = match hg_path_to_path_buf(filename) - { - Ok(f) => f, - Err(_) => { - return ( - Cow::Borrowed(filename), - INVALID_PATH_DISPATCH, - ) - } - }; - let meta = self - .root_dir - .join(filename_as_path) - .symlink_metadata(); - match meta { - Ok(m) - if !(m.file_type().is_file() - || m.file_type().is_symlink()) => - { - ( - Cow::Borrowed(filename), - dispatch_missing(entry.state()), - ) - } - Ok(m) => ( - Cow::Borrowed(filename), - dispatch_found( - filename, - *entry, - HgMetadata::from_metadata(m), - &self.dmap.copy_map, - self.options, - ), - ), - Err(e) - if e.kind() == ErrorKind::NotFound - || e.raw_os_error() == Some(20) => - { - // Rust does not yet have an `ErrorKind` for - // `NotADirectory` (errno 20) - // It happens if the dirstate contains `foo/bar` - // and foo is not a - // directory - ( - Cow::Borrowed(filename), - dispatch_missing(entry.state()), - ) - } - Err(e) => { - (Cow::Borrowed(filename), dispatch_os_error(&e)) - } - } - }), - ); - } - - /// Checks all files that are in the dirstate but were not found during the - /// working directory traversal. This means that the rest must - /// be either ignored, under a symlink or under a new nested repo. - /// - /// This takes a mutable reference to the results to account for the - /// `extend` in timings - #[timed] - pub fn handle_unknowns(&self, results: &mut Vec>) { - let to_visit: Vec<(&HgPath, &DirstateEntry)> = - if results.is_empty() && self.matcher.matches_everything() { - self.dmap.iter().map(|(f, e)| (f.deref(), e)).collect() - } else { - // Only convert to a hashmap if needed. - let old_results: FastHashMap<_, _> = - results.iter().cloned().collect(); - self.dmap - .iter() - .filter_map(move |(f, e)| { - if !old_results.contains_key(f.deref()) - && self.matcher.matches(f) - { - Some((f.deref(), e)) - } else { - None - } - }) - .collect() - }; - - let path_auditor = PathAuditor::new(&self.root_dir); - - let new_results = to_visit.into_par_iter().filter_map( - |(filename, entry)| -> Option<_> { - // Report ignored items in the dmap as long as they are not - // under a symlink directory. - if path_auditor.check(filename) { - // TODO normalize for case-insensitive filesystems - let buf = match hg_path_to_path_buf(filename) { - Ok(x) => x, - Err(_) => { - return Some(( - Cow::Owned(filename.to_owned()), - INVALID_PATH_DISPATCH, - )); - } - }; - Some(( - Cow::Owned(filename.to_owned()), - match self.root_dir.join(&buf).symlink_metadata() { - // File was just ignored, no links, and exists - Ok(meta) => { - let metadata = HgMetadata::from_metadata(meta); - dispatch_found( - filename, - *entry, - metadata, - &self.dmap.copy_map, - self.options, - ) - } - // File doesn't exist - Err(_) => dispatch_missing(entry.state()), - }, - )) - } else { - // It's either missing or under a symlink directory which - // we, in this case, report as missing. - Some(( - Cow::Owned(filename.to_owned()), - dispatch_missing(entry.state()), - )) - } - }, - ); - - results.par_extend(new_results); - } -} - -#[timed] -pub fn build_response<'a>( - results: impl IntoIterator>, - traversed: Vec>, -) -> DirstateStatus<'a> { - let mut unsure = vec![]; - let mut modified = vec![]; - let mut added = vec![]; - let mut removed = vec![]; - let mut deleted = vec![]; - let mut clean = vec![]; - let mut ignored = vec![]; - let mut unknown = vec![]; - let mut bad = vec![]; - - for (filename, dispatch) in results.into_iter() { - match dispatch { - Dispatch::Unknown => unknown.push(filename), - Dispatch::Unsure => unsure.push(filename), - Dispatch::Modified => modified.push(filename), - Dispatch::Added => added.push(filename), - Dispatch::Removed => removed.push(filename), - Dispatch::Deleted => deleted.push(filename), - Dispatch::Clean => clean.push(filename), - Dispatch::Ignored => ignored.push(filename), - Dispatch::None => {} - Dispatch::Bad(reason) => bad.push((filename, reason)), - Dispatch::Directory { .. } => {} - } - } - - DirstateStatus { - modified, - added, - removed, - deleted, - clean, - ignored, - unknown, - bad, - unsure, - traversed, - dirty: false, - } -} - -/// Get the status of files in the working directory. -/// -/// This is the current entry-point for `hg-core` and is realistically unusable -/// outside of a Python context because its arguments need to provide a lot of -/// information that will not be necessary in the future. -#[timed] -pub fn status<'a>( - dmap: &'a DirstateMap, - matcher: &'a (dyn Matcher + Sync), - root_dir: PathBuf, - ignore_files: Vec, - options: StatusOptions, -) -> StatusResult<(DirstateStatus<'a>, Vec)> { - let (status, warnings) = - Status::new(dmap, matcher, root_dir, ignore_files, options)?; - - Ok((status.run()?, warnings)) -} diff --git a/rust/hg-core/src/dirstate_tree/dirstate_map.rs b/rust/hg-core/src/dirstate_tree/dirstate_map.rs --- a/rust/hg-core/src/dirstate_tree/dirstate_map.rs +++ b/rust/hg-core/src/dirstate_tree/dirstate_map.rs @@ -11,11 +11,12 @@ use crate::dirstate::parsers::packed_entry_size; use crate::dirstate::parsers::parse_dirstate_entries; use crate::dirstate::parsers::Timestamp; +use crate::dirstate::CopyMapIter; +use crate::dirstate::StateMapIter; use crate::dirstate::SIZE_FROM_OTHER_PARENT; use crate::dirstate::SIZE_NON_NORMAL; use crate::matchers::Matcher; use crate::utils::hg_path::{HgPath, HgPathBuf}; -use crate::CopyMapIter; use crate::DirstateEntry; use crate::DirstateError; use crate::DirstateParents; @@ -23,7 +24,6 @@ use crate::EntryState; use crate::FastHashMap; use crate::PatternFileWarning; -use crate::StateMapIter; use crate::StatusError; use crate::StatusOptions; diff --git a/rust/hg-core/src/dirstate_tree/dispatch.rs b/rust/hg-core/src/dirstate_tree/dispatch.rs --- a/rust/hg-core/src/dirstate_tree/dispatch.rs +++ b/rust/hg-core/src/dirstate_tree/dispatch.rs @@ -1,17 +1,16 @@ use std::path::PathBuf; use crate::dirstate::parsers::Timestamp; +use crate::dirstate::CopyMapIter; +use crate::dirstate::StateMapIter; use crate::dirstate_tree::on_disk::DirstateV2ParseError; use crate::matchers::Matcher; use crate::utils::hg_path::{HgPath, HgPathBuf}; -use crate::CopyMapIter; use crate::DirstateEntry; use crate::DirstateError; -use crate::DirstateMap; use crate::DirstateParents; use crate::DirstateStatus; use crate::PatternFileWarning; -use crate::StateMapIter; use crate::StatusError; use crate::StatusOptions; @@ -212,190 +211,3 @@ + '_, >; } - -impl DirstateMapMethods for DirstateMap { - fn clear(&mut self) { - self.clear() - } - - /// Used to set a value directory. - /// - /// XXX Is temporary during a refactor of V1 dirstate and will disappear - /// shortly. - fn set_entry( - &mut self, - filename: &HgPath, - entry: DirstateEntry, - ) -> Result<(), DirstateV2ParseError> { - self.set_entry(&filename, entry); - Ok(()) - } - - fn add_file( - &mut self, - filename: &HgPath, - entry: DirstateEntry, - ) -> Result<(), DirstateError> { - self.add_file(filename, entry) - } - - fn remove_file( - &mut self, - filename: &HgPath, - in_merge: bool, - ) -> Result<(), DirstateError> { - self.remove_file(filename, in_merge) - } - - fn drop_entry_and_copy_source( - &mut self, - filename: &HgPath, - ) -> Result<(), DirstateError> { - self.drop_entry_and_copy_source(filename) - } - - fn has_tracked_dir( - &mut self, - directory: &HgPath, - ) -> Result { - self.has_tracked_dir(directory) - } - - fn has_dir(&mut self, directory: &HgPath) -> Result { - self.has_dir(directory) - } - - fn pack_v1( - &mut self, - parents: DirstateParents, - now: Timestamp, - ) -> Result, DirstateError> { - Ok(self.pack(parents, now)?) - } - - fn pack_v2( - &mut self, - _now: Timestamp, - _can_append: bool, - ) -> Result<(Vec, Vec, bool), DirstateError> { - panic!( - "should have used dirstate_tree::DirstateMap to use the v2 format" - ) - } - - fn status<'a>( - &'a mut self, - matcher: &'a (dyn Matcher + Sync), - root_dir: PathBuf, - ignore_files: Vec, - options: StatusOptions, - ) -> Result<(DirstateStatus<'a>, Vec), StatusError> - { - crate::status(self, matcher, root_dir, ignore_files, options) - } - - fn copy_map_len(&self) -> usize { - self.copy_map.len() - } - - fn copy_map_iter(&self) -> CopyMapIter<'_> { - Box::new( - self.copy_map - .iter() - .map(|(key, value)| Ok((&**key, &**value))), - ) - } - - fn copy_map_contains_key( - &self, - key: &HgPath, - ) -> Result { - Ok(self.copy_map.contains_key(key)) - } - - fn copy_map_get( - &self, - key: &HgPath, - ) -> Result, DirstateV2ParseError> { - Ok(self.copy_map.get(key).map(|p| &**p)) - } - - fn copy_map_remove( - &mut self, - key: &HgPath, - ) -> Result, DirstateV2ParseError> { - Ok(self.copy_map.remove(key)) - } - - fn copy_map_insert( - &mut self, - key: HgPathBuf, - value: HgPathBuf, - ) -> Result, DirstateV2ParseError> { - Ok(self.copy_map.insert(key, value)) - } - - fn len(&self) -> usize { - (&**self).len() - } - - fn contains_key( - &self, - key: &HgPath, - ) -> Result { - Ok((&**self).contains_key(key)) - } - - fn get( - &self, - key: &HgPath, - ) -> Result, DirstateV2ParseError> { - Ok((&**self).get(key).cloned()) - } - - fn iter(&self) -> StateMapIter<'_> { - Box::new((&**self).iter().map(|(key, value)| Ok((&**key, *value)))) - } - - fn iter_tracked_dirs( - &mut self, - ) -> Result< - Box< - dyn Iterator> - + Send - + '_, - >, - DirstateError, - > { - self.set_all_dirs()?; - Ok(Box::new( - self.all_dirs - .as_ref() - .unwrap() - .iter() - .map(|path| Ok(&**path)), - )) - } - - fn debug_iter( - &self, - all: bool, - ) -> Box< - dyn Iterator< - Item = Result< - (&HgPath, (u8, i32, i32, i32)), - DirstateV2ParseError, - >, - > + Send - + '_, - > { - // Not used for the flat (not tree-based) DirstateMap - let _ = all; - - Box::new( - (&**self) - .iter() - .map(|(path, entry)| Ok((&**path, entry.debug_tuple()))), - ) - } -} diff --git a/rust/hg-core/src/dirstate_tree/owning_dispatch.rs b/rust/hg-core/src/dirstate_tree/owning_dispatch.rs --- a/rust/hg-core/src/dirstate_tree/owning_dispatch.rs +++ b/rust/hg-core/src/dirstate_tree/owning_dispatch.rs @@ -1,16 +1,16 @@ use crate::dirstate::parsers::Timestamp; +use crate::dirstate::CopyMapIter; +use crate::dirstate::StateMapIter; use crate::dirstate_tree::dispatch::DirstateMapMethods; use crate::dirstate_tree::on_disk::DirstateV2ParseError; use crate::dirstate_tree::owning::OwningDirstateMap; use crate::matchers::Matcher; use crate::utils::hg_path::{HgPath, HgPathBuf}; -use crate::CopyMapIter; use crate::DirstateEntry; use crate::DirstateError; use crate::DirstateParents; use crate::DirstateStatus; use crate::PatternFileWarning; -use crate::StateMapIter; use crate::StatusError; use crate::StatusOptions; use std::path::PathBuf; diff --git a/rust/hg-core/src/lib.rs b/rust/hg-core/src/lib.rs --- a/rust/hg-core/src/lib.rs +++ b/rust/hg-core/src/lib.rs @@ -16,14 +16,11 @@ pub mod testing; // unconditionally built, for use from integration tests pub use dirstate::{ dirs_multiset::{DirsMultiset, DirsMultisetIter}, - dirstate_map::DirstateMap, - parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE}, status::{ - status, BadMatch, BadType, DirstateStatus, HgPathCow, StatusError, + BadMatch, BadType, DirstateStatus, HgPathCow, StatusError, StatusOptions, }, - CopyMap, CopyMapIter, DirstateEntry, DirstateParents, EntryState, - StateMap, StateMapIter, + DirstateEntry, DirstateParents, EntryState, }; pub mod copy_tracing; mod filepatterns; diff --git a/rust/hg-core/src/operations/dirstate_status.rs b/rust/hg-core/src/operations/dirstate_status.rs deleted file mode 100644 --- a/rust/hg-core/src/operations/dirstate_status.rs +++ /dev/null @@ -1,71 +0,0 @@ -// dirstate_status.rs -// -// Copyright 2019, Raphaël Gomès -// -// This software may be used and distributed according to the terms of the -// GNU General Public License version 2 or any later version. - -use crate::dirstate::status::{build_response, Dispatch, Status}; -use crate::matchers::Matcher; -use crate::{DirstateStatus, StatusError}; - -impl<'a, M: ?Sized + Matcher + Sync> Status<'a, M> { - pub(crate) fn run(&self) -> Result, StatusError> { - let (traversed_sender, traversed_receiver) = - crossbeam_channel::unbounded(); - - // Step 1: check the files explicitly mentioned by the user - let (work, mut results) = self.walk_explicit(traversed_sender.clone()); - - if !work.is_empty() { - // Hashmaps are quite a bit slower to build than vecs, so only - // build it if needed. - let old_results = results.iter().cloned().collect(); - - // Step 2: recursively check the working directory for changes if - // needed - for (dir, dispatch) in work { - match dispatch { - Dispatch::Directory { was_file } => { - if was_file { - results.push((dir.to_owned(), Dispatch::Removed)); - } - if self.options.list_ignored - || self.options.list_unknown - && !self.dir_ignore(&dir) - { - self.traverse( - &dir, - &old_results, - &mut results, - traversed_sender.clone(), - ); - } - } - _ => { - unreachable!("There can only be directories in `work`") - } - } - } - } - - if !self.matcher.is_exact() { - if self.options.list_unknown { - self.handle_unknowns(&mut results); - } else { - // TODO this is incorrect, see issue6335 - // This requires a fix in both Python and Rust that can happen - // with other pending changes to `status`. - self.extend_from_dmap(&mut results); - } - } - - drop(traversed_sender); - let traversed = traversed_receiver - .into_iter() - .map(std::borrow::Cow::Owned) - .collect(); - - Ok(build_response(results, traversed)) - } -} diff --git a/rust/hg-core/src/operations/mod.rs b/rust/hg-core/src/operations/mod.rs --- a/rust/hg-core/src/operations/mod.rs +++ b/rust/hg-core/src/operations/mod.rs @@ -4,7 +4,6 @@ mod cat; mod debugdata; -mod dirstate_status; mod list_tracked_files; pub use cat::{cat, CatOutput}; pub use debugdata::{debug_data, DebugDataKind}; diff --git a/rust/hg-cpython/src/dirstate/copymap.rs b/rust/hg-cpython/src/dirstate/copymap.rs --- a/rust/hg-cpython/src/dirstate/copymap.rs +++ b/rust/hg-cpython/src/dirstate/copymap.rs @@ -15,9 +15,9 @@ use crate::dirstate::dirstate_map::v2_error; use crate::dirstate::dirstate_map::DirstateMap; +use hg::dirstate::CopyMapIter; use hg::dirstate_tree::on_disk::DirstateV2ParseError; use hg::utils::hg_path::HgPath; -use hg::CopyMapIter; py_class!(pub class CopyMap |py| { data dirstate_map: DirstateMap; diff --git a/rust/hg-cpython/src/dirstate/dirstate_map.rs b/rust/hg-cpython/src/dirstate/dirstate_map.rs --- a/rust/hg-cpython/src/dirstate/dirstate_map.rs +++ b/rust/hg-cpython/src/dirstate/dirstate_map.rs @@ -23,6 +23,7 @@ }; use hg::{ dirstate::parsers::Timestamp, + dirstate::StateMapIter, dirstate_tree::dirstate_map::DirstateMap as TreeDirstateMap, dirstate_tree::dispatch::DirstateMapMethods, dirstate_tree::on_disk::DirstateV2ParseError, @@ -30,8 +31,7 @@ revlog::Node, utils::files::normalize_case, utils::hg_path::{HgPath, HgPathBuf}, - DirstateEntry, DirstateError, DirstateMap as RustDirstateMap, - DirstateParents, EntryState, StateMapIter, + DirstateEntry, DirstateError, DirstateParents, EntryState, }; // TODO @@ -52,25 +52,16 @@ /// Returns a `(dirstate_map, parents)` tuple @staticmethod def new_v1( - use_dirstate_tree: bool, on_disk: PyBytes, ) -> PyResult { - let (inner, parents) = if use_dirstate_tree { - let on_disk = PyBytesDeref::new(py, on_disk); - let mut map = OwningDirstateMap::new_empty(on_disk); - let (on_disk, map_placeholder) = map.get_mut_pair(); + let on_disk = PyBytesDeref::new(py, on_disk); + let mut map = OwningDirstateMap::new_empty(on_disk); + let (on_disk, map_placeholder) = map.get_mut_pair(); - let (actual_map, parents) = TreeDirstateMap::new_v1(on_disk) - .map_err(|e| dirstate_error(py, e))?; - *map_placeholder = actual_map; - (Box::new(map) as _, parents) - } else { - let bytes = on_disk.data(py); - let mut map = RustDirstateMap::default(); - let parents = map.read(bytes).map_err(|e| dirstate_error(py, e))?; - (Box::new(map) as _, parents) - }; - let map = Self::create_instance(py, inner)?; + let (actual_map, parents) = TreeDirstateMap::new_v1(on_disk) + .map_err(|e| dirstate_error(py, e))?; + *map_placeholder = actual_map; + let map = Self::create_instance(py, Box::new(map))?; let parents = parents.map(|p| { let p1 = PyBytes::new(py, p.p1.as_bytes()); let p2 = PyBytes::new(py, p.p2.as_bytes()); diff --git a/tests/test-dirstate-race.t b/tests/test-dirstate-race.t --- a/tests/test-dirstate-race.t +++ b/tests/test-dirstate-race.t @@ -1,10 +1,4 @@ -#testcases dirstate-v1 dirstate-v1-tree dirstate-v2 - -#if dirstate-v1-tree -#require rust - $ echo '[experimental]' >> $HGRCPATH - $ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH -#endif +#testcases dirstate-v1 dirstate-v2 #if dirstate-v2 #require rust diff --git a/tests/test-dirstate-race2.t b/tests/test-dirstate-race2.t --- a/tests/test-dirstate-race2.t +++ b/tests/test-dirstate-race2.t @@ -1,10 +1,4 @@ -#testcases dirstate-v1 dirstate-v1-tree dirstate-v2 - -#if dirstate-v1-tree -#require rust - $ echo '[experimental]' >> $HGRCPATH - $ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH -#endif +#testcases dirstate-v1 dirstate-v2 #if dirstate-v2 #require rust diff --git a/tests/test-dirstate.t b/tests/test-dirstate.t --- a/tests/test-dirstate.t +++ b/tests/test-dirstate.t @@ -1,10 +1,4 @@ -#testcases dirstate-v1 dirstate-v1-tree dirstate-v2 - -#if dirstate-v1-tree -#require rust - $ echo '[experimental]' >> $HGRCPATH - $ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH -#endif +#testcases dirstate-v1 dirstate-v2 #if dirstate-v2 #require rust diff --git a/tests/test-hgignore.t b/tests/test-hgignore.t --- a/tests/test-hgignore.t +++ b/tests/test-hgignore.t @@ -1,10 +1,4 @@ -#testcases dirstate-v1 dirstate-v1-tree dirstate-v2 - -#if dirstate-v1-tree -#require rust - $ echo '[experimental]' >> $HGRCPATH - $ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH -#endif +#testcases dirstate-v1 dirstate-v2 #if dirstate-v2 #require rust diff --git a/tests/test-permissions.t b/tests/test-permissions.t --- a/tests/test-permissions.t +++ b/tests/test-permissions.t @@ -1,12 +1,6 @@ #require unix-permissions no-root reporevlogstore -#testcases dirstate-v1 dirstate-v1-tree dirstate-v2 - -#if dirstate-v1-tree -#require rust - $ echo '[experimental]' >> $HGRCPATH - $ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH -#endif +#testcases dirstate-v1 dirstate-v2 #if dirstate-v2 #require rust diff --git a/tests/test-purge.t b/tests/test-purge.t --- a/tests/test-purge.t +++ b/tests/test-purge.t @@ -1,10 +1,4 @@ -#testcases dirstate-v1 dirstate-v1-tree dirstate-v2 - -#if dirstate-v1-tree -#require rust - $ echo '[experimental]' >> $HGRCPATH - $ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH -#endif +#testcases dirstate-v1 dirstate-v2 #if dirstate-v2 #require rust diff --git a/tests/test-status.t b/tests/test-status.t --- a/tests/test-status.t +++ b/tests/test-status.t @@ -1,4 +1,4 @@ -#testcases dirstate-v1 dirstate-v1-tree dirstate-v2 +#testcases dirstate-v1 dirstate-v2 #if no-rust $ hg init repo0 --config format.exp-dirstate-v2=1 @@ -6,12 +6,6 @@ [255] #endif -#if dirstate-v1-tree -#require rust - $ echo '[experimental]' >> $HGRCPATH - $ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH -#endif - #if dirstate-v2 #require rust $ echo '[format]' >> $HGRCPATH diff --git a/tests/test-symlinks.t b/tests/test-symlinks.t --- a/tests/test-symlinks.t +++ b/tests/test-symlinks.t @@ -1,12 +1,6 @@ #require symlink -#testcases dirstate-v1 dirstate-v1-tree dirstate-v2 - -#if dirstate-v1-tree -#require rust - $ echo '[experimental]' >> $HGRCPATH - $ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH -#endif +#testcases dirstate-v1 dirstate-v2 #if dirstate-v2 #require rust