##// END OF EJS Templates
rust: Remove the compile-time 'dirstate-tree' feature flag...
Simon Sapin -
r47652:441024b2 default
parent child Browse files
Show More
@@ -41,9 +41,3 b' default-features = false'
41 clap = "*"
41 clap = "*"
42 pretty_assertions = "0.6.1"
42 pretty_assertions = "0.6.1"
43 tempfile = "3.1.0"
43 tempfile = "3.1.0"
44
45 [features]
46 # Use a (still unoptimized) tree for the dirstate instead of the current flat
47 # dirstate. This is not yet recommended for performance reasons. A future
48 # version might make it the default, or make it a runtime option.
49 dirstate-tree = []
@@ -14,8 +14,6 b' use std::convert::TryFrom;'
14
14
15 pub mod dirs_multiset;
15 pub mod dirs_multiset;
16 pub mod dirstate_map;
16 pub mod dirstate_map;
17 #[cfg(feature = "dirstate-tree")]
18 pub mod dirstate_tree;
19 pub mod parsers;
17 pub mod parsers;
20 pub mod status;
18 pub mod status;
21
19
@@ -52,15 +50,9 b' struct RawEntry {'
52 /// merge.
50 /// merge.
53 pub const SIZE_FROM_OTHER_PARENT: i32 = -2;
51 pub const SIZE_FROM_OTHER_PARENT: i32 = -2;
54
52
55 #[cfg(not(feature = "dirstate-tree"))]
56 pub type StateMap = FastHashMap<HgPathBuf, DirstateEntry>;
53 pub type StateMap = FastHashMap<HgPathBuf, DirstateEntry>;
57 #[cfg(not(feature = "dirstate-tree"))]
58 pub type StateMapIter<'a> = hash_map::Iter<'a, HgPathBuf, DirstateEntry>;
54 pub type StateMapIter<'a> = hash_map::Iter<'a, HgPathBuf, DirstateEntry>;
59
55
60 #[cfg(feature = "dirstate-tree")]
61 pub type StateMap = dirstate_tree::tree::Tree;
62 #[cfg(feature = "dirstate-tree")]
63 pub type StateMapIter<'a> = dirstate_tree::iter::Iter<'a>;
64 pub type CopyMap = FastHashMap<HgPathBuf, HgPathBuf>;
56 pub type CopyMap = FastHashMap<HgPathBuf, HgPathBuf>;
65 pub type CopyMapIter<'a> = hash_map::Iter<'a, HgPathBuf, HgPathBuf>;
57 pub type CopyMapIter<'a> = hash_map::Iter<'a, HgPathBuf, HgPathBuf>;
66
58
@@ -30,7 +30,6 b' impl DirsMultiset {'
30 /// Initializes the multiset from a dirstate.
30 /// Initializes the multiset from a dirstate.
31 ///
31 ///
32 /// If `skip_state` is provided, skips dirstate entries with equal state.
32 /// If `skip_state` is provided, skips dirstate entries with equal state.
33 #[cfg(not(feature = "dirstate-tree"))]
34 pub fn from_dirstate(
33 pub fn from_dirstate(
35 dirstate: &StateMap,
34 dirstate: &StateMap,
36 skip_state: Option<EntryState>,
35 skip_state: Option<EntryState>,
@@ -51,30 +50,6 b' impl DirsMultiset {'
51
50
52 Ok(multiset)
51 Ok(multiset)
53 }
52 }
54 /// Initializes the multiset from a dirstate.
55 ///
56 /// If `skip_state` is provided, skips dirstate entries with equal state.
57 #[cfg(feature = "dirstate-tree")]
58 pub fn from_dirstate(
59 dirstate: &StateMap,
60 skip_state: Option<EntryState>,
61 ) -> Result<Self, DirstateMapError> {
62 let mut multiset = DirsMultiset {
63 inner: FastHashMap::default(),
64 };
65 for (filename, DirstateEntry { state, .. }) in dirstate.iter() {
66 // This `if` is optimized out of the loop
67 if let Some(skip) = skip_state {
68 if skip != state {
69 multiset.add_path(filename)?;
70 }
71 } else {
72 multiset.add_path(filename)?;
73 }
74 }
75
76 Ok(multiset)
77 }
78
53
79 /// Initializes the multiset from a manifest.
54 /// Initializes the multiset from a manifest.
80 pub fn from_manifest(
55 pub fn from_manifest(
@@ -254,7 +254,6 b' impl DirstateMap {'
254 )
254 )
255 }
255 }
256
256
257 #[cfg(not(feature = "dirstate-tree"))]
258 pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
257 pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
259 if !force
258 if !force
260 && self.non_normal_set.is_some()
259 && self.non_normal_set.is_some()
@@ -283,34 +282,6 b' impl DirstateMap {'
283 self.non_normal_set = Some(non_normal);
282 self.non_normal_set = Some(non_normal);
284 self.other_parent_set = Some(other_parent);
283 self.other_parent_set = Some(other_parent);
285 }
284 }
286 #[cfg(feature = "dirstate-tree")]
287 pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
288 if !force
289 && self.non_normal_set.is_some()
290 && self.other_parent_set.is_some()
291 {
292 return;
293 }
294 let mut non_normal = HashSet::new();
295 let mut other_parent = HashSet::new();
296
297 for (
298 filename,
299 DirstateEntry {
300 state, size, mtime, ..
301 },
302 ) in self.state_map.iter()
303 {
304 if state != EntryState::Normal || mtime == MTIME_UNSET {
305 non_normal.insert(filename.to_owned());
306 }
307 if state == EntryState::Normal && size == SIZE_FROM_OTHER_PARENT {
308 other_parent.insert(filename.to_owned());
309 }
310 }
311 self.non_normal_set = Some(non_normal);
312 self.other_parent_set = Some(other_parent);
313 }
314
285
315 /// Both of these setters and their uses appear to be the simplest way to
286 /// Both of these setters and their uses appear to be the simplest way to
316 /// emulate a Python lazy property, but it is ugly and unidiomatic.
287 /// emulate a Python lazy property, but it is ugly and unidiomatic.
@@ -426,7 +397,6 b' impl DirstateMap {'
426 self.set_non_normal_other_parent_entries(true);
397 self.set_non_normal_other_parent_entries(true);
427 Ok(packed)
398 Ok(packed)
428 }
399 }
429 #[cfg(not(feature = "dirstate-tree"))]
430 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
400 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
431 if let Some(ref file_fold_map) = self.file_fold_map {
401 if let Some(ref file_fold_map) = self.file_fold_map {
432 return file_fold_map;
402 return file_fold_map;
@@ -442,22 +412,6 b' impl DirstateMap {'
442 self.file_fold_map = Some(new_file_fold_map);
412 self.file_fold_map = Some(new_file_fold_map);
443 self.file_fold_map.as_ref().unwrap()
413 self.file_fold_map.as_ref().unwrap()
444 }
414 }
445 #[cfg(feature = "dirstate-tree")]
446 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
447 if let Some(ref file_fold_map) = self.file_fold_map {
448 return file_fold_map;
449 }
450 let mut new_file_fold_map = FileFoldMap::default();
451
452 for (filename, DirstateEntry { state, .. }) in self.state_map.iter() {
453 if state != EntryState::Removed {
454 new_file_fold_map
455 .insert(normalize_case(&filename), filename.to_owned());
456 }
457 }
458 self.file_fold_map = Some(new_file_fold_map);
459 self.file_fold_map.as_ref().unwrap()
460 }
461 }
415 }
462
416
463 #[cfg(test)]
417 #[cfg(test)]
@@ -73,7 +73,6 b' pub fn parse_dirstate(mut contents: &[u8'
73 }
73 }
74
74
75 /// `now` is the duration in seconds since the Unix epoch
75 /// `now` is the duration in seconds since the Unix epoch
76 #[cfg(not(feature = "dirstate-tree"))]
77 pub fn pack_dirstate(
76 pub fn pack_dirstate(
78 state_map: &mut StateMap,
77 state_map: &mut StateMap,
79 copy_map: &CopyMap,
78 copy_map: &CopyMap,
@@ -146,79 +145,6 b' pub fn pack_dirstate('
146
145
147 Ok(packed)
146 Ok(packed)
148 }
147 }
149 /// `now` is the duration in seconds since the Unix epoch
150 #[cfg(feature = "dirstate-tree")]
151 pub fn pack_dirstate(
152 state_map: &mut StateMap,
153 copy_map: &CopyMap,
154 parents: DirstateParents,
155 now: Duration,
156 ) -> Result<Vec<u8>, DirstatePackError> {
157 // TODO move away from i32 before 2038.
158 let now: i32 = now.as_secs().try_into().expect("time overflow");
159
160 let expected_size: usize = state_map
161 .iter()
162 .map(|(filename, _)| {
163 let mut length = MIN_ENTRY_SIZE + filename.len();
164 if let Some(copy) = copy_map.get(&filename) {
165 length += copy.len() + 1;
166 }
167 length
168 })
169 .sum();
170 let expected_size = expected_size + PARENT_SIZE * 2;
171
172 let mut packed = Vec::with_capacity(expected_size);
173 let mut new_state_map = vec![];
174
175 packed.extend(&parents.p1);
176 packed.extend(&parents.p2);
177
178 for (filename, entry) in state_map.iter() {
179 let new_filename = filename.to_owned();
180 let mut new_mtime: i32 = entry.mtime;
181 if entry.state == EntryState::Normal && entry.mtime == now {
182 // The file was last modified "simultaneously" with the current
183 // write to dirstate (i.e. within the same second for file-
184 // systems with a granularity of 1 sec). This commonly happens
185 // for at least a couple of files on 'update'.
186 // The user could change the file without changing its size
187 // within the same second. Invalidate the file's mtime in
188 // dirstate, forcing future 'status' calls to compare the
189 // contents of the file if the size is the same. This prevents
190 // mistakenly treating such files as clean.
191 new_mtime = -1;
192 new_state_map.push((
193 filename.to_owned(),
194 DirstateEntry {
195 mtime: new_mtime,
196 ..entry
197 },
198 ));
199 }
200 let mut new_filename = new_filename.into_vec();
201 if let Some(copy) = copy_map.get(&filename) {
202 new_filename.push(b'\0');
203 new_filename.extend(copy.bytes());
204 }
205
206 packed.write_u8(entry.state.into())?;
207 packed.write_i32::<BigEndian>(entry.mode)?;
208 packed.write_i32::<BigEndian>(entry.size)?;
209 packed.write_i32::<BigEndian>(new_mtime)?;
210 packed.write_i32::<BigEndian>(new_filename.len() as i32)?;
211 packed.extend(new_filename)
212 }
213
214 if packed.len() != expected_size {
215 return Err(DirstatePackError::BadSize(expected_size, packed.len()));
216 }
217
218 state_map.extend(new_state_map);
219
220 Ok(packed)
221 }
222
148
223 #[cfg(test)]
149 #[cfg(test)]
224 mod tests {
150 mod tests {
@@ -9,9 +9,6 b''
9 //! It is currently missing a lot of functionality compared to the Python one
9 //! It is currently missing a lot of functionality compared to the Python one
10 //! and will only be triggered in narrow cases.
10 //! and will only be triggered in narrow cases.
11
11
12 #[cfg(feature = "dirstate-tree")]
13 use crate::dirstate::dirstate_tree::iter::StatusShortcut;
14 #[cfg(not(feature = "dirstate-tree"))]
15 use crate::utils::path_auditor::PathAuditor;
12 use crate::utils::path_auditor::PathAuditor;
16 use crate::{
13 use crate::{
17 dirstate::SIZE_FROM_OTHER_PARENT,
14 dirstate::SIZE_FROM_OTHER_PARENT,
@@ -703,83 +700,6 b' where'
703 ///
700 ///
704 /// This takes a mutable reference to the results to account for the
701 /// This takes a mutable reference to the results to account for the
705 /// `extend` in timings
702 /// `extend` in timings
706 #[cfg(feature = "dirstate-tree")]
707 #[timed]
708 pub fn extend_from_dmap(&self, results: &mut Vec<DispatchedPath<'a>>) {
709 results.par_extend(
710 self.dmap
711 .fs_iter(self.root_dir.clone())
712 .par_bridge()
713 .filter(|(path, _)| self.matcher.matches(path))
714 .map(move |(filename, shortcut)| {
715 let entry = match shortcut {
716 StatusShortcut::Entry(e) => e,
717 StatusShortcut::Dispatch(d) => {
718 return (Cow::Owned(filename), d)
719 }
720 };
721 let filename_as_path = match hg_path_to_path_buf(&filename)
722 {
723 Ok(f) => f,
724 Err(_) => {
725 return (
726 Cow::Owned(filename),
727 INVALID_PATH_DISPATCH,
728 )
729 }
730 };
731 let meta = self
732 .root_dir
733 .join(filename_as_path)
734 .symlink_metadata();
735
736 match meta {
737 Ok(m)
738 if !(m.file_type().is_file()
739 || m.file_type().is_symlink()) =>
740 {
741 (
742 Cow::Owned(filename),
743 dispatch_missing(entry.state),
744 )
745 }
746 Ok(m) => {
747 let dispatch = dispatch_found(
748 &filename,
749 entry,
750 HgMetadata::from_metadata(m),
751 &self.dmap.copy_map,
752 self.options,
753 );
754 (Cow::Owned(filename), dispatch)
755 }
756 Err(e)
757 if e.kind() == ErrorKind::NotFound
758 || e.raw_os_error() == Some(20) =>
759 {
760 // Rust does not yet have an `ErrorKind` for
761 // `NotADirectory` (errno 20)
762 // It happens if the dirstate contains `foo/bar`
763 // and foo is not a
764 // directory
765 (
766 Cow::Owned(filename),
767 dispatch_missing(entry.state),
768 )
769 }
770 Err(e) => {
771 (Cow::Owned(filename), dispatch_os_error(&e))
772 }
773 }
774 }),
775 );
776 }
777
778 /// Add the files in the dirstate to the results.
779 ///
780 /// This takes a mutable reference to the results to account for the
781 /// `extend` in timings
782 #[cfg(not(feature = "dirstate-tree"))]
783 #[timed]
703 #[timed]
784 pub fn extend_from_dmap(&self, results: &mut Vec<DispatchedPath<'a>>) {
704 pub fn extend_from_dmap(&self, results: &mut Vec<DispatchedPath<'a>>) {
785 results.par_extend(
705 results.par_extend(
@@ -850,7 +770,6 b' where'
850 ///
770 ///
851 /// This takes a mutable reference to the results to account for the
771 /// This takes a mutable reference to the results to account for the
852 /// `extend` in timings
772 /// `extend` in timings
853 #[cfg(not(feature = "dirstate-tree"))]
854 #[timed]
773 #[timed]
855 pub fn handle_unknowns(&self, results: &mut Vec<DispatchedPath<'a>>) {
774 pub fn handle_unknowns(&self, results: &mut Vec<DispatchedPath<'a>>) {
856 let to_visit: Vec<(&HgPath, &DirstateEntry)> =
775 let to_visit: Vec<(&HgPath, &DirstateEntry)> =
@@ -14,66 +14,6 b' use crate::{DirstateStatus, StatusError}'
14 /// files.
14 /// files.
15 pub type LookupAndStatus<'a> = (Vec<HgPathCow<'a>>, DirstateStatus<'a>);
15 pub type LookupAndStatus<'a> = (Vec<HgPathCow<'a>>, DirstateStatus<'a>);
16
16
17 #[cfg(feature = "dirstate-tree")]
18 impl<'a, M: Matcher + Sync> Status<'a, M> {
19 pub(crate) fn run(&self) -> Result<LookupAndStatus<'a>, StatusError> {
20 let (traversed_sender, traversed_receiver) =
21 crossbeam_channel::unbounded();
22
23 // Step 1: check the files explicitly mentioned by the user
24 let (work, mut results) = self.walk_explicit(traversed_sender.clone());
25
26 // Step 2: Check files in the dirstate
27 if !self.matcher.is_exact() {
28 self.extend_from_dmap(&mut results);
29 }
30 // Step 3: Check the working directory if listing unknowns
31 if !work.is_empty() {
32 // Hashmaps are quite a bit slower to build than vecs, so only
33 // build it if needed.
34 let mut old_results = None;
35
36 // Step 2: recursively check the working directory for changes if
37 // needed
38 for (dir, dispatch) in work {
39 match dispatch {
40 Dispatch::Directory { was_file } => {
41 if was_file {
42 results.push((dir.to_owned(), Dispatch::Removed));
43 }
44 if self.options.list_ignored
45 || self.options.list_unknown
46 && !self.dir_ignore(&dir)
47 {
48 if old_results.is_none() {
49 old_results =
50 Some(results.iter().cloned().collect());
51 }
52 self.traverse(
53 &dir,
54 old_results
55 .as_ref()
56 .expect("old results should exist"),
57 &mut results,
58 traversed_sender.clone(),
59 );
60 }
61 }
62 _ => {
63 unreachable!("There can only be directories in `work`")
64 }
65 }
66 }
67 }
68
69 drop(traversed_sender);
70 let traversed = traversed_receiver.into_iter().collect();
71
72 Ok(build_response(results, traversed))
73 }
74 }
75
76 #[cfg(not(feature = "dirstate-tree"))]
77 impl<'a, M: Matcher + Sync> Status<'a, M> {
17 impl<'a, M: Matcher + Sync> Status<'a, M> {
78 pub(crate) fn run(&self) -> Result<LookupAndStatus<'a>, StatusError> {
18 pub(crate) fn run(&self) -> Result<LookupAndStatus<'a>, StatusError> {
79 let (traversed_sender, traversed_receiver) =
19 let (traversed_sender, traversed_receiver) =
@@ -10,7 +10,6 b' crate-type = ["cdylib"]'
10
10
11 [features]
11 [features]
12 default = ["python27"]
12 default = ["python27"]
13 dirstate-tree = ["hg-core/dirstate-tree"]
14
13
15 # Features to build an extension module:
14 # Features to build an extension module:
16 python27 = ["cpython/python27-sys", "cpython/extension-module-2-7"]
15 python27 = ["cpython/python27-sys", "cpython/extension-module-2-7"]
@@ -547,14 +547,12 b' impl DirstateMap {'
547 ) -> Ref<'a, RustDirstateMap> {
547 ) -> Ref<'a, RustDirstateMap> {
548 self.inner(py).borrow()
548 self.inner(py).borrow()
549 }
549 }
550 #[cfg(not(feature = "dirstate-tree"))]
551 fn translate_key(
550 fn translate_key(
552 py: Python,
551 py: Python,
553 res: (&HgPathBuf, &DirstateEntry),
552 res: (&HgPathBuf, &DirstateEntry),
554 ) -> PyResult<Option<PyBytes>> {
553 ) -> PyResult<Option<PyBytes>> {
555 Ok(Some(PyBytes::new(py, res.0.as_bytes())))
554 Ok(Some(PyBytes::new(py, res.0.as_bytes())))
556 }
555 }
557 #[cfg(not(feature = "dirstate-tree"))]
558 fn translate_key_value(
556 fn translate_key_value(
559 py: Python,
557 py: Python,
560 res: (&HgPathBuf, &DirstateEntry),
558 res: (&HgPathBuf, &DirstateEntry),
@@ -565,24 +563,6 b' impl DirstateMap {'
565 make_dirstate_tuple(py, &entry)?,
563 make_dirstate_tuple(py, &entry)?,
566 )))
564 )))
567 }
565 }
568 #[cfg(feature = "dirstate-tree")]
569 fn translate_key(
570 py: Python,
571 res: (HgPathBuf, DirstateEntry),
572 ) -> PyResult<Option<PyBytes>> {
573 Ok(Some(PyBytes::new(py, res.0.as_bytes())))
574 }
575 #[cfg(feature = "dirstate-tree")]
576 fn translate_key_value(
577 py: Python,
578 res: (HgPathBuf, DirstateEntry),
579 ) -> PyResult<Option<(PyBytes, PyObject)>> {
580 let (f, entry) = res;
581 Ok(Some((
582 PyBytes::new(py, f.as_bytes()),
583 make_dirstate_tuple(py, &entry)?,
584 )))
585 }
586 }
566 }
587
567
588 py_shared_iterator!(
568 py_shared_iterator!(
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (682 lines changed) Show them Hide them
General Comments 0
You need to be logged in to leave comments. Login now