##// END OF EJS Templates
rhg: `cat` command: print error messages for missing files...
Simon Sapin -
r47478:b1f2c2b3 default
parent child Browse files
Show More
@@ -17,30 +17,49 b' use crate::revlog::Node;'
17 use crate::utils::files::get_path_from_bytes;
17 use crate::utils::files::get_path_from_bytes;
18 use crate::utils::hg_path::{HgPath, HgPathBuf};
18 use crate::utils::hg_path::{HgPath, HgPathBuf};
19
19
20 pub struct CatOutput {
21 /// Whether any file in the manifest matched the paths given as CLI
22 /// arguments
23 pub found_any: bool,
24 /// The contents of matching files, in manifest order
25 pub concatenated: Vec<u8>,
26 /// Which of the CLI arguments did not match any manifest file
27 pub missing: Vec<HgPathBuf>,
28 /// The node ID that the given revset was resolved to
29 pub node: Node,
30 }
31
20 const METADATA_DELIMITER: [u8; 2] = [b'\x01', b'\n'];
32 const METADATA_DELIMITER: [u8; 2] = [b'\x01', b'\n'];
21
33
22 /// List files under Mercurial control at a given revision.
34 /// Output the given revision of files
23 ///
35 ///
24 /// * `root`: Repository root
36 /// * `root`: Repository root
25 /// * `rev`: The revision to cat the files from.
37 /// * `rev`: The revision to cat the files from.
26 /// * `files`: The files to output.
38 /// * `files`: The files to output.
27 pub fn cat(
39 pub fn cat<'a>(
28 repo: &Repo,
40 repo: &Repo,
29 revset: &str,
41 revset: &str,
30 files: &[HgPathBuf],
42 files: &'a [HgPathBuf],
31 ) -> Result<Vec<u8>, RevlogError> {
43 ) -> Result<CatOutput, RevlogError> {
32 let rev = crate::revset::resolve_single(revset, repo)?;
44 let rev = crate::revset::resolve_single(revset, repo)?;
33 let changelog = Changelog::open(repo)?;
45 let changelog = Changelog::open(repo)?;
34 let manifest = Manifest::open(repo)?;
46 let manifest = Manifest::open(repo)?;
35 let changelog_entry = changelog.get_rev(rev)?;
47 let changelog_entry = changelog.get_rev(rev)?;
48 let node = *changelog
49 .node_from_rev(rev)
50 .expect("should succeed when changelog.get_rev did");
36 let manifest_node =
51 let manifest_node =
37 Node::from_hex_for_repo(&changelog_entry.manifest_node()?)?;
52 Node::from_hex_for_repo(&changelog_entry.manifest_node()?)?;
38 let manifest_entry = manifest.get_node(manifest_node.into())?;
53 let manifest_entry = manifest.get_node(manifest_node.into())?;
39 let mut bytes = vec![];
54 let mut bytes = vec![];
55 let mut matched = vec![false; files.len()];
56 let mut found_any = false;
40
57
41 for (manifest_file, node_bytes) in manifest_entry.files_with_nodes() {
58 for (manifest_file, node_bytes) in manifest_entry.files_with_nodes() {
42 for cat_file in files.iter() {
59 for (cat_file, is_matched) in files.iter().zip(&mut matched) {
43 if cat_file.as_bytes() == manifest_file.as_bytes() {
60 if cat_file.as_bytes() == manifest_file.as_bytes() {
61 *is_matched = true;
62 found_any = true;
44 let index_path = store_path(manifest_file, b".i");
63 let index_path = store_path(manifest_file, b".i");
45 let data_path = store_path(manifest_file, b".d");
64 let data_path = store_path(manifest_file, b".d");
46
65
@@ -65,7 +84,18 b' pub fn cat('
65 }
84 }
66 }
85 }
67
86
68 Ok(bytes)
87 let missing: Vec<_> = files
88 .iter()
89 .zip(&matched)
90 .filter(|pair| !*pair.1)
91 .map(|pair| pair.0.clone())
92 .collect();
93 Ok(CatOutput {
94 found_any,
95 concatenated: bytes,
96 missing,
97 node,
98 })
69 }
99 }
70
100
71 fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf {
101 fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf {
@@ -6,7 +6,7 b' mod cat;'
6 mod debugdata;
6 mod debugdata;
7 mod dirstate_status;
7 mod dirstate_status;
8 mod list_tracked_files;
8 mod list_tracked_files;
9 pub use cat::cat;
9 pub use cat::{cat, CatOutput};
10 pub use debugdata::{debug_data, DebugDataKind};
10 pub use debugdata::{debug_data, DebugDataKind};
11 pub use list_tracked_files::Dirstate;
11 pub use list_tracked_files::Dirstate;
12 pub use list_tracked_files::{list_rev_tracked_files, FilesForRev};
12 pub use list_tracked_files::{list_rev_tracked_files, FilesForRev};
@@ -1,8 +1,8 b''
1 use crate::errors::HgError;
1 use crate::errors::HgError;
2 use crate::repo::Repo;
2 use crate::repo::Repo;
3 use crate::revlog::revlog::{Revlog, RevlogError};
3 use crate::revlog::revlog::{Revlog, RevlogError};
4 use crate::revlog::NodePrefix;
5 use crate::revlog::Revision;
4 use crate::revlog::Revision;
5 use crate::revlog::{Node, NodePrefix};
6
6
7 /// A specialized `Revlog` to work with `changelog` data format.
7 /// A specialized `Revlog` to work with `changelog` data format.
8 pub struct Changelog {
8 pub struct Changelog {
@@ -34,6 +34,10 b' impl Changelog {'
34 let bytes = self.revlog.get_rev_data(rev)?;
34 let bytes = self.revlog.get_rev_data(rev)?;
35 Ok(ChangelogEntry { bytes })
35 Ok(ChangelogEntry { bytes })
36 }
36 }
37
38 pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> {
39 Some(self.revlog.index.get_entry(rev)?.hash())
40 }
37 }
41 }
38
42
39 /// `Changelog` entry which knows how to interpret the `changelog` data bytes.
43 /// `Changelog` entry which knows how to interpret the `changelog` data bytes.
@@ -31,6 +31,9 b' pub const NULL_NODE_ID: [u8; NODE_BYTES_'
31 /// see also `NODES_BYTES_LENGTH` about it being private.
31 /// see also `NODES_BYTES_LENGTH` about it being private.
32 const NODE_NYBBLES_LENGTH: usize = 2 * NODE_BYTES_LENGTH;
32 const NODE_NYBBLES_LENGTH: usize = 2 * NODE_BYTES_LENGTH;
33
33
34 /// Default for UI presentation
35 const SHORT_PREFIX_DEFAULT_NYBBLES_LENGTH: u8 = 12;
36
34 /// Private alias for readability and to ease future change
37 /// Private alias for readability and to ease future change
35 type NodeData = [u8; NODE_BYTES_LENGTH];
38 type NodeData = [u8; NODE_BYTES_LENGTH];
36
39
@@ -164,6 +167,13 b' impl Node {'
164 pub fn as_bytes(&self) -> &[u8] {
167 pub fn as_bytes(&self) -> &[u8] {
165 &self.data
168 &self.data
166 }
169 }
170
171 pub fn short(&self) -> NodePrefix {
172 NodePrefix {
173 nybbles_len: SHORT_PREFIX_DEFAULT_NYBBLES_LENGTH,
174 data: self.data,
175 }
176 }
167 }
177 }
168
178
169 /// The beginning of a binary revision SHA.
179 /// The beginning of a binary revision SHA.
@@ -49,7 +49,7 b' pub struct Revlog {'
49 /// When index and data are not interleaved: bytes of the revlog index.
49 /// When index and data are not interleaved: bytes of the revlog index.
50 /// When index and data are interleaved: bytes of the revlog index and
50 /// When index and data are interleaved: bytes of the revlog index and
51 /// data.
51 /// data.
52 index: Index,
52 pub(crate) index: Index,
53 /// When index and data are not interleaved: bytes of the revlog data
53 /// When index and data are not interleaved: bytes of the revlog data
54 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
54 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
55 /// When present on disk: the persistent nodemap for this revlog
55 /// When present on disk: the persistent nodemap for this revlog
@@ -1,5 +1,6 b''
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 hg::operations::cat;
4 use hg::operations::cat;
4 use hg::utils::hg_path::HgPathBuf;
5 use hg::utils::hg_path::HgPathBuf;
5 use micro_timer::timed;
6 use micro_timer::timed;
@@ -58,9 +59,23 b' pub fn run(invocation: &crate::CliInvoca'
58
59
59 match rev {
60 match rev {
60 Some(rev) => {
61 Some(rev) => {
61 let data = cat(&repo, rev, &files).map_err(|e| (e, rev))?;
62 let output = cat(&repo, rev, &files).map_err(|e| (e, rev))?;
62 invocation.ui.write_stdout(&data)?;
63 invocation.ui.write_stdout(&output.concatenated)?;
63 Ok(())
64 if !output.missing.is_empty() {
65 let short = format!("{:x}", output.node.short()).into_bytes();
66 for path in &output.missing {
67 invocation.ui.write_stderr(&format_bytes!(
68 b"{}: no such file in rev {}\n",
69 path.as_bytes(),
70 short
71 ))?;
72 }
73 }
74 if output.found_any {
75 Ok(())
76 } else {
77 Err(CommandError::Unsuccessful)
78 }
64 }
79 }
65 None => Err(CommandError::unsupported(
80 None => Err(CommandError::unsupported(
66 "`rhg cat` without `--rev` / `-r`",
81 "`rhg cat` without `--rev` / `-r`",
@@ -15,6 +15,9 b' pub enum CommandError {'
15 /// Exit with an error message and "standard" failure exit code.
15 /// Exit with an error message and "standard" failure exit code.
16 Abort { message: Vec<u8> },
16 Abort { message: Vec<u8> },
17
17
18 /// Exit with a failure exit code but no message.
19 Unsuccessful,
20
18 /// Encountered something (such as a CLI argument, repository layout, …)
21 /// Encountered something (such as a CLI argument, repository layout, …)
19 /// not supported by this version of `rhg`. Depending on configuration
22 /// not supported by this version of `rhg`. Depending on configuration
20 /// `rhg` may attempt to silently fall back to Python-based `hg`, which
23 /// `rhg` may attempt to silently fall back to Python-based `hg`, which
@@ -6,5 +6,8 b' pub const OK: ExitCode = 0;'
6 /// Generic abort
6 /// Generic abort
7 pub const ABORT: ExitCode = 255;
7 pub const ABORT: ExitCode = 255;
8
8
9 /// Generic something completed but did not succeed
10 pub const UNSUCCESSFUL: ExitCode = 1;
11
9 /// Command or feature not implemented by rhg
12 /// Command or feature not implemented by rhg
10 pub const UNIMPLEMENTED: ExitCode = 252;
13 pub const UNIMPLEMENTED: ExitCode = 252;
@@ -186,6 +186,7 b' fn exit_code(result: &Result<(), Command'
186 match result {
186 match result {
187 Ok(()) => exitcode::OK,
187 Ok(()) => exitcode::OK,
188 Err(CommandError::Abort { .. }) => exitcode::ABORT,
188 Err(CommandError::Abort { .. }) => exitcode::ABORT,
189 Err(CommandError::Unsuccessful) => exitcode::UNSUCCESSFUL,
189
190
190 // Exit with a specific code and no error message to let a potential
191 // Exit with a specific code and no error message to let a potential
191 // wrapper script fallback to Python-based Mercurial.
192 // wrapper script fallback to Python-based Mercurial.
@@ -242,6 +243,7 b' fn exit('
242 }
243 }
243 match &result {
244 match &result {
244 Ok(_) => {}
245 Ok(_) => {}
246 Err(CommandError::Unsuccessful) => {}
245 Err(CommandError::Abort { message }) => {
247 Err(CommandError::Abort { message }) => {
246 if !message.is_empty() {
248 if !message.is_empty() {
247 // Ignore errors when writing to stderr, we’re already exiting
249 // Ignore errors when writing to stderr, we’re already exiting
General Comments 0
You need to be logged in to leave comments. Login now