Show More
@@ -0,0 +1,89 b'' | |||||
|
1 | use crate::errors::HgError; | |||
|
2 | use crate::matchers::Matcher; | |||
|
3 | use crate::repo::Repo; | |||
|
4 | use crate::revlog::manifest::Manifest; | |||
|
5 | use crate::utils::filter_map_results; | |||
|
6 | use crate::utils::hg_path::HgPath; | |||
|
7 | use crate::utils::merge_join_results_by; | |||
|
8 | ||||
|
9 | use crate::Revision; | |||
|
10 | ||||
|
11 | use itertools::EitherOrBoth; | |||
|
12 | ||||
|
13 | #[derive(Debug, Copy, Clone)] | |||
|
14 | pub enum DiffStatus { | |||
|
15 | Removed, | |||
|
16 | Added, | |||
|
17 | Matching, | |||
|
18 | Modified, | |||
|
19 | } | |||
|
20 | ||||
|
21 | pub struct StatusRevRev { | |||
|
22 | manifest1: Manifest, | |||
|
23 | manifest2: Manifest, | |||
|
24 | narrow_matcher: Box<dyn Matcher>, | |||
|
25 | } | |||
|
26 | ||||
|
27 | fn manifest_for_rev(repo: &Repo, rev: Revision) -> Result<Manifest, HgError> { | |||
|
28 | repo.manifest_for_rev(rev.into()).map_err(|e| { | |||
|
29 | HgError::corrupted(format!( | |||
|
30 | "manifest lookup failed for revision {}: {}", | |||
|
31 | rev, e | |||
|
32 | )) | |||
|
33 | }) | |||
|
34 | } | |||
|
35 | ||||
|
36 | pub fn status_rev_rev_no_copies( | |||
|
37 | repo: &Repo, | |||
|
38 | rev1: Revision, | |||
|
39 | rev2: Revision, | |||
|
40 | narrow_matcher: Box<dyn Matcher>, | |||
|
41 | ) -> Result<StatusRevRev, HgError> { | |||
|
42 | let manifest1 = manifest_for_rev(repo, rev1)?; | |||
|
43 | let manifest2 = manifest_for_rev(repo, rev2)?; | |||
|
44 | Ok(StatusRevRev { | |||
|
45 | manifest1, | |||
|
46 | manifest2, | |||
|
47 | narrow_matcher, | |||
|
48 | }) | |||
|
49 | } | |||
|
50 | ||||
|
51 | impl StatusRevRev { | |||
|
52 | pub fn iter( | |||
|
53 | &self, | |||
|
54 | ) -> impl Iterator<Item = Result<(&HgPath, DiffStatus), HgError>> { | |||
|
55 | let iter1 = self.manifest1.iter(); | |||
|
56 | let iter2 = self.manifest2.iter(); | |||
|
57 | ||||
|
58 | let merged = | |||
|
59 | merge_join_results_by(iter1, iter2, |i1, i2| i1.path.cmp(i2.path)); | |||
|
60 | ||||
|
61 | filter_map_results(merged, |entry| { | |||
|
62 | let (path, status) = match entry { | |||
|
63 | EitherOrBoth::Left(entry) => { | |||
|
64 | let path = entry.path; | |||
|
65 | (path, DiffStatus::Removed) | |||
|
66 | } | |||
|
67 | EitherOrBoth::Right(entry) => { | |||
|
68 | let path = entry.path; | |||
|
69 | (path, DiffStatus::Added) | |||
|
70 | } | |||
|
71 | EitherOrBoth::Both(entry1, entry2) => { | |||
|
72 | let path = entry1.path; | |||
|
73 | if entry1.node_id().unwrap() == entry2.node_id().unwrap() | |||
|
74 | && entry1.flags == entry2.flags | |||
|
75 | { | |||
|
76 | (path, DiffStatus::Matching) | |||
|
77 | } else { | |||
|
78 | (path, DiffStatus::Modified) | |||
|
79 | } | |||
|
80 | } | |||
|
81 | }; | |||
|
82 | Ok(if self.narrow_matcher.matches(path) { | |||
|
83 | Some((path, status)) | |||
|
84 | } else { | |||
|
85 | None | |||
|
86 | }) | |||
|
87 | }) | |||
|
88 | } | |||
|
89 | } |
@@ -5,6 +5,8 b'' | |||||
5 | mod cat; |
|
5 | mod cat; | |
6 | mod debugdata; |
|
6 | mod debugdata; | |
7 | mod list_tracked_files; |
|
7 | mod list_tracked_files; | |
|
8 | mod status_rev_rev; | |||
8 | pub use cat::{cat, CatOutput}; |
|
9 | pub use cat::{cat, CatOutput}; | |
9 | pub use debugdata::{debug_data, DebugDataKind}; |
|
10 | pub use debugdata::{debug_data, DebugDataKind}; | |
10 | pub use list_tracked_files::{list_rev_tracked_files, FilesForRev}; |
|
11 | pub use list_tracked_files::{list_rev_tracked_files, FilesForRev}; | |
|
12 | pub use status_rev_rev::{status_rev_rev_no_copies, DiffStatus, StatusRevRev}; |
@@ -30,11 +30,13 b' use hg::utils::files::{' | |||||
30 | use hg::utils::hg_path::{hg_path_to_path_buf, HgPath}; |
|
30 | use hg::utils::hg_path::{hg_path_to_path_buf, HgPath}; | |
31 | use hg::DirstateStatus; |
|
31 | use hg::DirstateStatus; | |
32 | use hg::PatternFileWarning; |
|
32 | use hg::PatternFileWarning; | |
|
33 | use hg::Revision; | |||
33 | use hg::StatusError; |
|
34 | use hg::StatusError; | |
34 | use hg::StatusOptions; |
|
35 | use hg::StatusOptions; | |
35 | use hg::{self, narrow, sparse}; |
|
36 | use hg::{self, narrow, sparse}; | |
36 | use log::info; |
|
37 | use log::info; | |
37 | use rayon::prelude::*; |
|
38 | use rayon::prelude::*; | |
|
39 | use std::borrow::Cow; | |||
38 | use std::io; |
|
40 | use std::io; | |
39 | use std::mem::take; |
|
41 | use std::mem::take; | |
40 | use std::path::PathBuf; |
|
42 | use std::path::PathBuf; | |
@@ -141,6 +143,38 b' pub fn args() -> clap::Command {' | |||||
141 | .action(clap::ArgAction::SetTrue) |
|
143 | .action(clap::ArgAction::SetTrue) | |
142 | .long("verbose"), |
|
144 | .long("verbose"), | |
143 | ) |
|
145 | ) | |
|
146 | .arg( | |||
|
147 | Arg::new("rev") | |||
|
148 | .help("show difference from/to revision") | |||
|
149 | .long("rev") | |||
|
150 | .num_args(1) | |||
|
151 | .action(clap::ArgAction::Append) | |||
|
152 | .value_name("REV"), | |||
|
153 | ) | |||
|
154 | } | |||
|
155 | ||||
|
156 | fn parse_revpair( | |||
|
157 | repo: &Repo, | |||
|
158 | revs: Option<Vec<String>>, | |||
|
159 | ) -> Result<Option<(Revision, Revision)>, CommandError> { | |||
|
160 | let revs = match revs { | |||
|
161 | None => return Ok(None), | |||
|
162 | Some(revs) => revs, | |||
|
163 | }; | |||
|
164 | if revs.is_empty() { | |||
|
165 | return Ok(None); | |||
|
166 | } | |||
|
167 | if revs.len() != 2 { | |||
|
168 | return Err(CommandError::unsupported("expected 0 or 2 --rev flags")); | |||
|
169 | } | |||
|
170 | ||||
|
171 | let rev1 = &revs[0]; | |||
|
172 | let rev2 = &revs[1]; | |||
|
173 | let rev1 = hg::revset::resolve_single(rev1, repo) | |||
|
174 | .map_err(|e| (e, rev1.as_str()))?; | |||
|
175 | let rev2 = hg::revset::resolve_single(rev2, repo) | |||
|
176 | .map_err(|e| (e, rev2.as_str()))?; | |||
|
177 | Ok(Some((rev1, rev2))) | |||
144 | } |
|
178 | } | |
145 |
|
179 | |||
146 | /// Pure data type allowing the caller to specify file states to display |
|
180 | /// Pure data type allowing the caller to specify file states to display | |
@@ -230,6 +264,7 b' pub fn run(invocation: &crate::CliInvoca' | |||||
230 | let config = invocation.config; |
|
264 | let config = invocation.config; | |
231 | let args = invocation.subcommand_args; |
|
265 | let args = invocation.subcommand_args; | |
232 |
|
266 | |||
|
267 | let revs = args.get_many::<String>("rev"); | |||
233 | let print0 = args.get_flag("print0"); |
|
268 | let print0 = args.get_flag("print0"); | |
234 | let verbose = args.get_flag("verbose") |
|
269 | let verbose = args.get_flag("verbose") | |
235 | || config.get_bool(b"ui", b"verbose")? |
|
270 | || config.get_bool(b"ui", b"verbose")? | |
@@ -263,6 +298,7 b' pub fn run(invocation: &crate::CliInvoca' | |||||
263 | || config.get_bool(b"ui", b"statuscopies")?; |
|
298 | || config.get_bool(b"ui", b"statuscopies")?; | |
264 |
|
299 | |||
265 | let repo = invocation.repo?; |
|
300 | let repo = invocation.repo?; | |
|
301 | let revpair = parse_revpair(repo, revs.map(|i| i.cloned().collect()))?; | |||
266 |
|
302 | |||
267 | if verbose && has_unfinished_state(repo)? { |
|
303 | if verbose && has_unfinished_state(repo)? { | |
268 | return Err(CommandError::unsupported( |
|
304 | return Err(CommandError::unsupported( | |
@@ -407,6 +443,57 b' pub fn run(invocation: &crate::CliInvoca' | |||||
407 | )) |
|
443 | )) | |
408 | }; |
|
444 | }; | |
409 | let (narrow_matcher, narrow_warnings) = narrow::matcher(repo)?; |
|
445 | let (narrow_matcher, narrow_warnings) = narrow::matcher(repo)?; | |
|
446 | ||||
|
447 | match revpair { | |||
|
448 | Some((rev1, rev2)) => { | |||
|
449 | let mut ds_status = DirstateStatus::default(); | |||
|
450 | if list_copies { | |||
|
451 | return Err(CommandError::unsupported( | |||
|
452 | "status --rev --rev with copy information is not implemented yet", | |||
|
453 | )); | |||
|
454 | } | |||
|
455 | ||||
|
456 | let stat = hg::operations::status_rev_rev_no_copies( | |||
|
457 | repo, | |||
|
458 | rev1, | |||
|
459 | rev2, | |||
|
460 | narrow_matcher, | |||
|
461 | )?; | |||
|
462 | for entry in stat.iter() { | |||
|
463 | let (path, status) = entry?; | |||
|
464 | let path = StatusPath { | |||
|
465 | path: Cow::Borrowed(path), | |||
|
466 | copy_source: None, | |||
|
467 | }; | |||
|
468 | match status { | |||
|
469 | hg::operations::DiffStatus::Removed => { | |||
|
470 | if display_states.removed { | |||
|
471 | ds_status.removed.push(path) | |||
|
472 | } | |||
|
473 | } | |||
|
474 | hg::operations::DiffStatus::Added => { | |||
|
475 | if display_states.added { | |||
|
476 | ds_status.added.push(path) | |||
|
477 | } | |||
|
478 | } | |||
|
479 | hg::operations::DiffStatus::Modified => { | |||
|
480 | if display_states.modified { | |||
|
481 | ds_status.modified.push(path) | |||
|
482 | } | |||
|
483 | } | |||
|
484 | hg::operations::DiffStatus::Matching => { | |||
|
485 | if display_states.clean { | |||
|
486 | ds_status.clean.push(path) | |||
|
487 | } | |||
|
488 | } | |||
|
489 | } | |||
|
490 | } | |||
|
491 | output.output(display_states, ds_status)?; | |||
|
492 | return Ok(()); | |||
|
493 | } | |||
|
494 | None => (), | |||
|
495 | } | |||
|
496 | ||||
410 | let (sparse_matcher, sparse_warnings) = sparse::matcher(repo)?; |
|
497 | let (sparse_matcher, sparse_warnings) = sparse::matcher(repo)?; | |
411 | let matcher = match (repo.has_narrow(), repo.has_sparse()) { |
|
498 | let matcher = match (repo.has_narrow(), repo.has_sparse()) { | |
412 | (true, true) => { |
|
499 | (true, true) => { |
@@ -524,13 +524,20 b' fn exit_no_fallback(' | |||||
524 | std::process::exit(exit_code(&result, use_detailed_exit_code)) |
|
524 | std::process::exit(exit_code(&result, use_detailed_exit_code)) | |
525 | } |
|
525 | } | |
526 |
|
526 | |||
|
527 | mod commands { | |||
|
528 | pub mod cat; | |||
|
529 | pub mod config; | |||
|
530 | pub mod debugdata; | |||
|
531 | pub mod debugignorerhg; | |||
|
532 | pub mod debugrequirements; | |||
|
533 | pub mod debugrhgsparse; | |||
|
534 | pub mod files; | |||
|
535 | pub mod root; | |||
|
536 | pub mod status; | |||
|
537 | } | |||
|
538 | ||||
527 | macro_rules! subcommands { |
|
539 | macro_rules! subcommands { | |
528 | ($( $command: ident )+) => { |
|
540 | ($( $command: ident )+) => { | |
529 | mod commands { |
|
|||
530 | $( |
|
|||
531 | pub mod $command; |
|
|||
532 | )+ |
|
|||
533 | } |
|
|||
534 |
|
541 | |||
535 | fn add_subcommand_args(app: clap::Command) -> clap::Command { |
|
542 | fn add_subcommand_args(app: clap::Command) -> clap::Command { | |
536 | app |
|
543 | app |
@@ -88,6 +88,33 b' Status compared to parent of the working' | |||||
88 |
|
88 | |||
89 | Status between first and second commit. Should ignore dirstate status. |
|
89 | Status between first and second commit. Should ignore dirstate status. | |
90 |
|
90 | |||
|
91 | $ hg status -marc --rev 0 --rev 1 --config rhg.on-unsupported=abort | |||
|
92 | M content1_content2_content1-tracked | |||
|
93 | M content1_content2_content1-untracked | |||
|
94 | M content1_content2_content2-tracked | |||
|
95 | M content1_content2_content2-untracked | |||
|
96 | M content1_content2_content3-tracked | |||
|
97 | M content1_content2_content3-untracked | |||
|
98 | M content1_content2_missing-tracked | |||
|
99 | M content1_content2_missing-untracked | |||
|
100 | A missing_content2_content2-tracked | |||
|
101 | A missing_content2_content2-untracked | |||
|
102 | A missing_content2_content3-tracked | |||
|
103 | A missing_content2_content3-untracked | |||
|
104 | A missing_content2_missing-tracked | |||
|
105 | A missing_content2_missing-untracked | |||
|
106 | R content1_missing_content1-tracked | |||
|
107 | R content1_missing_content1-untracked | |||
|
108 | R content1_missing_content3-tracked | |||
|
109 | R content1_missing_content3-untracked | |||
|
110 | R content1_missing_missing-tracked | |||
|
111 | R content1_missing_missing-untracked | |||
|
112 | C content1_content1_content1-tracked | |||
|
113 | C content1_content1_content1-untracked | |||
|
114 | C content1_content1_content3-tracked | |||
|
115 | C content1_content1_content3-untracked | |||
|
116 | C content1_content1_missing-tracked | |||
|
117 | C content1_content1_missing-untracked | |||
91 | $ hg status -A --rev 0:1 'glob:content1_content2_*' |
|
118 | $ hg status -A --rev 0:1 'glob:content1_content2_*' | |
92 | M content1_content2_content1-tracked |
|
119 | M content1_content2_content1-tracked | |
93 | M content1_content2_content1-untracked |
|
120 | M content1_content2_content1-untracked |
General Comments 0
You need to be logged in to leave comments.
Login now