diff --git a/rust/hg-core/src/dirstate/entry.rs b/rust/hg-core/src/dirstate/entry.rs --- a/rust/hg-core/src/dirstate/entry.rs +++ b/rust/hg-core/src/dirstate/entry.rs @@ -259,6 +259,12 @@ pub fallback_symlink: Option, } +#[derive(Debug, Default, Copy, Clone)] +pub struct ParentFileData { + pub mode_size: Option<(u32, u32)>, + pub mtime: Option, +} + impl DirstateEntry { pub fn from_v2_data(v2_data: DirstateV2Data) -> Self { let DirstateV2Data { 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,6 +11,8 @@ use crate::dirstate::parsers::packed_entry_size; use crate::dirstate::parsers::parse_dirstate_entries; use crate::dirstate::CopyMapIter; +use crate::dirstate::DirstateV2Data; +use crate::dirstate::ParentFileData; use crate::dirstate::StateMapIter; use crate::dirstate::TruncatedTimestamp; use crate::dirstate::SIZE_FROM_OTHER_PARENT; @@ -606,6 +608,73 @@ } } + fn reset_state( + &mut self, + filename: &HgPath, + old_entry_opt: Option, + wc_tracked: bool, + p1_tracked: bool, + p2_info: bool, + has_meaningful_mtime: bool, + parent_file_data_opt: Option, + ) -> Result<(), DirstateError> { + let (had_entry, was_tracked) = match old_entry_opt { + Some(old_entry) => (true, old_entry.tracked()), + None => (false, false), + }; + let node = Self::get_or_insert_node( + self.on_disk, + &mut self.unreachable_bytes, + &mut self.root, + filename, + WithBasename::to_cow_owned, + |ancestor| { + if !had_entry { + ancestor.descendants_with_entry_count += 1; + } + if was_tracked { + if !wc_tracked { + ancestor.tracked_descendants_count = ancestor + .tracked_descendants_count + .checked_sub(1) + .expect("tracked count to be >= 0"); + } + } else { + if wc_tracked { + ancestor.tracked_descendants_count += 1; + } + } + }, + )?; + + let v2_data = if let Some(parent_file_data) = parent_file_data_opt { + DirstateV2Data { + wc_tracked, + p1_tracked, + p2_info, + mode_size: parent_file_data.mode_size, + mtime: if has_meaningful_mtime { + parent_file_data.mtime + } else { + None + }, + ..Default::default() + } + } else { + DirstateV2Data { + wc_tracked, + p1_tracked, + p2_info, + ..Default::default() + } + }; + if !had_entry { + self.nodes_with_entry_count += 1; + } + node.data = NodeData::Entry(DirstateEntry::from_v2_data(v2_data)); + Ok(()) + } + fn set_tracked( &mut self, filename: &HgPath, @@ -812,6 +881,34 @@ self.with_dmap_mut(|map| map.set_tracked(filename, old_entry_opt)) } + pub fn reset_state( + &mut self, + filename: &HgPath, + wc_tracked: bool, + p1_tracked: bool, + p2_info: bool, + has_meaningful_mtime: bool, + parent_file_data_opt: Option, + ) -> Result<(), DirstateError> { + if !(p1_tracked || p2_info || wc_tracked) { + self.drop_entry_and_copy_source(filename)?; + return Ok(()); + } + self.copy_map_remove(filename)?; + let old_entry_opt = self.get(filename)?; + self.with_dmap_mut(|map| { + map.reset_state( + filename, + old_entry_opt, + wc_tracked, + p1_tracked, + p2_info, + has_meaningful_mtime, + parent_file_data_opt, + ) + }) + } + pub fn remove_file( &mut self, filename: &HgPath, 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 @@ -15,6 +15,7 @@ exc, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList, PyNone, PyObject, PyResult, Python, PythonObject, ToPyObject, UnsafePyLeaked, }; +use hg::dirstate::{ParentFileData, TruncatedTimestamp}; use crate::{ dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator}, @@ -141,6 +142,55 @@ Ok(was_tracked.to_py_object(py)) } + def reset_state( + &self, + f: PyObject, + wc_tracked: bool, + p1_tracked: bool, + p2_info: bool, + has_meaningful_mtime: bool, + parentfiledata: Option<(u32, u32, Option<(i64, u32, bool)>)>, + ) -> PyResult { + let mut has_meaningful_mtime = has_meaningful_mtime; + let parent_file_data = match parentfiledata { + None => { + has_meaningful_mtime = false; + None + }, + Some(data) => { + let (mode, size, mtime_info) = data; + let mtime = if let Some(mtime_info) = mtime_info { + let (mtime_s, mtime_ns, second_ambiguous) = mtime_info; + let timestamp = TruncatedTimestamp::new_truncate( + mtime_s, mtime_ns, second_ambiguous + ); + Some(timestamp) + } else { + has_meaningful_mtime = false; + None + }; + Some(ParentFileData { + mode_size: Some((mode, size)), + mtime, + }) + } + }; + let bytes = f.extract::(py)?; + let path = HgPath::new(bytes.data(py)); + let res = self.inner(py).borrow_mut().reset_state( + path, + wc_tracked, + p1_tracked, + p2_info, + has_meaningful_mtime, + parent_file_data, + ); + res.or_else(|_| { + Err(PyErr::new::(py, "Dirstate error".to_string())) + })?; + Ok(PyNone) + } + def removefile( &self, f: PyObject,