Show More
@@ -1,116 +1,149 b'' | |||
|
1 | 1 | use crate::error::CommandError; |
|
2 | 2 | use crate::ui::{ |
|
3 | 3 | print_narrow_sparse_warnings, relative_paths, RelativePaths, Ui, |
|
4 | 4 | }; |
|
5 | 5 | use crate::utils::path_utils::RelativizePaths; |
|
6 | 6 | use clap::Arg; |
|
7 | use hg::filepatterns::parse_pattern_args; | |
|
8 | use hg::matchers::IntersectionMatcher; | |
|
7 | 9 | use hg::narrow; |
|
8 | 10 | use hg::operations::list_rev_tracked_files; |
|
9 | 11 | use hg::repo::Repo; |
|
12 | use hg::utils::files::get_bytes_from_os_str; | |
|
10 | 13 | use hg::utils::filter_map_results; |
|
11 | 14 | use hg::utils::hg_path::HgPath; |
|
12 | 15 | use rayon::prelude::*; |
|
13 | 16 | |
|
14 | 17 | pub const HELP_TEXT: &str = " |
|
15 | 18 | List tracked files. |
|
16 | 19 | |
|
17 | 20 | Returns 0 on success. |
|
18 | 21 | "; |
|
19 | 22 | |
|
20 | 23 | pub fn args() -> clap::Command { |
|
21 | 24 | clap::command!("files") |
|
22 | 25 | .arg( |
|
23 | 26 | Arg::new("rev") |
|
24 | 27 | .help("search the repository as it is in REV") |
|
25 | 28 | .short('r') |
|
26 | 29 | .long("revision") |
|
27 | 30 | .value_name("REV"), |
|
28 | 31 | ) |
|
32 | .arg( | |
|
33 | Arg::new("file") | |
|
34 | .value_parser(clap::value_parser!(std::ffi::OsString)) | |
|
35 | .help("show only these files") | |
|
36 | .action(clap::ArgAction::Append), | |
|
37 | ) | |
|
29 | 38 | .about(HELP_TEXT) |
|
30 | 39 | } |
|
31 | 40 | |
|
32 | 41 | pub fn run(invocation: &crate::CliInvocation) -> Result<(), CommandError> { |
|
33 | 42 | let relative_paths = match relative_paths(invocation.config)? { |
|
34 | 43 | RelativePaths::Legacy => true, |
|
35 | 44 | RelativePaths::Bool(v) => v, |
|
36 | 45 | }; |
|
37 | 46 | |
|
38 |
let |
|
|
47 | let args = invocation.subcommand_args; | |
|
48 | let rev = args.get_one::<String>("rev"); | |
|
39 | 49 | |
|
40 | 50 | let repo = invocation.repo?; |
|
41 | 51 | |
|
42 | 52 | // It seems better if this check is removed: this would correspond to |
|
43 | 53 | // automatically enabling the extension if the repo requires it. |
|
44 | 54 | // However we need this check to be in sync with vanilla hg so hg tests |
|
45 | 55 | // pass. |
|
46 | 56 | if repo.has_sparse() |
|
47 | 57 | && invocation.config.get(b"extensions", b"sparse").is_none() |
|
48 | 58 | { |
|
49 | 59 | return Err(CommandError::unsupported( |
|
50 | 60 | "repo is using sparse, but sparse extension is not enabled", |
|
51 | 61 | )); |
|
52 | 62 | } |
|
53 | 63 | |
|
54 |
let ( |
|
|
64 | let (matcher, narrow_warnings) = narrow::matcher(repo)?; | |
|
55 | 65 | print_narrow_sparse_warnings(&narrow_warnings, &[], invocation.ui, repo)?; |
|
66 | let matcher = match args.get_many::<std::ffi::OsString>("file") { | |
|
67 | None => matcher, | |
|
68 | Some(files) => { | |
|
69 | let patterns: Vec<Vec<u8>> = files | |
|
70 | .filter(|s| !s.is_empty()) | |
|
71 | .map(get_bytes_from_os_str) | |
|
72 | .collect(); | |
|
73 | for file in &patterns { | |
|
74 | if file.starts_with(b"set:") { | |
|
75 | return Err(CommandError::unsupported("fileset")); | |
|
76 | } | |
|
77 | } | |
|
78 | let cwd = hg::utils::current_dir()?; | |
|
79 | let root = repo.working_directory_path(); | |
|
80 | let ignore_patterns = parse_pattern_args(patterns, &cwd, root)?; | |
|
81 | let files_matcher = | |
|
82 | hg::matchers::PatternMatcher::new(ignore_patterns)?; | |
|
83 | Box::new(IntersectionMatcher::new( | |
|
84 | Box::new(files_matcher), | |
|
85 | matcher, | |
|
86 | )) | |
|
87 | } | |
|
88 | }; | |
|
56 | 89 | |
|
57 | 90 | if let Some(rev) = rev { |
|
58 |
let files = list_rev_tracked_files(repo, rev, |
|
|
91 | let files = list_rev_tracked_files(repo, rev, matcher) | |
|
59 | 92 | .map_err(|e| (e, rev.as_ref()))?; |
|
60 | 93 | display_files(invocation.ui, repo, relative_paths, files.iter()) |
|
61 | 94 | } else { |
|
62 | 95 | // The dirstate always reflects the sparse narrowspec. |
|
63 | 96 | let dirstate = repo.dirstate_map()?; |
|
64 | 97 | let files_res: Result<Vec<_>, _> = |
|
65 | 98 | filter_map_results(dirstate.iter(), |(path, entry)| { |
|
66 |
Ok(if entry.tracked() && |
|
|
99 | Ok(if entry.tracked() && matcher.matches(path) { | |
|
67 | 100 | Some(path) |
|
68 | 101 | } else { |
|
69 | 102 | None |
|
70 | 103 | }) |
|
71 | 104 | }) |
|
72 | 105 | .collect(); |
|
73 | 106 | |
|
74 | 107 | let mut files = files_res?; |
|
75 | 108 | files.par_sort_unstable(); |
|
76 | 109 | |
|
77 | 110 | display_files( |
|
78 | 111 | invocation.ui, |
|
79 | 112 | repo, |
|
80 | 113 | relative_paths, |
|
81 | 114 | files.into_iter().map::<Result<_, CommandError>, _>(Ok), |
|
82 | 115 | ) |
|
83 | 116 | } |
|
84 | 117 | } |
|
85 | 118 | |
|
86 | 119 | fn display_files<'a, E>( |
|
87 | 120 | ui: &Ui, |
|
88 | 121 | repo: &Repo, |
|
89 | 122 | relative_paths: bool, |
|
90 | 123 | files: impl IntoIterator<Item = Result<&'a HgPath, E>>, |
|
91 | 124 | ) -> Result<(), CommandError> |
|
92 | 125 | where |
|
93 | 126 | CommandError: From<E>, |
|
94 | 127 | { |
|
95 | 128 | let mut stdout = ui.stdout_buffer(); |
|
96 | 129 | let mut any = false; |
|
97 | 130 | |
|
98 | 131 | let relativize = RelativizePaths::new(repo)?; |
|
99 | 132 | for result in files { |
|
100 | 133 | let path = result?; |
|
101 | 134 | if relative_paths { |
|
102 | 135 | stdout.write_all(&relativize.relativize(path))?; |
|
103 | 136 | } else { |
|
104 | 137 | stdout.write_all(path.as_bytes())?; |
|
105 | 138 | } |
|
106 | 139 | stdout.write_all(b"\n")?; |
|
107 | 140 | any = true; |
|
108 | 141 | } |
|
109 | 142 | |
|
110 | 143 | stdout.flush()?; |
|
111 | 144 | if any { |
|
112 | 145 | Ok(()) |
|
113 | 146 | } else { |
|
114 | 147 | Err(CommandError::Unsuccessful) |
|
115 | 148 | } |
|
116 | 149 | } |
General Comments 0
You need to be logged in to leave comments.
Login now