##// END OF EJS Templates
rhg: add error message for paths outside the repository when cwd != root...
Raphaël Gomès -
r50270:044e42ae stable
parent child Browse files
Show More
@@ -1,105 +1,114
1 use crate::error::CommandError;
1 use crate::error::CommandError;
2 use clap::Arg;
2 use clap::Arg;
3 use format_bytes::format_bytes;
3 use format_bytes::format_bytes;
4 use hg::operations::cat;
4 use hg::operations::cat;
5 use hg::utils::hg_path::HgPathBuf;
5 use hg::utils::hg_path::HgPathBuf;
6 use micro_timer::timed;
6 use micro_timer::timed;
7 use std::convert::TryFrom;
7 use std::convert::TryFrom;
8
8
9 pub const HELP_TEXT: &str = "
9 pub const HELP_TEXT: &str = "
10 Output the current or given revision of files
10 Output the current or given revision of files
11 ";
11 ";
12
12
13 pub fn args() -> clap::App<'static, 'static> {
13 pub fn args() -> clap::App<'static, 'static> {
14 clap::SubCommand::with_name("cat")
14 clap::SubCommand::with_name("cat")
15 .arg(
15 .arg(
16 Arg::with_name("rev")
16 Arg::with_name("rev")
17 .help("search the repository as it is in REV")
17 .help("search the repository as it is in REV")
18 .short("-r")
18 .short("-r")
19 .long("--rev")
19 .long("--rev")
20 .value_name("REV")
20 .value_name("REV")
21 .takes_value(true),
21 .takes_value(true),
22 )
22 )
23 .arg(
23 .arg(
24 clap::Arg::with_name("files")
24 clap::Arg::with_name("files")
25 .required(true)
25 .required(true)
26 .multiple(true)
26 .multiple(true)
27 .empty_values(false)
27 .empty_values(false)
28 .value_name("FILE")
28 .value_name("FILE")
29 .help("Files to output"),
29 .help("Files to output"),
30 )
30 )
31 .about(HELP_TEXT)
31 .about(HELP_TEXT)
32 }
32 }
33
33
34 #[timed]
34 #[timed]
35 pub fn run(invocation: &crate::CliInvocation) -> Result<(), CommandError> {
35 pub fn run(invocation: &crate::CliInvocation) -> Result<(), CommandError> {
36 let cat_enabled_default = true;
36 let cat_enabled_default = true;
37 let cat_enabled = invocation.config.get_option(b"rhg", b"cat")?;
37 let cat_enabled = invocation.config.get_option(b"rhg", b"cat")?;
38 if !cat_enabled.unwrap_or(cat_enabled_default) {
38 if !cat_enabled.unwrap_or(cat_enabled_default) {
39 return Err(CommandError::unsupported(
39 return Err(CommandError::unsupported(
40 "cat is disabled in rhg (enable it with 'rhg.cat = true' \
40 "cat is disabled in rhg (enable it with 'rhg.cat = true' \
41 or enable fallback with 'rhg.on-unsupported = fallback')",
41 or enable fallback with 'rhg.on-unsupported = fallback')",
42 ));
42 ));
43 }
43 }
44
44
45 let rev = invocation.subcommand_args.value_of("rev");
45 let rev = invocation.subcommand_args.value_of("rev");
46 let file_args = match invocation.subcommand_args.values_of("files") {
46 let file_args = match invocation.subcommand_args.values_of("files") {
47 Some(files) => files.collect(),
47 Some(files) => files.collect(),
48 None => vec![],
48 None => vec![],
49 };
49 };
50
50
51 let repo = invocation.repo?;
51 let repo = invocation.repo?;
52 let cwd = hg::utils::current_dir()?;
52 let cwd = hg::utils::current_dir()?;
53 let working_directory = repo.working_directory_path();
53 let working_directory = repo.working_directory_path();
54 let working_directory = cwd.join(working_directory); // Make it absolute
54 let working_directory = cwd.join(working_directory); // Make it absolute
55
55
56 let mut files = vec![];
56 let mut files = vec![];
57 for file in file_args.iter() {
57 for file in file_args.iter() {
58 if file.starts_with("set:") {
58 if file.starts_with("set:") {
59 let message = "fileset";
59 let message = "fileset";
60 return Err(CommandError::unsupported(message));
60 return Err(CommandError::unsupported(message));
61 }
61 }
62
62
63 let normalized = cwd.join(&file);
63 let normalized = cwd.join(&file);
64 // TODO: actually normalize `..` path segments etc?
64 // TODO: actually normalize `..` path segments etc?
65 let dotted = normalized.components().any(|c| c.as_os_str() == "..");
65 let dotted = normalized.components().any(|c| c.as_os_str() == "..");
66 if file == &"." || dotted {
66 if file == &"." || dotted {
67 let message = "`..` or `.` path segment";
67 let message = "`..` or `.` path segment";
68 return Err(CommandError::unsupported(message));
68 return Err(CommandError::unsupported(message));
69 }
69 }
70 let relative_path = working_directory
71 .strip_prefix(&cwd)
72 .unwrap_or(&working_directory);
70 let stripped = normalized
73 let stripped = normalized
71 .strip_prefix(&working_directory)
74 .strip_prefix(&working_directory)
72 // TODO: error message for path arguments outside of the repo
75 .map_err(|_| {
73 .map_err(|_| CommandError::abort(""))?;
76 CommandError::abort(format!(
77 "abort: {} not under root '{}'\n(consider using '--cwd {}')",
78 file,
79 working_directory.display(),
80 relative_path.display(),
81 ))
82 })?;
74 let hg_file = HgPathBuf::try_from(stripped.to_path_buf())
83 let hg_file = HgPathBuf::try_from(stripped.to_path_buf())
75 .map_err(|e| CommandError::abort(e.to_string()))?;
84 .map_err(|e| CommandError::abort(e.to_string()))?;
76 files.push(hg_file);
85 files.push(hg_file);
77 }
86 }
78 let files = files.iter().map(|file| file.as_ref()).collect();
87 let files = files.iter().map(|file| file.as_ref()).collect();
79 // TODO probably move this to a util function like `repo.default_rev` or
88 // TODO probably move this to a util function like `repo.default_rev` or
80 // something when it's used somewhere else
89 // something when it's used somewhere else
81 let rev = match rev {
90 let rev = match rev {
82 Some(r) => r.to_string(),
91 Some(r) => r.to_string(),
83 None => format!("{:x}", repo.dirstate_parents()?.p1),
92 None => format!("{:x}", repo.dirstate_parents()?.p1),
84 };
93 };
85
94
86 let output = cat(&repo, &rev, files).map_err(|e| (e, rev.as_str()))?;
95 let output = cat(&repo, &rev, files).map_err(|e| (e, rev.as_str()))?;
87 for (_file, contents) in output.results {
96 for (_file, contents) in output.results {
88 invocation.ui.write_stdout(&contents)?;
97 invocation.ui.write_stdout(&contents)?;
89 }
98 }
90 if !output.missing.is_empty() {
99 if !output.missing.is_empty() {
91 let short = format!("{:x}", output.node.short()).into_bytes();
100 let short = format!("{:x}", output.node.short()).into_bytes();
92 for path in &output.missing {
101 for path in &output.missing {
93 invocation.ui.write_stderr(&format_bytes!(
102 invocation.ui.write_stderr(&format_bytes!(
94 b"{}: no such file in rev {}\n",
103 b"{}: no such file in rev {}\n",
95 path.as_bytes(),
104 path.as_bytes(),
96 short
105 short
97 ))?;
106 ))?;
98 }
107 }
99 }
108 }
100 if output.found_any {
109 if output.found_any {
101 Ok(())
110 Ok(())
102 } else {
111 } else {
103 Err(CommandError::Unsuccessful)
112 Err(CommandError::Unsuccessful)
104 }
113 }
105 }
114 }
General Comments 0
You need to be logged in to leave comments. Login now