Show More
@@ -1,10 +1,9 | |||
|
1 | 1 | use crate::error::CommandError; |
|
2 | use crate::utils::path_utils::resolve_file_args; | |
|
2 | 3 | use clap::Arg; |
|
3 | 4 | use format_bytes::format_bytes; |
|
4 | 5 | use hg::operations::cat; |
|
5 | use hg::utils::hg_path::HgPathBuf; | |
|
6 | 6 | use std::ffi::OsString; |
|
7 | use std::os::unix::prelude::OsStrExt; | |
|
8 | 7 | |
|
9 | 8 | pub const HELP_TEXT: &str = " |
|
10 | 9 | Output the current or given revision of files |
@@ -40,52 +39,15 pub fn run(invocation: &crate::CliInvoca | |||
|
40 | 39 | )); |
|
41 | 40 | } |
|
42 | 41 | |
|
43 | let rev = invocation.subcommand_args.get_one::<String>("rev"); | |
|
44 | let file_args = | |
|
45 | match invocation.subcommand_args.get_many::<OsString>("files") { | |
|
46 | Some(files) => files | |
|
47 | .filter(|s| !s.is_empty()) | |
|
48 | .map(|s| s.as_os_str()) | |
|
49 | .collect(), | |
|
50 | None => vec![], | |
|
51 | }; | |
|
52 | ||
|
53 | 42 | let repo = invocation.repo?; |
|
54 | let cwd = hg::utils::current_dir()?; | |
|
55 | let working_directory = repo.working_directory_path(); | |
|
56 | let working_directory = cwd.join(working_directory); // Make it absolute | |
|
57 | ||
|
58 | let mut files = vec![]; | |
|
59 | for file in file_args { | |
|
60 | if file.as_bytes().starts_with(b"set:") { | |
|
61 | let message = "fileset"; | |
|
62 | return Err(CommandError::unsupported(message)); | |
|
63 | } | |
|
64 | 43 | |
|
65 | let normalized = cwd.join(file); | |
|
66 | // TODO: actually normalize `..` path segments etc? | |
|
67 | let dotted = normalized.components().any(|c| c.as_os_str() == ".."); | |
|
68 | if file.as_bytes() == b"." || dotted { | |
|
69 | let message = "`..` or `.` path segment"; | |
|
70 | return Err(CommandError::unsupported(message)); | |
|
71 | } | |
|
72 | let relative_path = working_directory | |
|
73 | .strip_prefix(&cwd) | |
|
74 | .unwrap_or(&working_directory); | |
|
75 | let stripped = normalized | |
|
76 | .strip_prefix(&working_directory) | |
|
77 | .map_err(|_| { | |
|
78 | CommandError::abort(format!( | |
|
79 | "abort: {} not under root '{}'\n(consider using '--cwd {}')", | |
|
80 | String::from_utf8_lossy(file.as_bytes()), | |
|
81 | working_directory.display(), | |
|
82 | relative_path.display(), | |
|
83 | )) | |
|
84 | })?; | |
|
85 | let hg_file = HgPathBuf::try_from(stripped.to_path_buf()) | |
|
86 | .map_err(|e| CommandError::abort(e.to_string()))?; | |
|
87 | files.push(hg_file); | |
|
88 | } | |
|
44 | let rev = invocation.subcommand_args.get_one::<String>("rev"); | |
|
45 | let files = match invocation.subcommand_args.get_many::<OsString>("files") | |
|
46 | { | |
|
47 | None => vec![], | |
|
48 | Some(files) => resolve_file_args(repo, files)?, | |
|
49 | }; | |
|
50 | ||
|
89 | 51 | let files = files.iter().map(|file| file.as_ref()).collect(); |
|
90 | 52 | // TODO probably move this to a util function like `repo.default_rev` or |
|
91 | 53 | // something when it's used somewhere else |
@@ -10,6 +10,9 use hg::utils::files::{get_bytes_from_pa | |||
|
10 | 10 | use hg::utils::hg_path::HgPath; |
|
11 | 11 | use hg::utils::hg_path::HgPathBuf; |
|
12 | 12 | use std::borrow::Cow; |
|
13 | use std::ffi::OsString; | |
|
14 | ||
|
15 | use crate::error::CommandError; | |
|
13 | 16 | |
|
14 | 17 | pub struct RelativizePaths { |
|
15 | 18 | repo_root: HgPathBuf, |
@@ -53,3 +56,41 impl RelativizePaths { | |||
|
53 | 56 | } |
|
54 | 57 | } |
|
55 | 58 | } |
|
59 | ||
|
60 | /// Resolves `FILE ...` arguments to a list of paths in the repository. | |
|
61 | pub fn resolve_file_args<'a>( | |
|
62 | repo: &Repo, | |
|
63 | file_args: impl Iterator<Item = &'a OsString>, | |
|
64 | ) -> Result<Vec<HgPathBuf>, CommandError> { | |
|
65 | let cwd = hg::utils::current_dir()?; | |
|
66 | let root = cwd.join(repo.working_directory_path()); | |
|
67 | let mut result = Vec::new(); | |
|
68 | for pattern in file_args { | |
|
69 | // TODO: Support all the formats in `hg help patterns`. | |
|
70 | if pattern.as_encoded_bytes().contains(&b':') { | |
|
71 | return Err(CommandError::unsupported( | |
|
72 | "rhg does not support file patterns", | |
|
73 | )); | |
|
74 | } | |
|
75 | // TODO: use hg::utils::files::canonical_path (currently doesn't work). | |
|
76 | let path = cwd.join(pattern); | |
|
77 | let dotted = path.components().any(|c| c.as_os_str() == ".."); | |
|
78 | if pattern.as_encoded_bytes() == b"." || dotted { | |
|
79 | let message = "`..` or `.` path segment"; | |
|
80 | return Err(CommandError::unsupported(message)); | |
|
81 | } | |
|
82 | let relative_path = root.strip_prefix(&cwd).unwrap_or(&root); | |
|
83 | let stripped = path.strip_prefix(&root).map_err(|_| { | |
|
84 | CommandError::abort(format!( | |
|
85 | "abort: {} not under root '{}'\n(consider using '--cwd {}')", | |
|
86 | String::from_utf8_lossy(pattern.as_encoded_bytes()), | |
|
87 | root.display(), | |
|
88 | relative_path.display(), | |
|
89 | )) | |
|
90 | })?; | |
|
91 | let hg_file = HgPathBuf::try_from(stripped.to_path_buf()) | |
|
92 | .map_err(|e| CommandError::abort(e.to_string()))?; | |
|
93 | result.push(hg_file); | |
|
94 | } | |
|
95 | Ok(result) | |
|
96 | } |
General Comments 0
You need to be logged in to leave comments.
Login now