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 | 5 | mod cat; |
|
6 | 6 | mod debugdata; |
|
7 | 7 | mod list_tracked_files; |
|
8 | mod status_rev_rev; | |
|
8 | 9 | pub use cat::{cat, CatOutput}; |
|
9 | 10 | pub use debugdata::{debug_data, DebugDataKind}; |
|
10 | 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 | 30 | use hg::utils::hg_path::{hg_path_to_path_buf, HgPath}; |
|
31 | 31 | use hg::DirstateStatus; |
|
32 | 32 | use hg::PatternFileWarning; |
|
33 | use hg::Revision; | |
|
33 | 34 | use hg::StatusError; |
|
34 | 35 | use hg::StatusOptions; |
|
35 | 36 | use hg::{self, narrow, sparse}; |
|
36 | 37 | use log::info; |
|
37 | 38 | use rayon::prelude::*; |
|
39 | use std::borrow::Cow; | |
|
38 | 40 | use std::io; |
|
39 | 41 | use std::mem::take; |
|
40 | 42 | use std::path::PathBuf; |
@@ -141,6 +143,38 b' pub fn args() -> clap::Command {' | |||
|
141 | 143 | .action(clap::ArgAction::SetTrue) |
|
142 | 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 | 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 | 264 | let config = invocation.config; |
|
231 | 265 | let args = invocation.subcommand_args; |
|
232 | 266 | |
|
267 | let revs = args.get_many::<String>("rev"); | |
|
233 | 268 | let print0 = args.get_flag("print0"); |
|
234 | 269 | let verbose = args.get_flag("verbose") |
|
235 | 270 | || config.get_bool(b"ui", b"verbose")? |
@@ -263,6 +298,7 b' pub fn run(invocation: &crate::CliInvoca' | |||
|
263 | 298 | || config.get_bool(b"ui", b"statuscopies")?; |
|
264 | 299 | |
|
265 | 300 | let repo = invocation.repo?; |
|
301 | let revpair = parse_revpair(repo, revs.map(|i| i.cloned().collect()))?; | |
|
266 | 302 | |
|
267 | 303 | if verbose && has_unfinished_state(repo)? { |
|
268 | 304 | return Err(CommandError::unsupported( |
@@ -407,6 +443,57 b' pub fn run(invocation: &crate::CliInvoca' | |||
|
407 | 443 | )) |
|
408 | 444 | }; |
|
409 | 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 | 497 | let (sparse_matcher, sparse_warnings) = sparse::matcher(repo)?; |
|
411 | 498 | let matcher = match (repo.has_narrow(), repo.has_sparse()) { |
|
412 | 499 | (true, true) => { |
@@ -524,13 +524,20 b' fn exit_no_fallback(' | |||
|
524 | 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 | 539 | macro_rules! subcommands { |
|
528 | 540 | ($( $command: ident )+) => { |
|
529 | mod commands { | |
|
530 | $( | |
|
531 | pub mod $command; | |
|
532 | )+ | |
|
533 | } | |
|
534 | 541 | |
|
535 | 542 | fn add_subcommand_args(app: clap::Command) -> clap::Command { |
|
536 | 543 | app |
@@ -88,6 +88,33 b' Status compared to parent of the working' | |||
|
88 | 88 | |
|
89 | 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 | 118 | $ hg status -A --rev 0:1 'glob:content1_content2_*' |
|
92 | 119 | M content1_content2_content1-tracked |
|
93 | 120 | M content1_content2_content1-untracked |
General Comments 0
You need to be logged in to leave comments.
Login now