##// END OF EJS Templates
dirstate-tree: Change status() results to not borrow DirstateMap...
Simon Sapin -
r48136:73ddcede default
parent child Browse files
Show More
@@ -292,7 +292,7 b" pub struct DirstateStatus<'a> {"
292 pub unsure: Vec<HgPathCow<'a>>,
292 pub unsure: Vec<HgPathCow<'a>>,
293
293
294 /// Only filled if `collect_traversed_dirs` is `true`
294 /// Only filled if `collect_traversed_dirs` is `true`
295 pub traversed: Vec<HgPathBuf>,
295 pub traversed: Vec<HgPathCow<'a>>,
296 }
296 }
297
297
298 #[derive(Debug, derive_more::From)]
298 #[derive(Debug, derive_more::From)]
@@ -880,7 +880,7 b' where'
880 #[timed]
880 #[timed]
881 pub fn build_response<'a>(
881 pub fn build_response<'a>(
882 results: impl IntoIterator<Item = DispatchedPath<'a>>,
882 results: impl IntoIterator<Item = DispatchedPath<'a>>,
883 traversed: Vec<HgPathBuf>,
883 traversed: Vec<HgPathCow<'a>>,
884 ) -> DirstateStatus<'a> {
884 ) -> DirstateStatus<'a> {
885 let mut unsure = vec![];
885 let mut unsure = vec![];
886 let mut modified = vec![];
886 let mut modified = vec![];
@@ -46,6 +46,13 b" pub struct DirstateMap<'on_disk> {"
46 /// string prefix.
46 /// string prefix.
47 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
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 pub(super) enum ChildNodes<'on_disk> {
56 pub(super) enum ChildNodes<'on_disk> {
50 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
57 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
51 OnDisk(&'on_disk [on_disk::Node]),
58 OnDisk(&'on_disk [on_disk::Node]),
@@ -61,6 +68,26 b" pub(super) enum NodeRef<'tree, 'on_disk>"
61 OnDisk(&'on_disk on_disk::Node),
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 impl Default for ChildNodes<'_> {
91 impl Default for ChildNodes<'_> {
65 fn default() -> Self {
92 fn default() -> Self {
66 ChildNodes::InMemory(Default::default())
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
240 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
214 pub(super) fn full_path_cow(
241 /// HgPath>` detached from `'tree`
242 pub(super) fn full_path_borrowed(
215 &self,
243 &self,
216 on_disk: &'on_disk [u8],
244 on_disk: &'on_disk [u8],
217 ) -> Result<Cow<'on_disk, HgPath>, DirstateV2ParseError> {
245 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
218 match self {
246 match self {
219 NodeRef::InMemory(path, _node) => Ok(path.full_path().clone()),
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 NodeRef::OnDisk(node) => {
251 NodeRef::OnDisk(node) => {
221 Ok(Cow::Borrowed(node.full_path(on_disk)?))
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 node.copy_source(self.on_disk)?,
850 node.copy_source(self.on_disk)?,
820 );
851 );
821 if entry.mtime_is_ambiguous(now) {
852 if entry.mtime_is_ambiguous(now) {
822 ambiguous_mtimes.push(node.full_path_cow(self.on_disk)?)
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 let node = node?;
889 let node = node?;
856 if let Some(entry) = node.entry()? {
890 if let Some(entry) = node.entry()? {
857 if entry.mtime_is_ambiguous(now) {
891 if entry.mtime_is_ambiguous(now) {
858 paths.push(node.full_path_cow(self.on_disk)?)
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 use crate::dirstate::status::IgnoreFnType;
1 use crate::dirstate::status::IgnoreFnType;
2 use crate::dirstate_tree::dirstate_map::BorrowedPath;
2 use crate::dirstate_tree::dirstate_map::ChildNodesRef;
3 use crate::dirstate_tree::dirstate_map::ChildNodesRef;
3 use crate::dirstate_tree::dirstate_map::DirstateMap;
4 use crate::dirstate_tree::dirstate_map::DirstateMap;
4 use crate::dirstate_tree::dirstate_map::NodeRef;
5 use crate::dirstate_tree::dirstate_map::NodeRef;
@@ -17,7 +18,6 b' use crate::StatusError;'
17 use crate::StatusOptions;
18 use crate::StatusOptions;
18 use micro_timer::timed;
19 use micro_timer::timed;
19 use rayon::prelude::*;
20 use rayon::prelude::*;
20 use std::borrow::Cow;
21 use std::io;
21 use std::io;
22 use std::path::Path;
22 use std::path::Path;
23 use std::path::PathBuf;
23 use std::path::PathBuf;
@@ -39,7 +39,7 b" pub fn status<'tree, 'on_disk: 'tree>("
39 root_dir: PathBuf,
39 root_dir: PathBuf,
40 ignore_files: Vec<PathBuf>,
40 ignore_files: Vec<PathBuf>,
41 options: StatusOptions,
41 options: StatusOptions,
42 ) -> Result<(DirstateStatus<'tree>, Vec<PatternFileWarning>), StatusError> {
42 ) -> Result<(DirstateStatus<'on_disk>, Vec<PatternFileWarning>), StatusError> {
43 let (ignore_fn, warnings): (IgnoreFnType, _) =
43 let (ignore_fn, warnings): (IgnoreFnType, _) =
44 if options.list_ignored || options.list_unknown {
44 if options.list_ignored || options.list_unknown {
45 get_ignore_function(ignore_files, &root_dir)?
45 get_ignore_function(ignore_files, &root_dir)?
@@ -55,7 +55,7 b" pub fn status<'tree, 'on_disk: 'tree>("
55 outcome: Mutex::new(DirstateStatus::default()),
55 outcome: Mutex::new(DirstateStatus::default()),
56 };
56 };
57 let is_at_repo_root = true;
57 let is_at_repo_root = true;
58 let hg_path = HgPath::new("");
58 let hg_path = &BorrowedPath::OnDisk(HgPath::new(""));
59 let has_ignored_ancestor = false;
59 let has_ignored_ancestor = false;
60 common.traverse_fs_directory_and_dirstate(
60 common.traverse_fs_directory_and_dirstate(
61 has_ignored_ancestor,
61 has_ignored_ancestor,
@@ -69,15 +69,15 b" pub fn status<'tree, 'on_disk: 'tree>("
69
69
70 /// Bag of random things needed by various parts of the algorithm. Reduces the
70 /// Bag of random things needed by various parts of the algorithm. Reduces the
71 /// number of parameters passed to functions.
71 /// number of parameters passed to functions.
72 struct StatusCommon<'tree, 'a, 'on_disk: 'tree> {
72 struct StatusCommon<'a, 'tree, 'on_disk: 'tree> {
73 dmap: &'tree DirstateMap<'on_disk>,
73 dmap: &'tree DirstateMap<'on_disk>,
74 options: StatusOptions,
74 options: StatusOptions,
75 matcher: &'a (dyn Matcher + Sync),
75 matcher: &'a (dyn Matcher + Sync),
76 ignore_fn: IgnoreFnType<'a>,
76 ignore_fn: IgnoreFnType<'a>,
77 outcome: Mutex<DirstateStatus<'tree>>,
77 outcome: Mutex<DirstateStatus<'on_disk>>,
78 }
78 }
79
79
80 impl<'tree, 'a> StatusCommon<'tree, 'a, '_> {
80 impl<'a, 'tree, 'on_disk> StatusCommon<'a, 'tree, 'on_disk> {
81 fn read_dir(
81 fn read_dir(
82 &self,
82 &self,
83 hg_path: &HgPath,
83 hg_path: &HgPath,
@@ -100,8 +100,8 b" impl<'tree, 'a> StatusCommon<'tree, 'a, "
100 fn traverse_fs_directory_and_dirstate(
100 fn traverse_fs_directory_and_dirstate(
101 &self,
101 &self,
102 has_ignored_ancestor: bool,
102 has_ignored_ancestor: bool,
103 dirstate_nodes: ChildNodesRef<'tree, '_>,
103 dirstate_nodes: ChildNodesRef<'tree, 'on_disk>,
104 directory_hg_path: &'tree HgPath,
104 directory_hg_path: &BorrowedPath<'tree, 'on_disk>,
105 directory_fs_path: &Path,
105 directory_fs_path: &Path,
106 is_at_repo_root: bool,
106 is_at_repo_root: bool,
107 ) -> Result<(), DirstateV2ParseError> {
107 ) -> Result<(), DirstateV2ParseError> {
@@ -199,10 +199,10 b" impl<'tree, 'a> StatusCommon<'tree, 'a, "
199 &self,
199 &self,
200 fs_path: &Path,
200 fs_path: &Path,
201 fs_metadata: &std::fs::Metadata,
201 fs_metadata: &std::fs::Metadata,
202 dirstate_node: NodeRef<'tree, '_>,
202 dirstate_node: NodeRef<'tree, 'on_disk>,
203 has_ignored_ancestor: bool,
203 has_ignored_ancestor: bool,
204 ) -> Result<(), DirstateV2ParseError> {
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 let file_type = fs_metadata.file_type();
206 let file_type = fs_metadata.file_type();
207 let file_or_symlink = file_type.is_file() || file_type.is_symlink();
207 let file_or_symlink = file_type.is_file() || file_type.is_symlink();
208 if !file_or_symlink {
208 if !file_or_symlink {
@@ -210,13 +210,17 b" impl<'tree, 'a> StatusCommon<'tree, 'a, "
210 // `hg rm` or similar) or deleted before it could be
210 // `hg rm` or similar) or deleted before it could be
211 // replaced by a directory or something else.
211 // replaced by a directory or something else.
212 self.mark_removed_or_deleted_if_file(
212 self.mark_removed_or_deleted_if_file(
213 hg_path,
213 &hg_path,
214 dirstate_node.state()?,
214 dirstate_node.state()?,
215 );
215 );
216 }
216 }
217 if file_type.is_dir() {
217 if file_type.is_dir() {
218 if self.options.collect_traversed_dirs {
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 let is_ignored = has_ignored_ancestor || (self.ignore_fn)(hg_path);
225 let is_ignored = has_ignored_ancestor || (self.ignore_fn)(hg_path);
222 let is_at_repo_root = false;
226 let is_at_repo_root = false;
@@ -229,24 +233,26 b" impl<'tree, 'a> StatusCommon<'tree, 'a, "
229 )?
233 )?
230 } else {
234 } else {
231 if file_or_symlink && self.matcher.matches(hg_path) {
235 if file_or_symlink && self.matcher.matches(hg_path) {
232 let full_path = Cow::from(hg_path);
233 if let Some(state) = dirstate_node.state()? {
236 if let Some(state) = dirstate_node.state()? {
234 match state {
237 match state {
235 EntryState::Added => {
238 EntryState::Added => self
236 self.outcome.lock().unwrap().added.push(full_path)
239 .outcome
237 }
240 .lock()
241 .unwrap()
242 .added
243 .push(hg_path.detach_from_tree()),
238 EntryState::Removed => self
244 EntryState::Removed => self
239 .outcome
245 .outcome
240 .lock()
246 .lock()
241 .unwrap()
247 .unwrap()
242 .removed
248 .removed
243 .push(full_path),
249 .push(hg_path.detach_from_tree()),
244 EntryState::Merged => self
250 EntryState::Merged => self
245 .outcome
251 .outcome
246 .lock()
252 .lock()
247 .unwrap()
253 .unwrap()
248 .modified
254 .modified
249 .push(full_path),
255 .push(hg_path.detach_from_tree()),
250 EntryState::Normal => self
256 EntryState::Normal => self
251 .handle_normal_file(&dirstate_node, fs_metadata)?,
257 .handle_normal_file(&dirstate_node, fs_metadata)?,
252 // This variant is not used in DirstateMap
258 // This variant is not used in DirstateMap
@@ -256,10 +262,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a, "
256 } else {
262 } else {
257 // `node.entry.is_none()` indicates a "directory"
263 // `node.entry.is_none()` indicates a "directory"
258 // node, but the filesystem has a file
264 // node, but the filesystem has a file
259 self.mark_unknown_or_ignored(
265 self.mark_unknown_or_ignored(has_ignored_ancestor, hg_path)
260 has_ignored_ancestor,
261 full_path,
262 )
263 }
266 }
264 }
267 }
265
268
@@ -275,7 +278,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a, "
275 /// filesystem
278 /// filesystem
276 fn handle_normal_file(
279 fn handle_normal_file(
277 &self,
280 &self,
278 dirstate_node: &NodeRef<'tree, '_>,
281 dirstate_node: &NodeRef<'tree, 'on_disk>,
279 fs_metadata: &std::fs::Metadata,
282 fs_metadata: &std::fs::Metadata,
280 ) -> Result<(), DirstateV2ParseError> {
283 ) -> Result<(), DirstateV2ParseError> {
281 // Keep the low 31 bits
284 // Keep the low 31 bits
@@ -289,7 +292,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a, "
289 let entry = dirstate_node
292 let entry = dirstate_node
290 .entry()?
293 .entry()?
291 .expect("handle_normal_file called with entry-less node");
294 .expect("handle_normal_file called with entry-less node");
292 let full_path = Cow::from(dirstate_node.full_path(self.dmap.on_disk)?);
295 let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?;
293 let mode_changed =
296 let mode_changed =
294 || self.options.check_exec && entry.mode_changed(fs_metadata);
297 || self.options.check_exec && entry.mode_changed(fs_metadata);
295 let size_changed = entry.size != truncate_u64(fs_metadata.len());
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 // issue6456: Size returned may be longer due to encryption
303 // issue6456: Size returned may be longer due to encryption
301 // on EXT-4 fscrypt. TODO maybe only do it on EXT4?
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 } else if dirstate_node.has_copy_source()
310 } else if dirstate_node.has_copy_source()
304 || entry.is_from_other_parent()
311 || entry.is_from_other_parent()
305 || (entry.size >= 0 && (size_changed || mode_changed()))
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 } else {
319 } else {
309 let mtime = mtime_seconds(fs_metadata);
320 let mtime = mtime_seconds(fs_metadata);
310 if truncate_i64(mtime) != entry.mtime
321 if truncate_i64(mtime) != entry.mtime
311 || mtime == self.options.last_normal_time
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 } else if self.options.list_clean {
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 Ok(())
337 Ok(())
@@ -321,10 +340,10 b" impl<'tree, 'a> StatusCommon<'tree, 'a, "
321 /// A node in the dirstate tree has no corresponding filesystem entry
340 /// A node in the dirstate tree has no corresponding filesystem entry
322 fn traverse_dirstate_only(
341 fn traverse_dirstate_only(
323 &self,
342 &self,
324 dirstate_node: NodeRef<'tree, '_>,
343 dirstate_node: NodeRef<'tree, 'on_disk>,
325 ) -> Result<(), DirstateV2ParseError> {
344 ) -> Result<(), DirstateV2ParseError> {
326 self.mark_removed_or_deleted_if_file(
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 dirstate_node.state()?,
347 dirstate_node.state()?,
329 );
348 );
330 dirstate_node
349 dirstate_node
@@ -340,15 +359,23 b" impl<'tree, 'a> StatusCommon<'tree, 'a, "
340 /// Does nothing on a "directory" node
359 /// Does nothing on a "directory" node
341 fn mark_removed_or_deleted_if_file(
360 fn mark_removed_or_deleted_if_file(
342 &self,
361 &self,
343 hg_path: &'tree HgPath,
362 hg_path: &BorrowedPath<'tree, 'on_disk>,
344 dirstate_node_state: Option<EntryState>,
363 dirstate_node_state: Option<EntryState>,
345 ) {
364 ) {
346 if let Some(state) = dirstate_node_state {
365 if let Some(state) = dirstate_node_state {
347 if self.matcher.matches(hg_path) {
366 if self.matcher.matches(hg_path) {
348 if let EntryState::Removed = state {
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 } else {
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 self.outcome.lock().unwrap().traversed.push(hg_path.into())
422 self.outcome.lock().unwrap().traversed.push(hg_path.into())
396 }
423 }
397 } else if file_or_symlink && self.matcher.matches(&hg_path) {
424 } else if file_or_symlink && self.matcher.matches(&hg_path) {
398 self.mark_unknown_or_ignored(has_ignored_ancestor, hg_path.into())
425 self.mark_unknown_or_ignored(
426 has_ignored_ancestor,
427 &BorrowedPath::InMemory(&hg_path),
428 )
399 }
429 }
400 }
430 }
401
431
402 fn mark_unknown_or_ignored(
432 fn mark_unknown_or_ignored(
403 &self,
433 &self,
404 has_ignored_ancestor: bool,
434 has_ignored_ancestor: bool,
405 hg_path: Cow<'tree, HgPath>,
435 hg_path: &BorrowedPath<'_, 'on_disk>,
406 ) {
436 ) {
407 let is_ignored = has_ignored_ancestor || (self.ignore_fn)(&hg_path);
437 let is_ignored = has_ignored_ancestor || (self.ignore_fn)(&hg_path);
408 if is_ignored {
438 if is_ignored {
409 if self.options.list_ignored {
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 } else {
446 } else {
413 if self.options.list_unknown {
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 drop(traversed_sender);
63 drop(traversed_sender);
64 let traversed = traversed_receiver.into_iter().collect();
64 let traversed = traversed_receiver
65 .into_iter()
66 .map(std::borrow::Cow::Owned)
67 .collect();
65
68
66 Ok(build_response(results, traversed))
69 Ok(build_response(results, traversed))
67 }
70 }
General Comments 0
You need to be logged in to leave comments. Login now