Show More
@@ -66,41 +66,43 b' pub struct StatusOptions {' | |||||
66 | pub list_clean: bool, |
|
66 | pub list_clean: bool, | |
67 | pub list_unknown: bool, |
|
67 | pub list_unknown: bool, | |
68 | pub list_ignored: bool, |
|
68 | pub list_ignored: bool, | |
|
69 | /// Whether to populate `StatusPath::copy_source` | |||
|
70 | pub list_copies: bool, | |||
69 | /// Whether to collect traversed dirs for applying a callback later. |
|
71 | /// Whether to collect traversed dirs for applying a callback later. | |
70 | /// Used by `hg purge` for example. |
|
72 | /// Used by `hg purge` for example. | |
71 | pub collect_traversed_dirs: bool, |
|
73 | pub collect_traversed_dirs: bool, | |
72 | } |
|
74 | } | |
73 |
|
75 | |||
74 |
#[derive(De |
|
76 | #[derive(Default)] | |
75 | pub struct DirstateStatus<'a> { |
|
77 | pub struct DirstateStatus<'a> { | |
76 | /// The current time at the start of the `status()` algorithm, as measured |
|
78 | /// The current time at the start of the `status()` algorithm, as measured | |
77 | /// and possibly truncated by the filesystem. |
|
79 | /// and possibly truncated by the filesystem. | |
78 | pub filesystem_time_at_status_start: Option<std::time::SystemTime>, |
|
80 | pub filesystem_time_at_status_start: Option<std::time::SystemTime>, | |
79 |
|
81 | |||
80 | /// Tracked files whose contents have changed since the parent revision |
|
82 | /// Tracked files whose contents have changed since the parent revision | |
81 |
pub modified: Vec< |
|
83 | pub modified: Vec<StatusPath<'a>>, | |
82 |
|
84 | |||
83 | /// Newly-tracked files that were not present in the parent |
|
85 | /// Newly-tracked files that were not present in the parent | |
84 |
pub added: Vec< |
|
86 | pub added: Vec<StatusPath<'a>>, | |
85 |
|
87 | |||
86 | /// Previously-tracked files that have been (re)moved with an hg command |
|
88 | /// Previously-tracked files that have been (re)moved with an hg command | |
87 |
pub removed: Vec< |
|
89 | pub removed: Vec<StatusPath<'a>>, | |
88 |
|
90 | |||
89 | /// (Still) tracked files that are missing, (re)moved with an non-hg |
|
91 | /// (Still) tracked files that are missing, (re)moved with an non-hg | |
90 | /// command |
|
92 | /// command | |
91 |
pub deleted: Vec< |
|
93 | pub deleted: Vec<StatusPath<'a>>, | |
92 |
|
94 | |||
93 | /// Tracked files that are up to date with the parent. |
|
95 | /// Tracked files that are up to date with the parent. | |
94 | /// Only pupulated if `StatusOptions::list_clean` is true. |
|
96 | /// Only pupulated if `StatusOptions::list_clean` is true. | |
95 |
pub clean: Vec< |
|
97 | pub clean: Vec<StatusPath<'a>>, | |
96 |
|
98 | |||
97 | /// Files in the working directory that are ignored with `.hgignore`. |
|
99 | /// Files in the working directory that are ignored with `.hgignore`. | |
98 | /// Only pupulated if `StatusOptions::list_ignored` is true. |
|
100 | /// Only pupulated if `StatusOptions::list_ignored` is true. | |
99 |
pub ignored: Vec< |
|
101 | pub ignored: Vec<StatusPath<'a>>, | |
100 |
|
102 | |||
101 | /// Files in the working directory that are neither tracked nor ignored. |
|
103 | /// Files in the working directory that are neither tracked nor ignored. | |
102 | /// Only pupulated if `StatusOptions::list_unknown` is true. |
|
104 | /// Only pupulated if `StatusOptions::list_unknown` is true. | |
103 |
pub unknown: Vec< |
|
105 | pub unknown: Vec<StatusPath<'a>>, | |
104 |
|
106 | |||
105 | /// Was explicitly matched but cannot be found/accessed |
|
107 | /// Was explicitly matched but cannot be found/accessed | |
106 | pub bad: Vec<(HgPathCow<'a>, BadMatch)>, |
|
108 | pub bad: Vec<(HgPathCow<'a>, BadMatch)>, | |
@@ -108,7 +110,7 b" pub struct DirstateStatus<'a> {" | |||||
108 | /// Either clean or modified, but we can’t tell from filesystem metadata |
|
110 | /// Either clean or modified, but we can’t tell from filesystem metadata | |
109 | /// alone. The file contents need to be read and compared with that in |
|
111 | /// alone. The file contents need to be read and compared with that in | |
110 | /// the parent. |
|
112 | /// the parent. | |
111 |
pub unsure: Vec< |
|
113 | pub unsure: Vec<StatusPath<'a>>, | |
112 |
|
114 | |||
113 | /// Only filled if `collect_traversed_dirs` is `true` |
|
115 | /// Only filled if `collect_traversed_dirs` is `true` | |
114 | pub traversed: Vec<HgPathCow<'a>>, |
|
116 | pub traversed: Vec<HgPathCow<'a>>, | |
@@ -118,6 +120,12 b" pub struct DirstateStatus<'a> {" | |||||
118 | pub dirty: bool, |
|
120 | pub dirty: bool, | |
119 | } |
|
121 | } | |
120 |
|
122 | |||
|
123 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] | |||
|
124 | pub struct StatusPath<'a> { | |||
|
125 | pub path: HgPathCow<'a>, | |||
|
126 | pub copy_source: Option<HgPathCow<'a>>, | |||
|
127 | } | |||
|
128 | ||||
121 | #[derive(Debug, derive_more::From)] |
|
129 | #[derive(Debug, derive_more::From)] | |
122 | pub enum StatusError { |
|
130 | pub enum StatusError { | |
123 | /// Generic IO error |
|
131 | /// Generic IO error |
@@ -309,6 +309,25 b" impl<'tree, 'on_disk> NodeRef<'tree, 'on" | |||||
309 | NodeRef::OnDisk(node) => node.copy_source(on_disk), |
|
309 | NodeRef::OnDisk(node) => node.copy_source(on_disk), | |
310 | } |
|
310 | } | |
311 | } |
|
311 | } | |
|
312 | /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk, | |||
|
313 | /// HgPath>` detached from `'tree` | |||
|
314 | pub(super) fn copy_source_borrowed( | |||
|
315 | &self, | |||
|
316 | on_disk: &'on_disk [u8], | |||
|
317 | ) -> Result<Option<BorrowedPath<'tree, 'on_disk>>, DirstateV2ParseError> | |||
|
318 | { | |||
|
319 | Ok(match self { | |||
|
320 | NodeRef::InMemory(_path, node) => { | |||
|
321 | node.copy_source.as_ref().map(|source| match source { | |||
|
322 | Cow::Borrowed(on_disk) => BorrowedPath::OnDisk(on_disk), | |||
|
323 | Cow::Owned(in_memory) => BorrowedPath::InMemory(in_memory), | |||
|
324 | }) | |||
|
325 | } | |||
|
326 | NodeRef::OnDisk(node) => node | |||
|
327 | .copy_source(on_disk)? | |||
|
328 | .map(|source| BorrowedPath::OnDisk(source)), | |||
|
329 | }) | |||
|
330 | } | |||
312 |
|
331 | |||
313 | pub(super) fn entry( |
|
332 | pub(super) fn entry( | |
314 | &self, |
|
333 | &self, |
@@ -1,5 +1,6 b'' | |||||
1 | use crate::dirstate::entry::TruncatedTimestamp; |
|
1 | use crate::dirstate::entry::TruncatedTimestamp; | |
2 | use crate::dirstate::status::IgnoreFnType; |
|
2 | use crate::dirstate::status::IgnoreFnType; | |
|
3 | use crate::dirstate::status::StatusPath; | |||
3 | use crate::dirstate_tree::dirstate_map::BorrowedPath; |
|
4 | use crate::dirstate_tree::dirstate_map::BorrowedPath; | |
4 | use crate::dirstate_tree::dirstate_map::ChildNodesRef; |
|
5 | use crate::dirstate_tree::dirstate_map::ChildNodesRef; | |
5 | use crate::dirstate_tree::dirstate_map::DirstateMap; |
|
6 | use crate::dirstate_tree::dirstate_map::DirstateMap; | |
@@ -15,6 +16,7 b' use crate::BadMatch;' | |||||
15 | use crate::DirstateStatus; |
|
16 | use crate::DirstateStatus; | |
16 | use crate::EntryState; |
|
17 | use crate::EntryState; | |
17 | use crate::HgPathBuf; |
|
18 | use crate::HgPathBuf; | |
|
19 | use crate::HgPathCow; | |||
18 | use crate::PatternFileWarning; |
|
20 | use crate::PatternFileWarning; | |
19 | use crate::StatusError; |
|
21 | use crate::StatusError; | |
20 | use crate::StatusOptions; |
|
22 | use crate::StatusOptions; | |
@@ -146,7 +148,65 b" struct StatusCommon<'a, 'tree, 'on_disk:" | |||||
146 | filesystem_time_at_status_start: Option<SystemTime>, |
|
148 | filesystem_time_at_status_start: Option<SystemTime>, | |
147 | } |
|
149 | } | |
148 |
|
150 | |||
|
151 | enum Outcome { | |||
|
152 | Modified, | |||
|
153 | Added, | |||
|
154 | Removed, | |||
|
155 | Deleted, | |||
|
156 | Clean, | |||
|
157 | Ignored, | |||
|
158 | Unknown, | |||
|
159 | Unsure, | |||
|
160 | } | |||
|
161 | ||||
149 | impl<'a, 'tree, 'on_disk> StatusCommon<'a, 'tree, 'on_disk> { |
|
162 | impl<'a, 'tree, 'on_disk> StatusCommon<'a, 'tree, 'on_disk> { | |
|
163 | fn push_outcome( | |||
|
164 | &self, | |||
|
165 | which: Outcome, | |||
|
166 | dirstate_node: &NodeRef<'tree, 'on_disk>, | |||
|
167 | ) -> Result<(), DirstateV2ParseError> { | |||
|
168 | let path = dirstate_node | |||
|
169 | .full_path_borrowed(self.dmap.on_disk)? | |||
|
170 | .detach_from_tree(); | |||
|
171 | let copy_source = if self.options.list_copies { | |||
|
172 | dirstate_node | |||
|
173 | .copy_source_borrowed(self.dmap.on_disk)? | |||
|
174 | .map(|source| source.detach_from_tree()) | |||
|
175 | } else { | |||
|
176 | None | |||
|
177 | }; | |||
|
178 | self.push_outcome_common(which, path, copy_source); | |||
|
179 | Ok(()) | |||
|
180 | } | |||
|
181 | ||||
|
182 | fn push_outcome_without_copy_source( | |||
|
183 | &self, | |||
|
184 | which: Outcome, | |||
|
185 | path: &BorrowedPath<'_, 'on_disk>, | |||
|
186 | ) { | |||
|
187 | self.push_outcome_common(which, path.detach_from_tree(), None) | |||
|
188 | } | |||
|
189 | ||||
|
190 | fn push_outcome_common( | |||
|
191 | &self, | |||
|
192 | which: Outcome, | |||
|
193 | path: HgPathCow<'on_disk>, | |||
|
194 | copy_source: Option<HgPathCow<'on_disk>>, | |||
|
195 | ) { | |||
|
196 | let mut outcome = self.outcome.lock().unwrap(); | |||
|
197 | let vec = match which { | |||
|
198 | Outcome::Modified => &mut outcome.modified, | |||
|
199 | Outcome::Added => &mut outcome.added, | |||
|
200 | Outcome::Removed => &mut outcome.removed, | |||
|
201 | Outcome::Deleted => &mut outcome.deleted, | |||
|
202 | Outcome::Clean => &mut outcome.clean, | |||
|
203 | Outcome::Ignored => &mut outcome.ignored, | |||
|
204 | Outcome::Unknown => &mut outcome.unknown, | |||
|
205 | Outcome::Unsure => &mut outcome.unsure, | |||
|
206 | }; | |||
|
207 | vec.push(StatusPath { path, copy_source }); | |||
|
208 | } | |||
|
209 | ||||
150 | fn read_dir( |
|
210 | fn read_dir( | |
151 | &self, |
|
211 | &self, | |
152 | hg_path: &HgPath, |
|
212 | hg_path: &HgPath, | |
@@ -347,10 +407,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
347 | // If we previously had a file here, it was removed (with |
|
407 | // If we previously had a file here, it was removed (with | |
348 | // `hg rm` or similar) or deleted before it could be |
|
408 | // `hg rm` or similar) or deleted before it could be | |
349 | // replaced by a directory or something else. |
|
409 | // replaced by a directory or something else. | |
350 | self.mark_removed_or_deleted_if_file( |
|
410 | self.mark_removed_or_deleted_if_file(&dirstate_node)?; | |
351 | &hg_path, |
|
|||
352 | dirstate_node.state()?, |
|
|||
353 | ); |
|
|||
354 | } |
|
411 | } | |
355 | if file_type.is_dir() { |
|
412 | if file_type.is_dir() { | |
356 | if self.options.collect_traversed_dirs { |
|
413 | if self.options.collect_traversed_dirs { | |
@@ -381,24 +438,13 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
381 | if file_or_symlink && self.matcher.matches(hg_path) { |
|
438 | if file_or_symlink && self.matcher.matches(hg_path) { | |
382 | if let Some(state) = dirstate_node.state()? { |
|
439 | if let Some(state) = dirstate_node.state()? { | |
383 | match state { |
|
440 | match state { | |
384 |
EntryState::Added => |
|
441 | EntryState::Added => { | |
385 | .outcome |
|
442 | self.push_outcome(Outcome::Added, &dirstate_node)? | |
386 |
|
|
443 | } | |
387 | .unwrap() |
|
|||
388 | .added |
|
|||
389 | .push(hg_path.detach_from_tree()), |
|
|||
390 | EntryState::Removed => self |
|
444 | EntryState::Removed => self | |
391 | .outcome |
|
445 | .push_outcome(Outcome::Removed, &dirstate_node)?, | |
392 | .lock() |
|
|||
393 | .unwrap() |
|
|||
394 | .removed |
|
|||
395 | .push(hg_path.detach_from_tree()), |
|
|||
396 | EntryState::Merged => self |
|
446 | EntryState::Merged => self | |
397 | .outcome |
|
447 | .push_outcome(Outcome::Modified, &dirstate_node)?, | |
398 | .lock() |
|
|||
399 | .unwrap() |
|
|||
400 | .modified |
|
|||
401 | .push(hg_path.detach_from_tree()), |
|
|||
402 | EntryState::Normal => self |
|
448 | EntryState::Normal => self | |
403 | .handle_normal_file(&dirstate_node, fs_metadata)?, |
|
449 | .handle_normal_file(&dirstate_node, fs_metadata)?, | |
404 | } |
|
450 | } | |
@@ -510,7 +556,6 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
510 | let entry = dirstate_node |
|
556 | let entry = dirstate_node | |
511 | .entry()? |
|
557 | .entry()? | |
512 | .expect("handle_normal_file called with entry-less node"); |
|
558 | .expect("handle_normal_file called with entry-less node"); | |
513 | let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?; |
|
|||
514 | let mode_changed = |
|
559 | let mode_changed = | |
515 | || self.options.check_exec && entry.mode_changed(fs_metadata); |
|
560 | || self.options.check_exec && entry.mode_changed(fs_metadata); | |
516 | let size = entry.size(); |
|
561 | let size = entry.size(); | |
@@ -518,20 +563,12 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
518 | if size >= 0 && size_changed && fs_metadata.file_type().is_symlink() { |
|
563 | if size >= 0 && size_changed && fs_metadata.file_type().is_symlink() { | |
519 | // issue6456: Size returned may be longer due to encryption |
|
564 | // issue6456: Size returned may be longer due to encryption | |
520 | // on EXT-4 fscrypt. TODO maybe only do it on EXT4? |
|
565 | // on EXT-4 fscrypt. TODO maybe only do it on EXT4? | |
521 | self.outcome |
|
566 | self.push_outcome(Outcome::Unsure, dirstate_node)? | |
522 | .lock() |
|
|||
523 | .unwrap() |
|
|||
524 | .unsure |
|
|||
525 | .push(hg_path.detach_from_tree()) |
|
|||
526 | } else if dirstate_node.has_copy_source() |
|
567 | } else if dirstate_node.has_copy_source() | |
527 | || entry.is_from_other_parent() |
|
568 | || entry.is_from_other_parent() | |
528 | || (size >= 0 && (size_changed || mode_changed())) |
|
569 | || (size >= 0 && (size_changed || mode_changed())) | |
529 | { |
|
570 | { | |
530 | self.outcome |
|
571 | self.push_outcome(Outcome::Modified, dirstate_node)? | |
531 | .lock() |
|
|||
532 | .unwrap() |
|
|||
533 | .modified |
|
|||
534 | .push(hg_path.detach_from_tree()) |
|
|||
535 | } else { |
|
572 | } else { | |
536 | let mtime_looks_clean; |
|
573 | let mtime_looks_clean; | |
537 | if let Some(dirstate_mtime) = entry.truncated_mtime() { |
|
574 | if let Some(dirstate_mtime) = entry.truncated_mtime() { | |
@@ -548,17 +585,9 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
548 | mtime_looks_clean = false |
|
585 | mtime_looks_clean = false | |
549 | }; |
|
586 | }; | |
550 | if !mtime_looks_clean { |
|
587 | if !mtime_looks_clean { | |
551 | self.outcome |
|
588 | self.push_outcome(Outcome::Unsure, dirstate_node)? | |
552 | .lock() |
|
|||
553 | .unwrap() |
|
|||
554 | .unsure |
|
|||
555 | .push(hg_path.detach_from_tree()) |
|
|||
556 | } else if self.options.list_clean { |
|
589 | } else if self.options.list_clean { | |
557 | self.outcome |
|
590 | self.push_outcome(Outcome::Clean, dirstate_node)? | |
558 | .lock() |
|
|||
559 | .unwrap() |
|
|||
560 | .clean |
|
|||
561 | .push(hg_path.detach_from_tree()) |
|
|||
562 | } |
|
591 | } | |
563 | } |
|
592 | } | |
564 | Ok(()) |
|
593 | Ok(()) | |
@@ -570,10 +599,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
570 | dirstate_node: NodeRef<'tree, 'on_disk>, |
|
599 | dirstate_node: NodeRef<'tree, 'on_disk>, | |
571 | ) -> Result<(), DirstateV2ParseError> { |
|
600 | ) -> Result<(), DirstateV2ParseError> { | |
572 | self.check_for_outdated_directory_cache(&dirstate_node)?; |
|
601 | self.check_for_outdated_directory_cache(&dirstate_node)?; | |
573 | self.mark_removed_or_deleted_if_file( |
|
602 | self.mark_removed_or_deleted_if_file(&dirstate_node)?; | |
574 | &dirstate_node.full_path_borrowed(self.dmap.on_disk)?, |
|
|||
575 | dirstate_node.state()?, |
|
|||
576 | ); |
|
|||
577 | dirstate_node |
|
603 | dirstate_node | |
578 | .children(self.dmap.on_disk)? |
|
604 | .children(self.dmap.on_disk)? | |
579 | .par_iter() |
|
605 | .par_iter() | |
@@ -587,26 +613,19 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
587 | /// Does nothing on a "directory" node |
|
613 | /// Does nothing on a "directory" node | |
588 | fn mark_removed_or_deleted_if_file( |
|
614 | fn mark_removed_or_deleted_if_file( | |
589 | &self, |
|
615 | &self, | |
590 |
|
|
616 | dirstate_node: &NodeRef<'tree, 'on_disk>, | |
591 | dirstate_node_state: Option<EntryState>, |
|
617 | ) -> Result<(), DirstateV2ParseError> { | |
592 | ) { |
|
618 | if let Some(state) = dirstate_node.state()? { | |
593 | if let Some(state) = dirstate_node_state { |
|
619 | let path = dirstate_node.full_path(self.dmap.on_disk)?; | |
594 |
if self.matcher.matches( |
|
620 | if self.matcher.matches(path) { | |
595 | if let EntryState::Removed = state { |
|
621 | if let EntryState::Removed = state { | |
596 | self.outcome |
|
622 | self.push_outcome(Outcome::Removed, dirstate_node)? | |
597 | .lock() |
|
|||
598 | .unwrap() |
|
|||
599 | .removed |
|
|||
600 | .push(hg_path.detach_from_tree()) |
|
|||
601 | } else { |
|
623 | } else { | |
602 | self.outcome |
|
624 | self.push_outcome(Outcome::Deleted, &dirstate_node)? | |
603 | .lock() |
|
|||
604 | .unwrap() |
|
|||
605 | .deleted |
|
|||
606 | .push(hg_path.detach_from_tree()) |
|
|||
607 | } |
|
625 | } | |
608 | } |
|
626 | } | |
609 | } |
|
627 | } | |
|
628 | Ok(()) | |||
610 | } |
|
629 | } | |
611 |
|
630 | |||
612 | /// Something in the filesystem has no corresponding dirstate node |
|
631 | /// Something in the filesystem has no corresponding dirstate node | |
@@ -684,19 +703,17 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
684 | let is_ignored = has_ignored_ancestor || (self.ignore_fn)(&hg_path); |
|
703 | let is_ignored = has_ignored_ancestor || (self.ignore_fn)(&hg_path); | |
685 | if is_ignored { |
|
704 | if is_ignored { | |
686 | if self.options.list_ignored { |
|
705 | if self.options.list_ignored { | |
687 | self.outcome |
|
706 | self.push_outcome_without_copy_source( | |
688 |
|
|
707 | Outcome::Ignored, | |
689 |
|
|
708 | hg_path, | |
690 |
|
|
709 | ) | |
691 | .push(hg_path.detach_from_tree()) |
|
|||
692 | } |
|
710 | } | |
693 | } else { |
|
711 | } else { | |
694 | if self.options.list_unknown { |
|
712 | if self.options.list_unknown { | |
695 | self.outcome |
|
713 | self.push_outcome_without_copy_source( | |
696 |
|
|
714 | Outcome::Unknown, | |
697 |
|
|
715 | hg_path, | |
698 |
|
|
716 | ) | |
699 | .push(hg_path.detach_from_tree()) |
|
|||
700 | } |
|
717 | } | |
701 | } |
|
718 | } | |
702 | is_ignored |
|
719 | is_ignored |
@@ -15,6 +15,7 b' use cpython::{' | |||||
15 | exc::ValueError, ObjectProtocol, PyBytes, PyErr, PyList, PyObject, |
|
15 | exc::ValueError, ObjectProtocol, PyBytes, PyErr, PyList, PyObject, | |
16 | PyResult, PyTuple, Python, PythonObject, ToPyObject, |
|
16 | PyResult, PyTuple, Python, PythonObject, ToPyObject, | |
17 | }; |
|
17 | }; | |
|
18 | use hg::dirstate::status::StatusPath; | |||
18 | use hg::{ |
|
19 | use hg::{ | |
19 | matchers::{AlwaysMatcher, FileMatcher, IncludeMatcher}, |
|
20 | matchers::{AlwaysMatcher, FileMatcher, IncludeMatcher}, | |
20 | parse_pattern_syntax, |
|
21 | parse_pattern_syntax, | |
@@ -27,15 +28,19 b' use hg::{' | |||||
27 | }; |
|
28 | }; | |
28 | use std::borrow::Borrow; |
|
29 | use std::borrow::Borrow; | |
29 |
|
30 | |||
|
31 | fn collect_status_path_list(py: Python, paths: &[StatusPath<'_>]) -> PyList { | |||
|
32 | collect_pybytes_list(py, paths.iter().map(|item| &*item.path)) | |||
|
33 | } | |||
|
34 | ||||
30 | /// This will be useless once trait impls for collection are added to `PyBytes` |
|
35 | /// This will be useless once trait impls for collection are added to `PyBytes` | |
31 | /// upstream. |
|
36 | /// upstream. | |
32 | fn collect_pybytes_list( |
|
37 | fn collect_pybytes_list( | |
33 | py: Python, |
|
38 | py: Python, | |
34 | collection: &[impl AsRef<HgPath>], |
|
39 | iter: impl Iterator<Item = impl AsRef<HgPath>>, | |
35 | ) -> PyList { |
|
40 | ) -> PyList { | |
36 | let list = PyList::new(py, &[]); |
|
41 | let list = PyList::new(py, &[]); | |
37 |
|
42 | |||
38 |
for path in |
|
43 | for path in iter { | |
39 | list.append( |
|
44 | list.append( | |
40 | py, |
|
45 | py, | |
41 | PyBytes::new(py, path.as_ref().as_bytes()).into_object(), |
|
46 | PyBytes::new(py, path.as_ref().as_bytes()).into_object(), | |
@@ -121,6 +126,8 b' pub fn status_wrapper(' | |||||
121 | }) |
|
126 | }) | |
122 | .collect(); |
|
127 | .collect(); | |
123 | let ignore_files = ignore_files?; |
|
128 | let ignore_files = ignore_files?; | |
|
129 | // The caller may call `copymap.items()` separately | |||
|
130 | let list_copies = false; | |||
124 |
|
131 | |||
125 | match matcher.get_type(py).name(py).borrow() { |
|
132 | match matcher.get_type(py).name(py).borrow() { | |
126 | "alwaysmatcher" => { |
|
133 | "alwaysmatcher" => { | |
@@ -135,6 +142,7 b' pub fn status_wrapper(' | |||||
135 | list_clean, |
|
142 | list_clean, | |
136 | list_ignored, |
|
143 | list_ignored, | |
137 | list_unknown, |
|
144 | list_unknown, | |
|
145 | list_copies, | |||
138 | collect_traversed_dirs, |
|
146 | collect_traversed_dirs, | |
139 | }, |
|
147 | }, | |
140 | ) |
|
148 | ) | |
@@ -171,6 +179,7 b' pub fn status_wrapper(' | |||||
171 | list_clean, |
|
179 | list_clean, | |
172 | list_ignored, |
|
180 | list_ignored, | |
173 | list_unknown, |
|
181 | list_unknown, | |
|
182 | list_copies, | |||
174 | collect_traversed_dirs, |
|
183 | collect_traversed_dirs, | |
175 | }, |
|
184 | }, | |
176 | ) |
|
185 | ) | |
@@ -222,6 +231,7 b' pub fn status_wrapper(' | |||||
222 | list_clean, |
|
231 | list_clean, | |
223 | list_ignored, |
|
232 | list_ignored, | |
224 | list_unknown, |
|
233 | list_unknown, | |
|
234 | list_copies, | |||
225 | collect_traversed_dirs, |
|
235 | collect_traversed_dirs, | |
226 | }, |
|
236 | }, | |
227 | ) |
|
237 | ) | |
@@ -241,16 +251,16 b' fn build_response(' | |||||
241 | status_res: DirstateStatus, |
|
251 | status_res: DirstateStatus, | |
242 | warnings: Vec<PatternFileWarning>, |
|
252 | warnings: Vec<PatternFileWarning>, | |
243 | ) -> PyResult<PyTuple> { |
|
253 | ) -> PyResult<PyTuple> { | |
244 |
let modified = collect_ |
|
254 | let modified = collect_status_path_list(py, &status_res.modified); | |
245 |
let added = collect_ |
|
255 | let added = collect_status_path_list(py, &status_res.added); | |
246 |
let removed = collect_ |
|
256 | let removed = collect_status_path_list(py, &status_res.removed); | |
247 |
let deleted = collect_ |
|
257 | let deleted = collect_status_path_list(py, &status_res.deleted); | |
248 |
let clean = collect_ |
|
258 | let clean = collect_status_path_list(py, &status_res.clean); | |
249 |
let ignored = collect_ |
|
259 | let ignored = collect_status_path_list(py, &status_res.ignored); | |
250 |
let unknown = collect_ |
|
260 | let unknown = collect_status_path_list(py, &status_res.unknown); | |
251 |
let unsure = collect_ |
|
261 | let unsure = collect_status_path_list(py, &status_res.unsure); | |
252 |
let bad = collect_bad_matches(py, status_res.bad |
|
262 | let bad = collect_bad_matches(py, &status_res.bad)?; | |
253 |
let traversed = collect_pybytes_list(py, status_res.traversed. |
|
263 | let traversed = collect_pybytes_list(py, status_res.traversed.iter()); | |
254 | let dirty = status_res.dirty.to_py_object(py); |
|
264 | let dirty = status_res.dirty.to_py_object(py); | |
255 | let py_warnings = PyList::new(py, &[]); |
|
265 | let py_warnings = PyList::new(py, &[]); | |
256 | for warning in warnings.iter() { |
|
266 | for warning in warnings.iter() { |
@@ -13,6 +13,7 b' use format_bytes::format_bytes;' | |||||
13 | use hg; |
|
13 | use hg; | |
14 | use hg::config::Config; |
|
14 | use hg::config::Config; | |
15 | use hg::dirstate::has_exec_bit; |
|
15 | use hg::dirstate::has_exec_bit; | |
|
16 | use hg::dirstate::status::StatusPath; | |||
16 | use hg::dirstate::TruncatedTimestamp; |
|
17 | use hg::dirstate::TruncatedTimestamp; | |
17 | use hg::dirstate::RANGE_MASK_31BIT; |
|
18 | use hg::dirstate::RANGE_MASK_31BIT; | |
18 | use hg::errors::{HgError, IoResultExt}; |
|
19 | use hg::errors::{HgError, IoResultExt}; | |
@@ -23,7 +24,7 b' use hg::repo::Repo;' | |||||
23 | use hg::utils::files::get_bytes_from_os_string; |
|
24 | use hg::utils::files::get_bytes_from_os_string; | |
24 | use hg::utils::files::get_path_from_bytes; |
|
25 | use hg::utils::files::get_path_from_bytes; | |
25 | use hg::utils::hg_path::{hg_path_to_path_buf, HgPath}; |
|
26 | use hg::utils::hg_path::{hg_path_to_path_buf, HgPath}; | |
26 |
use hg:: |
|
27 | use hg::StatusOptions; | |
27 | use log::{info, warn}; |
|
28 | use log::{info, warn}; | |
28 | use std::io; |
|
29 | use std::io; | |
29 | use std::path::PathBuf; |
|
30 | use std::path::PathBuf; | |
@@ -89,6 +90,12 b" pub fn args() -> clap::App<'static, 'sta" | |||||
89 | .long("--ignored"), |
|
90 | .long("--ignored"), | |
90 | ) |
|
91 | ) | |
91 | .arg( |
|
92 | .arg( | |
|
93 | Arg::with_name("copies") | |||
|
94 | .help("show source of copied files (DEFAULT: ui.statuscopies)") | |||
|
95 | .short("-C") | |||
|
96 | .long("--copies"), | |||
|
97 | ) | |||
|
98 | .arg( | |||
92 | Arg::with_name("no-status") |
|
99 | Arg::with_name("no-status") | |
93 | .help("hide status prefix") |
|
100 | .help("hide status prefix") | |
94 | .short("-n") |
|
101 | .short("-n") | |
@@ -174,7 +181,8 b' pub fn run(invocation: &crate::CliInvoca' | |||||
174 | let ui = invocation.ui; |
|
181 | let ui = invocation.ui; | |
175 | let config = invocation.config; |
|
182 | let config = invocation.config; | |
176 | let args = invocation.subcommand_args; |
|
183 | let args = invocation.subcommand_args; | |
177 |
let |
|
184 | let all = args.is_present("all"); | |
|
185 | let display_states = if all { | |||
178 | // TODO when implementing `--quiet`: it excludes clean files |
|
186 | // TODO when implementing `--quiet`: it excludes clean files | |
179 | // from `--all` |
|
187 | // from `--all` | |
180 | ALL_DISPLAY_STATES |
|
188 | ALL_DISPLAY_STATES | |
@@ -195,6 +203,9 b' pub fn run(invocation: &crate::CliInvoca' | |||||
195 | } |
|
203 | } | |
196 | }; |
|
204 | }; | |
197 | let no_status = args.is_present("no-status"); |
|
205 | let no_status = args.is_present("no-status"); | |
|
206 | let list_copies = all | |||
|
207 | || args.is_present("copies") | |||
|
208 | || config.get_bool(b"ui", b"statuscopies")?; | |||
198 |
|
209 | |||
199 | let repo = invocation.repo?; |
|
210 | let repo = invocation.repo?; | |
200 |
|
211 | |||
@@ -213,6 +224,7 b' pub fn run(invocation: &crate::CliInvoca' | |||||
213 | list_clean: display_states.clean, |
|
224 | list_clean: display_states.clean, | |
214 | list_unknown: display_states.unknown, |
|
225 | list_unknown: display_states.unknown, | |
215 | list_ignored: display_states.ignored, |
|
226 | list_ignored: display_states.ignored, | |
|
227 | list_copies, | |||
216 | collect_traversed_dirs: false, |
|
228 | collect_traversed_dirs: false, | |
217 | }; |
|
229 | }; | |
218 | let (mut ds_status, pattern_warnings) = dmap.status( |
|
230 | let (mut ds_status, pattern_warnings) = dmap.status( | |
@@ -231,7 +243,7 b' pub fn run(invocation: &crate::CliInvoca' | |||||
231 | if !ds_status.unsure.is_empty() { |
|
243 | if !ds_status.unsure.is_empty() { | |
232 | info!( |
|
244 | info!( | |
233 | "Files to be rechecked by retrieval from filelog: {:?}", |
|
245 | "Files to be rechecked by retrieval from filelog: {:?}", | |
234 | &ds_status.unsure |
|
246 | ds_status.unsure.iter().map(|s| &s.path).collect::<Vec<_>>() | |
235 | ); |
|
247 | ); | |
236 | } |
|
248 | } | |
237 | let mut fixup = Vec::new(); |
|
249 | let mut fixup = Vec::new(); | |
@@ -243,7 +255,7 b' pub fn run(invocation: &crate::CliInvoca' | |||||
243 | CommandError::from((e, &*format!("{:x}", p1.short()))) |
|
255 | CommandError::from((e, &*format!("{:x}", p1.short()))) | |
244 | })?; |
|
256 | })?; | |
245 | for to_check in ds_status.unsure { |
|
257 | for to_check in ds_status.unsure { | |
246 | if unsure_is_modified(repo, &manifest, &to_check)? { |
|
258 | if unsure_is_modified(repo, &manifest, &to_check.path)? { | |
247 | if display_states.modified { |
|
259 | if display_states.modified { | |
248 | ds_status.modified.push(to_check); |
|
260 | ds_status.modified.push(to_check); | |
249 | } |
|
261 | } | |
@@ -251,7 +263,7 b' pub fn run(invocation: &crate::CliInvoca' | |||||
251 | if display_states.clean { |
|
263 | if display_states.clean { | |
252 | ds_status.clean.push(to_check.clone()); |
|
264 | ds_status.clean.push(to_check.clone()); | |
253 | } |
|
265 | } | |
254 | fixup.push(to_check.into_owned()) |
|
266 | fixup.push(to_check.path.into_owned()) | |
255 | } |
|
267 | } | |
256 | } |
|
268 | } | |
257 | } |
|
269 | } | |
@@ -392,10 +404,10 b" impl DisplayStatusPaths<'_> {" | |||||
392 | fn display( |
|
404 | fn display( | |
393 | &self, |
|
405 | &self, | |
394 | status_prefix: &[u8], |
|
406 | status_prefix: &[u8], | |
395 |
mut paths: Vec< |
|
407 | mut paths: Vec<StatusPath<'_>>, | |
396 | ) -> Result<(), CommandError> { |
|
408 | ) -> Result<(), CommandError> { | |
397 | paths.sort_unstable(); |
|
409 | paths.sort_unstable(); | |
398 | for path in paths { |
|
410 | for StatusPath { path, copy_source } in paths { | |
399 | let relative; |
|
411 | let relative; | |
400 | let path = if let Some(relativize) = &self.relativize { |
|
412 | let path = if let Some(relativize) = &self.relativize { | |
401 | relative = relativize.relativize(&path); |
|
413 | relative = relativize.relativize(&path); | |
@@ -414,6 +426,12 b" impl DisplayStatusPaths<'_> {" | |||||
414 | path |
|
426 | path | |
415 | ))? |
|
427 | ))? | |
416 | } |
|
428 | } | |
|
429 | if let Some(source) = copy_source { | |||
|
430 | self.ui.write_stdout(&format_bytes!( | |||
|
431 | b" {}\n", | |||
|
432 | source.as_bytes() | |||
|
433 | ))? | |||
|
434 | } | |||
417 | } |
|
435 | } | |
418 | Ok(()) |
|
436 | Ok(()) | |
419 | } |
|
437 | } |
@@ -1,7 +1,3 b'' | |||||
1 | TODO: fix rhg bugs that make this test fail when status is enabled |
|
|||
2 | $ unset RHG_STATUS |
|
|||
3 |
|
||||
4 |
|
||||
5 |
|
|
1 | $ hg init t | |
6 | $ cd t |
|
2 | $ cd t | |
7 |
|
3 |
@@ -9,10 +9,6 b'' | |||||
9 | > EOF |
|
9 | > EOF | |
10 | #endif |
|
10 | #endif | |
11 |
|
11 | |||
12 | TODO: fix rhg bugs that make this test fail when status is enabled |
|
|||
13 | $ unset RHG_STATUS |
|
|||
14 |
|
||||
15 |
|
||||
16 | $ hg init repo1 |
|
12 | $ hg init repo1 | |
17 | $ cd repo1 |
|
13 | $ cd repo1 | |
18 | $ mkdir a b a/1 b/1 b/2 |
|
14 | $ mkdir a b a/1 b/1 b/2 | |
@@ -223,7 +219,7 b' hg status:' | |||||
223 | ? unknown |
|
219 | ? unknown | |
224 |
|
220 | |||
225 | hg status -n: |
|
221 | hg status -n: | |
226 |
$ env |
|
222 | $ env RHG_ON_UNSUPPORTED=abort hg status -n | |
227 | added |
|
223 | added | |
228 | removed |
|
224 | removed | |
229 | deleted |
|
225 | deleted |
General Comments 0
You need to be logged in to leave comments.
Login now