diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -1200,9 +1200,6 @@ if rustmod is None: use_rust = False - elif self._checkcase: - # Case-insensitive filesystems are not handled yet - use_rust = False elif subrepos: use_rust = False elif sparse.enabled: diff --git a/rust/hg-core/src/dirstate/dirstate_map.rs b/rust/hg-core/src/dirstate/dirstate_map.rs --- a/rust/hg-core/src/dirstate/dirstate_map.rs +++ b/rust/hg-core/src/dirstate/dirstate_map.rs @@ -8,10 +8,7 @@ use crate::{ dirstate::{parsers::PARENT_SIZE, EntryState, SIZE_FROM_OTHER_PARENT}, pack_dirstate, parse_dirstate, - utils::{ - files::normalize_case, - hg_path::{HgPath, HgPathBuf}, - }, + utils::hg_path::{HgPath, HgPathBuf}, CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError, DirstateParents, DirstateParseError, FastHashMap, StateMap, }; @@ -135,7 +132,12 @@ } if let Some(ref mut file_fold_map) = self.file_fold_map { - file_fold_map.remove(&normalize_case(filename)); + match filename.canonical_name() { + Ok(canonical_name) => { + file_fold_map.remove(&canonical_name); + } + Err(_) => {} + } } self.state_map.insert( filename.to_owned(), @@ -172,7 +174,12 @@ } } if let Some(ref mut file_fold_map) = self.file_fold_map { - file_fold_map.remove(&normalize_case(filename)); + match filename.canonical_name() { + Ok(canonical_name) => { + file_fold_map.remove(&canonical_name); + } + Err(_) => {} + } } self.get_non_normal_other_parent_entries() .0 @@ -399,8 +406,14 @@ for (filename, DirstateEntry { state, .. }) in self.state_map.borrow() { if *state == EntryState::Removed { - new_file_fold_map - .insert(normalize_case(filename), filename.to_owned()); + match filename.canonical_name() { + Ok(canonical_filename) => { + new_file_fold_map + .insert(filename.to_owned(), canonical_filename); + } + Err(_) => { + } + } } } self.file_fold_map = Some(new_file_fold_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 @@ -786,8 +786,8 @@ .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 + // Only consider items found under the proper name + if filename.check_name() && path_auditor.check(filename) { let buf = match hg_path_to_path_buf(filename) { Ok(x) => x, Err(e) => return Some(Err(e.into())), 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 @@ -26,11 +26,10 @@ pub mod operations; pub mod utils; -// Remove this to see (potential) non-artificial compile failures. MacOS -// *should* compile, but fail to compile tests for example as of 2020-03-06 -#[cfg(not(target_os = "linux"))] +// Remove this to see (potential) non-artificial compile failures. +#[cfg(not(any(target_os = "linux", target_os = "macos")))] compile_error!( - "`hg-core` has only been tested on Linux and will most \ + "`hg-core` has only been tested on Linux and macOS and will most \ likely not behave correctly on other platforms." ); diff --git a/rust/hg-core/src/utils/hg_path.rs b/rust/hg-core/src/utils/hg_path.rs --- a/rust/hg-core/src/utils/hg_path.rs +++ b/rust/hg-core/src/utils/hg_path.rs @@ -6,6 +6,7 @@ // GNU General Public License version 2 or any later version. use std::borrow::Borrow; +use std::fs::canonicalize; use std::ffi::{OsStr, OsString}; use std::fmt; use std::ops::Deref; @@ -183,6 +184,32 @@ &self.inner[..] }) } + pub fn canonical_name(&self) -> Result { + match canonicalize(&hg_path_to_path_buf(&self)?) { + Ok(canonical_path) => { + let canonical_name = os_string_to_hg_path_buf( + &canonical_path.file_name().unwrap() + )?; + Ok(self.parent().join(&canonical_name)) + } + Err(_) => Err(HgPathError::NotFsCompliant(self.to_hg_path_buf())), + } + } + pub fn check_name(&self) -> bool { + let path; + + match hg_path_to_path_buf(&self) { + Ok(p) => path = p, + Err(_) => return true, + } + + match canonicalize(&path) { + Ok(canonical_path) => { + canonical_path.file_name() == path.file_name() + } + Err(_) => true + } + } /// Returns a tuple of slices `(base, filename)` resulting from the split /// at the rightmost `/`, if any. /// 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 @@ -350,8 +350,8 @@ { dict.set_item( py, - key.as_bytes().to_vec(), - value.as_bytes().to_vec(), + PyBytes::new(py, key.as_bytes()).into_object(), + PyBytes::new(py, value.as_bytes()).into_object(), )?; } Ok(dict)