Show More
@@ -13,10 +13,12 b' use crate::HgPathBuf;' | |||||
13 | use crate::PatternFileWarning; |
|
13 | use crate::PatternFileWarning; | |
14 | use crate::StatusError; |
|
14 | use crate::StatusError; | |
15 | use crate::StatusOptions; |
|
15 | use crate::StatusOptions; | |
|
16 | use rayon::prelude::*; | |||
16 | use std::borrow::Cow; |
|
17 | use std::borrow::Cow; | |
17 | use std::io; |
|
18 | use std::io; | |
18 | use std::path::Path; |
|
19 | use std::path::Path; | |
19 | use std::path::PathBuf; |
|
20 | use std::path::PathBuf; | |
|
21 | use std::sync::Mutex; | |||
20 |
|
22 | |||
21 | /// Returns the status of the working directory compared to its parent |
|
23 | /// Returns the status of the working directory compared to its parent | |
22 | /// changeset. |
|
24 | /// changeset. | |
@@ -41,11 +43,11 b" pub fn status<'tree>(" | |||||
41 | (Box::new(|&_| true), vec![]) |
|
43 | (Box::new(|&_| true), vec![]) | |
42 | }; |
|
44 | }; | |
43 |
|
45 | |||
44 |
let |
|
46 | let common = StatusCommon { | |
45 | options, |
|
47 | options, | |
46 | matcher, |
|
48 | matcher, | |
47 | ignore_fn, |
|
49 | ignore_fn, | |
48 | outcome: DirstateStatus::default(), |
|
50 | outcome: Mutex::new(DirstateStatus::default()), | |
49 | }; |
|
51 | }; | |
50 | let is_at_repo_root = true; |
|
52 | let is_at_repo_root = true; | |
51 | let hg_path = HgPath::new(""); |
|
53 | let hg_path = HgPath::new(""); | |
@@ -57,7 +59,7 b" pub fn status<'tree>(" | |||||
57 | &root_dir, |
|
59 | &root_dir, | |
58 | is_at_repo_root, |
|
60 | is_at_repo_root, | |
59 | ); |
|
61 | ); | |
60 | Ok((common.outcome, warnings)) |
|
62 | Ok((common.outcome.into_inner().unwrap(), warnings)) | |
61 | } |
|
63 | } | |
62 |
|
64 | |||
63 | /// Bag of random things needed by various parts of the algorithm. Reduces the |
|
65 | /// Bag of random things needed by various parts of the algorithm. Reduces the | |
@@ -66,12 +68,12 b" struct StatusCommon<'tree, 'a> {" | |||||
66 | options: StatusOptions, |
|
68 | options: StatusOptions, | |
67 | matcher: &'a (dyn Matcher + Sync), |
|
69 | matcher: &'a (dyn Matcher + Sync), | |
68 | ignore_fn: IgnoreFnType<'a>, |
|
70 | ignore_fn: IgnoreFnType<'a>, | |
69 | outcome: DirstateStatus<'tree>, |
|
71 | outcome: Mutex<DirstateStatus<'tree>>, | |
70 | } |
|
72 | } | |
71 |
|
73 | |||
72 | impl<'tree, 'a> StatusCommon<'tree, 'a> { |
|
74 | impl<'tree, 'a> StatusCommon<'tree, 'a> { | |
73 | fn read_dir( |
|
75 | fn read_dir( | |
74 |
& |
|
76 | &self, | |
75 | hg_path: &HgPath, |
|
77 | hg_path: &HgPath, | |
76 | fs_path: &Path, |
|
78 | fs_path: &Path, | |
77 | is_at_repo_root: bool, |
|
79 | is_at_repo_root: bool, | |
@@ -79,13 +81,15 b" impl<'tree, 'a> StatusCommon<'tree, 'a> " | |||||
79 | DirEntry::read_dir(fs_path, is_at_repo_root).map_err(|error| { |
|
81 | DirEntry::read_dir(fs_path, is_at_repo_root).map_err(|error| { | |
80 | let errno = error.raw_os_error().expect("expected real OS error"); |
|
82 | let errno = error.raw_os_error().expect("expected real OS error"); | |
81 | self.outcome |
|
83 | self.outcome | |
|
84 | .lock() | |||
|
85 | .unwrap() | |||
82 | .bad |
|
86 | .bad | |
83 | .push((hg_path.to_owned().into(), BadMatch::OsError(errno))) |
|
87 | .push((hg_path.to_owned().into(), BadMatch::OsError(errno))) | |
84 | }) |
|
88 | }) | |
85 | } |
|
89 | } | |
86 |
|
90 | |||
87 | fn traverse_fs_directory_and_dirstate( |
|
91 | fn traverse_fs_directory_and_dirstate( | |
88 |
& |
|
92 | &self, | |
89 | has_ignored_ancestor: bool, |
|
93 | has_ignored_ancestor: bool, | |
90 | dirstate_nodes: &'tree mut ChildNodes, |
|
94 | dirstate_nodes: &'tree mut ChildNodes, | |
91 | directory_hg_path: &'tree HgPath, |
|
95 | directory_hg_path: &'tree HgPath, | |
@@ -104,19 +108,22 b" impl<'tree, 'a> StatusCommon<'tree, 'a> " | |||||
104 |
|
108 | |||
105 | // `merge_join_by` requires both its input iterators to be sorted: |
|
109 | // `merge_join_by` requires both its input iterators to be sorted: | |
106 |
|
110 | |||
|
111 | // | |||
107 | // * `BTreeMap` iterates according to keysβ ordering by definition |
|
112 | // * `BTreeMap` iterates according to keysβ ordering by definition | |
108 |
|
113 | |||
109 | // `sort_unstable_by_key` doesnβt allow keys borrowing from the value: |
|
114 | // `sort_unstable_by_key` doesnβt allow keys borrowing from the value: | |
110 | // https://github.com/rust-lang/rust/issues/34162 |
|
115 | // https://github.com/rust-lang/rust/issues/34162 | |
111 | fs_entries.sort_unstable_by(|e1, e2| e1.base_name.cmp(&e2.base_name)); |
|
116 | fs_entries.sort_unstable_by(|e1, e2| e1.base_name.cmp(&e2.base_name)); | |
112 |
|
117 | |||
113 |
|
|
118 | itertools::merge_join_by( | |
114 | dirstate_nodes, |
|
119 | dirstate_nodes, | |
115 | &fs_entries, |
|
120 | &fs_entries, | |
116 | |(full_path, _node), fs_entry| { |
|
121 | |(full_path, _node), fs_entry| { | |
117 | full_path.base_name().cmp(&fs_entry.base_name) |
|
122 | full_path.base_name().cmp(&fs_entry.base_name) | |
118 | }, |
|
123 | }, | |
119 |
) |
|
124 | ) | |
|
125 | .par_bridge() | |||
|
126 | .for_each(|pair| { | |||
120 | use itertools::EitherOrBoth::*; |
|
127 | use itertools::EitherOrBoth::*; | |
121 | match pair { |
|
128 | match pair { | |
122 | Both((hg_path, dirstate_node), fs_entry) => { |
|
129 | Both((hg_path, dirstate_node), fs_entry) => { | |
@@ -137,11 +144,11 b" impl<'tree, 'a> StatusCommon<'tree, 'a> " | |||||
137 | fs_entry, |
|
144 | fs_entry, | |
138 | ), |
|
145 | ), | |
139 | } |
|
146 | } | |
140 | } |
|
147 | }) | |
141 | } |
|
148 | } | |
142 |
|
149 | |||
143 | fn traverse_fs_and_dirstate( |
|
150 | fn traverse_fs_and_dirstate( | |
144 |
& |
|
151 | &self, | |
145 | fs_entry: &DirEntry, |
|
152 | fs_entry: &DirEntry, | |
146 | hg_path: &'tree HgPath, |
|
153 | hg_path: &'tree HgPath, | |
147 | dirstate_node: &'tree mut Node, |
|
154 | dirstate_node: &'tree mut Node, | |
@@ -160,7 +167,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a> " | |||||
160 | } |
|
167 | } | |
161 | if file_type.is_dir() { |
|
168 | if file_type.is_dir() { | |
162 | if self.options.collect_traversed_dirs { |
|
169 | if self.options.collect_traversed_dirs { | |
163 | self.outcome.traversed.push(hg_path.into()) |
|
170 | self.outcome.lock().unwrap().traversed.push(hg_path.into()) | |
164 | } |
|
171 | } | |
165 | let is_ignored = has_ignored_ancestor || (self.ignore_fn)(hg_path); |
|
172 | let is_ignored = has_ignored_ancestor || (self.ignore_fn)(hg_path); | |
166 | let is_at_repo_root = false; |
|
173 | let is_at_repo_root = false; | |
@@ -177,14 +184,20 b" impl<'tree, 'a> StatusCommon<'tree, 'a> " | |||||
177 | if let Some(entry) = &dirstate_node.entry { |
|
184 | if let Some(entry) = &dirstate_node.entry { | |
178 | match entry.state { |
|
185 | match entry.state { | |
179 | EntryState::Added => { |
|
186 | EntryState::Added => { | |
180 | self.outcome.added.push(full_path) |
|
187 | self.outcome.lock().unwrap().added.push(full_path) | |
181 | } |
|
188 | } | |
182 |
EntryState::Removed => |
|
189 | EntryState::Removed => self | |
183 |
|
|
190 | .outcome | |
184 |
|
|
191 | .lock() | |
185 |
|
|
192 | .unwrap() | |
186 |
|
|
193 | .removed | |
187 |
|
|
194 | .push(full_path), | |
|
195 | EntryState::Merged => self | |||
|
196 | .outcome | |||
|
197 | .lock() | |||
|
198 | .unwrap() | |||
|
199 | .modified | |||
|
200 | .push(full_path), | |||
188 | EntryState::Normal => { |
|
201 | EntryState::Normal => { | |
189 | self.handle_normal_file( |
|
202 | self.handle_normal_file( | |
190 | full_path, |
|
203 | full_path, | |
@@ -219,7 +232,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a> " | |||||
219 | /// A file with `EntryState::Normal` in the dirstate was found in the |
|
232 | /// A file with `EntryState::Normal` in the dirstate was found in the | |
220 | /// filesystem |
|
233 | /// filesystem | |
221 | fn handle_normal_file( |
|
234 | fn handle_normal_file( | |
222 |
& |
|
235 | &self, | |
223 | full_path: Cow<'tree, HgPath>, |
|
236 | full_path: Cow<'tree, HgPath>, | |
224 | dirstate_node: &Node, |
|
237 | dirstate_node: &Node, | |
225 | entry: &crate::DirstateEntry, |
|
238 | entry: &crate::DirstateEntry, | |
@@ -243,34 +256,39 b" impl<'tree, 'a> StatusCommon<'tree, 'a> " | |||||
243 | { |
|
256 | { | |
244 | // issue6456: Size returned may be longer due to encryption |
|
257 | // issue6456: Size returned may be longer due to encryption | |
245 | // on EXT-4 fscrypt. TODO maybe only do it on EXT4? |
|
258 | // on EXT-4 fscrypt. TODO maybe only do it on EXT4? | |
246 | self.outcome.unsure.push(full_path) |
|
259 | self.outcome.lock().unwrap().unsure.push(full_path) | |
247 | } else if dirstate_node.copy_source.is_some() |
|
260 | } else if dirstate_node.copy_source.is_some() | |
248 | || entry.is_from_other_parent() |
|
261 | || entry.is_from_other_parent() | |
249 | || (entry.size >= 0 && (size_changed || mode_changed())) |
|
262 | || (entry.size >= 0 && (size_changed || mode_changed())) | |
250 | { |
|
263 | { | |
251 | self.outcome.modified.push(full_path) |
|
264 | self.outcome.lock().unwrap().modified.push(full_path) | |
252 | } else { |
|
265 | } else { | |
253 | let mtime = mtime_seconds(&fs_entry.metadata); |
|
266 | let mtime = mtime_seconds(&fs_entry.metadata); | |
254 | if truncate_i64(mtime) != entry.mtime |
|
267 | if truncate_i64(mtime) != entry.mtime | |
255 | || mtime == self.options.last_normal_time |
|
268 | || mtime == self.options.last_normal_time | |
256 | { |
|
269 | { | |
257 | self.outcome.unsure.push(full_path) |
|
270 | self.outcome.lock().unwrap().unsure.push(full_path) | |
258 | } else if self.options.list_clean { |
|
271 | } else if self.options.list_clean { | |
259 | self.outcome.clean.push(full_path) |
|
272 | self.outcome.lock().unwrap().clean.push(full_path) | |
260 | } |
|
273 | } | |
261 | } |
|
274 | } | |
262 | } |
|
275 | } | |
263 |
|
276 | |||
264 | /// A node in the dirstate tree has no corresponding filesystem entry |
|
277 | /// A node in the dirstate tree has no corresponding filesystem entry | |
265 | fn traverse_dirstate_only( |
|
278 | fn traverse_dirstate_only( | |
266 |
& |
|
279 | &self, | |
267 | hg_path: &'tree HgPath, |
|
280 | hg_path: &'tree HgPath, | |
268 | dirstate_node: &'tree mut Node, |
|
281 | dirstate_node: &'tree mut Node, | |
269 | ) { |
|
282 | ) { | |
270 | self.mark_removed_or_deleted_if_file(hg_path, dirstate_node.state()); |
|
283 | self.mark_removed_or_deleted_if_file(hg_path, dirstate_node.state()); | |
271 | for (child_hg_path, child_node) in &mut dirstate_node.children { |
|
284 | dirstate_node.children.par_iter_mut().for_each( | |
272 |
|
|
285 | |(child_hg_path, child_node)| { | |
273 | } |
|
286 | self.traverse_dirstate_only( | |
|
287 | child_hg_path.full_path(), | |||
|
288 | child_node, | |||
|
289 | ) | |||
|
290 | }, | |||
|
291 | ) | |||
274 | } |
|
292 | } | |
275 |
|
293 | |||
276 | /// A node in the dirstate tree has no corresponding *file* on the |
|
294 | /// A node in the dirstate tree has no corresponding *file* on the | |
@@ -278,16 +296,16 b" impl<'tree, 'a> StatusCommon<'tree, 'a> " | |||||
278 | /// |
|
296 | /// | |
279 | /// Does nothing on a "directory" node |
|
297 | /// Does nothing on a "directory" node | |
280 | fn mark_removed_or_deleted_if_file( |
|
298 | fn mark_removed_or_deleted_if_file( | |
281 |
& |
|
299 | &self, | |
282 | hg_path: &'tree HgPath, |
|
300 | hg_path: &'tree HgPath, | |
283 | dirstate_node_state: Option<EntryState>, |
|
301 | dirstate_node_state: Option<EntryState>, | |
284 | ) { |
|
302 | ) { | |
285 | if let Some(state) = dirstate_node_state { |
|
303 | if let Some(state) = dirstate_node_state { | |
286 | if self.matcher.matches(hg_path) { |
|
304 | if self.matcher.matches(hg_path) { | |
287 | if let EntryState::Removed = state { |
|
305 | if let EntryState::Removed = state { | |
288 | self.outcome.removed.push(hg_path.into()) |
|
306 | self.outcome.lock().unwrap().removed.push(hg_path.into()) | |
289 | } else { |
|
307 | } else { | |
290 | self.outcome.deleted.push(hg_path.into()) |
|
308 | self.outcome.lock().unwrap().deleted.push(hg_path.into()) | |
291 | } |
|
309 | } | |
292 | } |
|
310 | } | |
293 | } |
|
311 | } | |
@@ -295,7 +313,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a> " | |||||
295 |
|
313 | |||
296 | /// Something in the filesystem has no corresponding dirstate node |
|
314 | /// Something in the filesystem has no corresponding dirstate node | |
297 | fn traverse_fs_only( |
|
315 | fn traverse_fs_only( | |
298 |
& |
|
316 | &self, | |
299 | has_ignored_ancestor: bool, |
|
317 | has_ignored_ancestor: bool, | |
300 | directory_hg_path: &HgPath, |
|
318 | directory_hg_path: &HgPath, | |
301 | fs_entry: &DirEntry, |
|
319 | fs_entry: &DirEntry, | |
@@ -321,17 +339,17 b" impl<'tree, 'a> StatusCommon<'tree, 'a> " | |||||
321 | &fs_entry.full_path, |
|
339 | &fs_entry.full_path, | |
322 | is_at_repo_root, |
|
340 | is_at_repo_root, | |
323 | ) { |
|
341 | ) { | |
324 | for child_fs_entry in children_fs_entries { |
|
342 | children_fs_entries.par_iter().for_each(|child_fs_entry| { | |
325 | self.traverse_fs_only( |
|
343 | self.traverse_fs_only( | |
326 | is_ignored, |
|
344 | is_ignored, | |
327 | &hg_path, |
|
345 | &hg_path, | |
328 |
|
|
346 | child_fs_entry, | |
329 | ) |
|
347 | ) | |
330 | } |
|
348 | }) | |
331 | } |
|
349 | } | |
332 | } |
|
350 | } | |
333 | if self.options.collect_traversed_dirs { |
|
351 | if self.options.collect_traversed_dirs { | |
334 | self.outcome.traversed.push(hg_path.into()) |
|
352 | self.outcome.lock().unwrap().traversed.push(hg_path.into()) | |
335 | } |
|
353 | } | |
336 | } else if file_or_symlink && self.matcher.matches(&hg_path) { |
|
354 | } else if file_or_symlink && self.matcher.matches(&hg_path) { | |
337 | self.mark_unknown_or_ignored(has_ignored_ancestor, hg_path.into()) |
|
355 | self.mark_unknown_or_ignored(has_ignored_ancestor, hg_path.into()) | |
@@ -339,18 +357,18 b" impl<'tree, 'a> StatusCommon<'tree, 'a> " | |||||
339 | } |
|
357 | } | |
340 |
|
358 | |||
341 | fn mark_unknown_or_ignored( |
|
359 | fn mark_unknown_or_ignored( | |
342 |
& |
|
360 | &self, | |
343 | has_ignored_ancestor: bool, |
|
361 | has_ignored_ancestor: bool, | |
344 | hg_path: Cow<'tree, HgPath>, |
|
362 | hg_path: Cow<'tree, HgPath>, | |
345 | ) { |
|
363 | ) { | |
346 | let is_ignored = has_ignored_ancestor || (self.ignore_fn)(&hg_path); |
|
364 | let is_ignored = has_ignored_ancestor || (self.ignore_fn)(&hg_path); | |
347 | if is_ignored { |
|
365 | if is_ignored { | |
348 | if self.options.list_ignored { |
|
366 | if self.options.list_ignored { | |
349 | self.outcome.ignored.push(hg_path) |
|
367 | self.outcome.lock().unwrap().ignored.push(hg_path) | |
350 | } |
|
368 | } | |
351 | } else { |
|
369 | } else { | |
352 | if self.options.list_unknown { |
|
370 | if self.options.list_unknown { | |
353 | self.outcome.unknown.push(hg_path) |
|
371 | self.outcome.lock().unwrap().unknown.push(hg_path) | |
354 | } |
|
372 | } | |
355 | } |
|
373 | } | |
356 | } |
|
374 | } |
General Comments 0
You need to be logged in to leave comments.
Login now