##// END OF EJS Templates
rhg: support rhg status --rev --rev
Arseniy Alekseyev -
r52048:ac3859a8 default
parent child Browse files
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