##// END OF EJS Templates
rhg: Add support for `rhg status --copies`...
Simon Sapin -
r49285:473af5cb default
parent child Browse files
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(Debug, Default)]
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<HgPathCow<'a>>,
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<HgPathCow<'a>>,
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<HgPathCow<'a>>,
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<HgPathCow<'a>>,
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<HgPathCow<'a>>,
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<HgPathCow<'a>>,
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<HgPathCow<'a>>,
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<HgPathCow<'a>>,
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 => self
441 EntryState::Added => {
385 .outcome
442 self.push_outcome(Outcome::Added, &dirstate_node)?
386 .lock()
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 hg_path: &BorrowedPath<'tree, 'on_disk>,
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(hg_path) {
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 .lock()
707 Outcome::Ignored,
689 .unwrap()
708 hg_path,
690 .ignored
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 .lock()
714 Outcome::Unknown,
697 .unwrap()
715 hg_path,
698 .unknown
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 collection.iter() {
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_pybytes_list(py, status_res.modified.as_ref());
254 let modified = collect_status_path_list(py, &status_res.modified);
245 let added = collect_pybytes_list(py, status_res.added.as_ref());
255 let added = collect_status_path_list(py, &status_res.added);
246 let removed = collect_pybytes_list(py, status_res.removed.as_ref());
256 let removed = collect_status_path_list(py, &status_res.removed);
247 let deleted = collect_pybytes_list(py, status_res.deleted.as_ref());
257 let deleted = collect_status_path_list(py, &status_res.deleted);
248 let clean = collect_pybytes_list(py, status_res.clean.as_ref());
258 let clean = collect_status_path_list(py, &status_res.clean);
249 let ignored = collect_pybytes_list(py, status_res.ignored.as_ref());
259 let ignored = collect_status_path_list(py, &status_res.ignored);
250 let unknown = collect_pybytes_list(py, status_res.unknown.as_ref());
260 let unknown = collect_status_path_list(py, &status_res.unknown);
251 let unsure = collect_pybytes_list(py, status_res.unsure.as_ref());
261 let unsure = collect_status_path_list(py, &status_res.unsure);
252 let bad = collect_bad_matches(py, status_res.bad.as_ref())?;
262 let bad = collect_bad_matches(py, &status_res.bad)?;
253 let traversed = collect_pybytes_list(py, status_res.traversed.as_ref());
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::{HgPathCow, StatusOptions};
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 display_states = if args.is_present("all") {
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<HgPathCow>,
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 $ hg init t
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 RHG_STATUS=1 RHG_ON_UNSUPPORTED=abort hg status -n
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