Show More
@@ -292,7 +292,7 b" pub struct DirstateStatus<'a> {" | |||
|
292 | 292 | pub unsure: Vec<HgPathCow<'a>>, |
|
293 | 293 | |
|
294 | 294 | /// Only filled if `collect_traversed_dirs` is `true` |
|
295 |
pub traversed: Vec<HgPath |
|
|
295 | pub traversed: Vec<HgPathCow<'a>>, | |
|
296 | 296 | } |
|
297 | 297 | |
|
298 | 298 | #[derive(Debug, derive_more::From)] |
@@ -880,7 +880,7 b' where' | |||
|
880 | 880 | #[timed] |
|
881 | 881 | pub fn build_response<'a>( |
|
882 | 882 | results: impl IntoIterator<Item = DispatchedPath<'a>>, |
|
883 |
traversed: Vec<HgPath |
|
|
883 | traversed: Vec<HgPathCow<'a>>, | |
|
884 | 884 | ) -> DirstateStatus<'a> { |
|
885 | 885 | let mut unsure = vec![]; |
|
886 | 886 | let mut modified = vec![]; |
@@ -46,6 +46,13 b" pub struct DirstateMap<'on_disk> {" | |||
|
46 | 46 | /// string prefix. |
|
47 | 47 | pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>; |
|
48 | 48 | |
|
49 | /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned | |
|
50 | /// for on-disk nodes that don’t actually have a `Cow` to borrow. | |
|
51 | pub(super) enum BorrowedPath<'tree, 'on_disk> { | |
|
52 | InMemory(&'tree HgPathBuf), | |
|
53 | OnDisk(&'on_disk HgPath), | |
|
54 | } | |
|
55 | ||
|
49 | 56 | pub(super) enum ChildNodes<'on_disk> { |
|
50 | 57 | InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>), |
|
51 | 58 | OnDisk(&'on_disk [on_disk::Node]), |
@@ -61,6 +68,26 b" pub(super) enum NodeRef<'tree, 'on_disk>" | |||
|
61 | 68 | OnDisk(&'on_disk on_disk::Node), |
|
62 | 69 | } |
|
63 | 70 | |
|
71 | impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> { | |
|
72 | pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> { | |
|
73 | match *self { | |
|
74 | BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()), | |
|
75 | BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk), | |
|
76 | } | |
|
77 | } | |
|
78 | } | |
|
79 | ||
|
80 | impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> { | |
|
81 | type Target = HgPath; | |
|
82 | ||
|
83 | fn deref(&self) -> &HgPath { | |
|
84 | match *self { | |
|
85 | BorrowedPath::InMemory(in_memory) => in_memory, | |
|
86 | BorrowedPath::OnDisk(on_disk) => on_disk, | |
|
87 | } | |
|
88 | } | |
|
89 | } | |
|
90 | ||
|
64 | 91 | impl Default for ChildNodes<'_> { |
|
65 | 92 | fn default() -> Self { |
|
66 | 93 | ChildNodes::InMemory(Default::default()) |
@@ -210,15 +237,19 b" impl<'tree, 'on_disk> NodeRef<'tree, 'on" | |||
|
210 | 237 | } |
|
211 | 238 | } |
|
212 | 239 | |
|
213 | /// Returns a `Cow` that can borrow 'on_disk but is detached from 'tree | |
|
214 | pub(super) fn full_path_cow( | |
|
240 | /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk, | |
|
241 | /// HgPath>` detached from `'tree` | |
|
242 | pub(super) fn full_path_borrowed( | |
|
215 | 243 | &self, |
|
216 | 244 | on_disk: &'on_disk [u8], |
|
217 |
) -> Result< |
|
|
245 | ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> { | |
|
218 | 246 | match self { |
|
219 |
NodeRef::InMemory(path, _node) => |
|
|
247 | NodeRef::InMemory(path, _node) => match path.full_path() { | |
|
248 | Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)), | |
|
249 | Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)), | |
|
250 | }, | |
|
220 | 251 | NodeRef::OnDisk(node) => { |
|
221 |
Ok( |
|
|
252 | Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?)) | |
|
222 | 253 | } |
|
223 | 254 | } |
|
224 | 255 | } |
@@ -819,7 +850,10 b" impl<'on_disk> super::dispatch::Dirstate" | |||
|
819 | 850 | node.copy_source(self.on_disk)?, |
|
820 | 851 | ); |
|
821 | 852 | if entry.mtime_is_ambiguous(now) { |
|
822 |
ambiguous_mtimes.push( |
|
|
853 | ambiguous_mtimes.push( | |
|
854 | node.full_path_borrowed(self.on_disk)? | |
|
855 | .detach_from_tree(), | |
|
856 | ) | |
|
823 | 857 | } |
|
824 | 858 | } |
|
825 | 859 | } |
@@ -855,7 +889,10 b" impl<'on_disk> super::dispatch::Dirstate" | |||
|
855 | 889 | let node = node?; |
|
856 | 890 | if let Some(entry) = node.entry()? { |
|
857 | 891 | if entry.mtime_is_ambiguous(now) { |
|
858 |
paths.push( |
|
|
892 | paths.push( | |
|
893 | node.full_path_borrowed(self.on_disk)? | |
|
894 | .detach_from_tree(), | |
|
895 | ) | |
|
859 | 896 | } |
|
860 | 897 | } |
|
861 | 898 | } |
@@ -1,4 +1,5 b'' | |||
|
1 | 1 | use crate::dirstate::status::IgnoreFnType; |
|
2 | use crate::dirstate_tree::dirstate_map::BorrowedPath; | |
|
2 | 3 | use crate::dirstate_tree::dirstate_map::ChildNodesRef; |
|
3 | 4 | use crate::dirstate_tree::dirstate_map::DirstateMap; |
|
4 | 5 | use crate::dirstate_tree::dirstate_map::NodeRef; |
@@ -17,7 +18,6 b' use crate::StatusError;' | |||
|
17 | 18 | use crate::StatusOptions; |
|
18 | 19 | use micro_timer::timed; |
|
19 | 20 | use rayon::prelude::*; |
|
20 | use std::borrow::Cow; | |
|
21 | 21 | use std::io; |
|
22 | 22 | use std::path::Path; |
|
23 | 23 | use std::path::PathBuf; |
@@ -39,7 +39,7 b" pub fn status<'tree, 'on_disk: 'tree>(" | |||
|
39 | 39 | root_dir: PathBuf, |
|
40 | 40 | ignore_files: Vec<PathBuf>, |
|
41 | 41 | options: StatusOptions, |
|
42 |
) -> Result<(DirstateStatus<' |
|
|
42 | ) -> Result<(DirstateStatus<'on_disk>, Vec<PatternFileWarning>), StatusError> { | |
|
43 | 43 | let (ignore_fn, warnings): (IgnoreFnType, _) = |
|
44 | 44 | if options.list_ignored || options.list_unknown { |
|
45 | 45 | get_ignore_function(ignore_files, &root_dir)? |
@@ -55,7 +55,7 b" pub fn status<'tree, 'on_disk: 'tree>(" | |||
|
55 | 55 | outcome: Mutex::new(DirstateStatus::default()), |
|
56 | 56 | }; |
|
57 | 57 | let is_at_repo_root = true; |
|
58 | let hg_path = HgPath::new(""); | |
|
58 | let hg_path = &BorrowedPath::OnDisk(HgPath::new("")); | |
|
59 | 59 | let has_ignored_ancestor = false; |
|
60 | 60 | common.traverse_fs_directory_and_dirstate( |
|
61 | 61 | has_ignored_ancestor, |
@@ -69,15 +69,15 b" pub fn status<'tree, 'on_disk: 'tree>(" | |||
|
69 | 69 | |
|
70 | 70 | /// Bag of random things needed by various parts of the algorithm. Reduces the |
|
71 | 71 | /// number of parameters passed to functions. |
|
72 |
struct StatusCommon<' |
|
|
72 | struct StatusCommon<'a, 'tree, 'on_disk: 'tree> { | |
|
73 | 73 | dmap: &'tree DirstateMap<'on_disk>, |
|
74 | 74 | options: StatusOptions, |
|
75 | 75 | matcher: &'a (dyn Matcher + Sync), |
|
76 | 76 | ignore_fn: IgnoreFnType<'a>, |
|
77 |
outcome: Mutex<DirstateStatus<' |
|
|
77 | outcome: Mutex<DirstateStatus<'on_disk>>, | |
|
78 | 78 | } |
|
79 | 79 | |
|
80 |
impl<'tree, ' |
|
|
80 | impl<'a, 'tree, 'on_disk> StatusCommon<'a, 'tree, 'on_disk> { | |
|
81 | 81 | fn read_dir( |
|
82 | 82 | &self, |
|
83 | 83 | hg_path: &HgPath, |
@@ -100,8 +100,8 b" impl<'tree, 'a> StatusCommon<'tree, 'a, " | |||
|
100 | 100 | fn traverse_fs_directory_and_dirstate( |
|
101 | 101 | &self, |
|
102 | 102 | has_ignored_ancestor: bool, |
|
103 | dirstate_nodes: ChildNodesRef<'tree, '_>, | |
|
104 |
directory_hg_path: &'tree |
|
|
103 | dirstate_nodes: ChildNodesRef<'tree, 'on_disk>, | |
|
104 | directory_hg_path: &BorrowedPath<'tree, 'on_disk>, | |
|
105 | 105 | directory_fs_path: &Path, |
|
106 | 106 | is_at_repo_root: bool, |
|
107 | 107 | ) -> Result<(), DirstateV2ParseError> { |
@@ -199,10 +199,10 b" impl<'tree, 'a> StatusCommon<'tree, 'a, " | |||
|
199 | 199 | &self, |
|
200 | 200 | fs_path: &Path, |
|
201 | 201 | fs_metadata: &std::fs::Metadata, |
|
202 | dirstate_node: NodeRef<'tree, '_>, | |
|
202 | dirstate_node: NodeRef<'tree, 'on_disk>, | |
|
203 | 203 | has_ignored_ancestor: bool, |
|
204 | 204 | ) -> Result<(), DirstateV2ParseError> { |
|
205 | let hg_path = dirstate_node.full_path(self.dmap.on_disk)?; | |
|
205 | let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?; | |
|
206 | 206 | let file_type = fs_metadata.file_type(); |
|
207 | 207 | let file_or_symlink = file_type.is_file() || file_type.is_symlink(); |
|
208 | 208 | if !file_or_symlink { |
@@ -210,13 +210,17 b" impl<'tree, 'a> StatusCommon<'tree, 'a, " | |||
|
210 | 210 | // `hg rm` or similar) or deleted before it could be |
|
211 | 211 | // replaced by a directory or something else. |
|
212 | 212 | self.mark_removed_or_deleted_if_file( |
|
213 | hg_path, | |
|
213 | &hg_path, | |
|
214 | 214 | dirstate_node.state()?, |
|
215 | 215 | ); |
|
216 | 216 | } |
|
217 | 217 | if file_type.is_dir() { |
|
218 | 218 | if self.options.collect_traversed_dirs { |
|
219 | self.outcome.lock().unwrap().traversed.push(hg_path.into()) | |
|
219 | self.outcome | |
|
220 | .lock() | |
|
221 | .unwrap() | |
|
222 | .traversed | |
|
223 | .push(hg_path.detach_from_tree()) | |
|
220 | 224 | } |
|
221 | 225 | let is_ignored = has_ignored_ancestor || (self.ignore_fn)(hg_path); |
|
222 | 226 | let is_at_repo_root = false; |
@@ -229,24 +233,26 b" impl<'tree, 'a> StatusCommon<'tree, 'a, " | |||
|
229 | 233 | )? |
|
230 | 234 | } else { |
|
231 | 235 | if file_or_symlink && self.matcher.matches(hg_path) { |
|
232 | let full_path = Cow::from(hg_path); | |
|
233 | 236 | if let Some(state) = dirstate_node.state()? { |
|
234 | 237 | match state { |
|
235 |
EntryState::Added => |
|
|
236 |
|
|
|
237 |
|
|
|
238 | EntryState::Added => self | |
|
239 | .outcome | |
|
240 | .lock() | |
|
241 | .unwrap() | |
|
242 | .added | |
|
243 | .push(hg_path.detach_from_tree()), | |
|
238 | 244 | EntryState::Removed => self |
|
239 | 245 | .outcome |
|
240 | 246 | .lock() |
|
241 | 247 | .unwrap() |
|
242 | 248 | .removed |
|
243 |
.push( |
|
|
249 | .push(hg_path.detach_from_tree()), | |
|
244 | 250 | EntryState::Merged => self |
|
245 | 251 | .outcome |
|
246 | 252 | .lock() |
|
247 | 253 | .unwrap() |
|
248 | 254 | .modified |
|
249 |
.push( |
|
|
255 | .push(hg_path.detach_from_tree()), | |
|
250 | 256 | EntryState::Normal => self |
|
251 | 257 | .handle_normal_file(&dirstate_node, fs_metadata)?, |
|
252 | 258 | // This variant is not used in DirstateMap |
@@ -256,10 +262,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a, " | |||
|
256 | 262 | } else { |
|
257 | 263 | // `node.entry.is_none()` indicates a "directory" |
|
258 | 264 | // node, but the filesystem has a file |
|
259 | self.mark_unknown_or_ignored( | |
|
260 | has_ignored_ancestor, | |
|
261 | full_path, | |
|
262 | ) | |
|
265 | self.mark_unknown_or_ignored(has_ignored_ancestor, hg_path) | |
|
263 | 266 | } |
|
264 | 267 | } |
|
265 | 268 | |
@@ -275,7 +278,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a, " | |||
|
275 | 278 | /// filesystem |
|
276 | 279 | fn handle_normal_file( |
|
277 | 280 | &self, |
|
278 | dirstate_node: &NodeRef<'tree, '_>, | |
|
281 | dirstate_node: &NodeRef<'tree, 'on_disk>, | |
|
279 | 282 | fs_metadata: &std::fs::Metadata, |
|
280 | 283 | ) -> Result<(), DirstateV2ParseError> { |
|
281 | 284 | // Keep the low 31 bits |
@@ -289,7 +292,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a, " | |||
|
289 | 292 | let entry = dirstate_node |
|
290 | 293 | .entry()? |
|
291 | 294 | .expect("handle_normal_file called with entry-less node"); |
|
292 |
let |
|
|
295 | let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?; | |
|
293 | 296 | let mode_changed = |
|
294 | 297 | || self.options.check_exec && entry.mode_changed(fs_metadata); |
|
295 | 298 | let size_changed = entry.size != truncate_u64(fs_metadata.len()); |
@@ -299,20 +302,36 b" impl<'tree, 'a> StatusCommon<'tree, 'a, " | |||
|
299 | 302 | { |
|
300 | 303 | // issue6456: Size returned may be longer due to encryption |
|
301 | 304 | // on EXT-4 fscrypt. TODO maybe only do it on EXT4? |
|
302 | self.outcome.lock().unwrap().unsure.push(full_path) | |
|
305 | self.outcome | |
|
306 | .lock() | |
|
307 | .unwrap() | |
|
308 | .unsure | |
|
309 | .push(hg_path.detach_from_tree()) | |
|
303 | 310 | } else if dirstate_node.has_copy_source() |
|
304 | 311 | || entry.is_from_other_parent() |
|
305 | 312 | || (entry.size >= 0 && (size_changed || mode_changed())) |
|
306 | 313 | { |
|
307 | self.outcome.lock().unwrap().modified.push(full_path) | |
|
314 | self.outcome | |
|
315 | .lock() | |
|
316 | .unwrap() | |
|
317 | .modified | |
|
318 | .push(hg_path.detach_from_tree()) | |
|
308 | 319 | } else { |
|
309 | 320 | let mtime = mtime_seconds(fs_metadata); |
|
310 | 321 | if truncate_i64(mtime) != entry.mtime |
|
311 | 322 | || mtime == self.options.last_normal_time |
|
312 | 323 | { |
|
313 | self.outcome.lock().unwrap().unsure.push(full_path) | |
|
324 | self.outcome | |
|
325 | .lock() | |
|
326 | .unwrap() | |
|
327 | .unsure | |
|
328 | .push(hg_path.detach_from_tree()) | |
|
314 | 329 | } else if self.options.list_clean { |
|
315 | self.outcome.lock().unwrap().clean.push(full_path) | |
|
330 | self.outcome | |
|
331 | .lock() | |
|
332 | .unwrap() | |
|
333 | .clean | |
|
334 | .push(hg_path.detach_from_tree()) | |
|
316 | 335 | } |
|
317 | 336 | } |
|
318 | 337 | Ok(()) |
@@ -321,10 +340,10 b" impl<'tree, 'a> StatusCommon<'tree, 'a, " | |||
|
321 | 340 | /// A node in the dirstate tree has no corresponding filesystem entry |
|
322 | 341 | fn traverse_dirstate_only( |
|
323 | 342 | &self, |
|
324 | dirstate_node: NodeRef<'tree, '_>, | |
|
343 | dirstate_node: NodeRef<'tree, 'on_disk>, | |
|
325 | 344 | ) -> Result<(), DirstateV2ParseError> { |
|
326 | 345 | self.mark_removed_or_deleted_if_file( |
|
327 | dirstate_node.full_path(self.dmap.on_disk)?, | |
|
346 | &dirstate_node.full_path_borrowed(self.dmap.on_disk)?, | |
|
328 | 347 | dirstate_node.state()?, |
|
329 | 348 | ); |
|
330 | 349 | dirstate_node |
@@ -340,15 +359,23 b" impl<'tree, 'a> StatusCommon<'tree, 'a, " | |||
|
340 | 359 | /// Does nothing on a "directory" node |
|
341 | 360 | fn mark_removed_or_deleted_if_file( |
|
342 | 361 | &self, |
|
343 |
hg_path: &'tree |
|
|
362 | hg_path: &BorrowedPath<'tree, 'on_disk>, | |
|
344 | 363 | dirstate_node_state: Option<EntryState>, |
|
345 | 364 | ) { |
|
346 | 365 | if let Some(state) = dirstate_node_state { |
|
347 | 366 | if self.matcher.matches(hg_path) { |
|
348 | 367 | if let EntryState::Removed = state { |
|
349 | self.outcome.lock().unwrap().removed.push(hg_path.into()) | |
|
368 | self.outcome | |
|
369 | .lock() | |
|
370 | .unwrap() | |
|
371 | .removed | |
|
372 | .push(hg_path.detach_from_tree()) | |
|
350 | 373 | } else { |
|
351 | self.outcome.lock().unwrap().deleted.push(hg_path.into()) | |
|
374 | self.outcome | |
|
375 | .lock() | |
|
376 | .unwrap() | |
|
377 | .deleted | |
|
378 | .push(hg_path.detach_from_tree()) | |
|
352 | 379 | } |
|
353 | 380 | } |
|
354 | 381 | } |
@@ -395,23 +422,34 b" impl<'tree, 'a> StatusCommon<'tree, 'a, " | |||
|
395 | 422 | self.outcome.lock().unwrap().traversed.push(hg_path.into()) |
|
396 | 423 | } |
|
397 | 424 | } else if file_or_symlink && self.matcher.matches(&hg_path) { |
|
398 |
self.mark_unknown_or_ignored( |
|
|
425 | self.mark_unknown_or_ignored( | |
|
426 | has_ignored_ancestor, | |
|
427 | &BorrowedPath::InMemory(&hg_path), | |
|
428 | ) | |
|
399 | 429 | } |
|
400 | 430 | } |
|
401 | 431 | |
|
402 | 432 | fn mark_unknown_or_ignored( |
|
403 | 433 | &self, |
|
404 | 434 | has_ignored_ancestor: bool, |
|
405 |
hg_path: |
|
|
435 | hg_path: &BorrowedPath<'_, 'on_disk>, | |
|
406 | 436 | ) { |
|
407 | 437 | let is_ignored = has_ignored_ancestor || (self.ignore_fn)(&hg_path); |
|
408 | 438 | if is_ignored { |
|
409 | 439 | if self.options.list_ignored { |
|
410 | self.outcome.lock().unwrap().ignored.push(hg_path) | |
|
440 | self.outcome | |
|
441 | .lock() | |
|
442 | .unwrap() | |
|
443 | .ignored | |
|
444 | .push(hg_path.detach_from_tree()) | |
|
411 | 445 | } |
|
412 | 446 | } else { |
|
413 | 447 | if self.options.list_unknown { |
|
414 | self.outcome.lock().unwrap().unknown.push(hg_path) | |
|
448 | self.outcome | |
|
449 | .lock() | |
|
450 | .unwrap() | |
|
451 | .unknown | |
|
452 | .push(hg_path.detach_from_tree()) | |
|
415 | 453 | } |
|
416 | 454 | } |
|
417 | 455 | } |
@@ -61,7 +61,10 b" impl<'a, M: ?Sized + Matcher + Sync> Sta" | |||
|
61 | 61 | } |
|
62 | 62 | |
|
63 | 63 | drop(traversed_sender); |
|
64 |
let traversed = traversed_receiver |
|
|
64 | let traversed = traversed_receiver | |
|
65 | .into_iter() | |
|
66 | .map(std::borrow::Cow::Owned) | |
|
67 | .collect(); | |
|
65 | 68 | |
|
66 | 69 | Ok(build_response(results, traversed)) |
|
67 | 70 | } |
General Comments 0
You need to be logged in to leave comments.
Login now