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