Show More
@@ -10,20 +10,19 b' use crate::revlog::revlog::RevlogError;' | |||||
10 | use crate::revlog::Node; |
|
10 | use crate::revlog::Node; | |
11 |
|
11 | |||
12 | use crate::utils::hg_path::HgPath; |
|
12 | use crate::utils::hg_path::HgPath; | |
13 | use crate::utils::hg_path::HgPathBuf; |
|
|||
14 |
|
13 | |||
15 | use itertools::put_back; |
|
14 | use itertools::put_back; | |
16 | use itertools::PutBack; |
|
15 | use itertools::PutBack; | |
17 | use std::cmp::Ordering; |
|
16 | use std::cmp::Ordering; | |
18 |
|
17 | |||
19 | pub struct CatOutput { |
|
18 | pub struct CatOutput<'a> { | |
20 | /// Whether any file in the manifest matched the paths given as CLI |
|
19 | /// Whether any file in the manifest matched the paths given as CLI | |
21 | /// arguments |
|
20 | /// arguments | |
22 | pub found_any: bool, |
|
21 | pub found_any: bool, | |
23 | /// The contents of matching files, in manifest order |
|
22 | /// The contents of matching files, in manifest order | |
24 | pub concatenated: Vec<u8>, |
|
23 | pub results: Vec<(&'a HgPath, Vec<u8>)>, | |
25 | /// Which of the CLI arguments did not match any manifest file |
|
24 | /// Which of the CLI arguments did not match any manifest file | |
26 |
pub missing: Vec<HgPath |
|
25 | pub missing: Vec<&'a HgPath>, | |
27 | /// The node ID that the given revset was resolved to |
|
26 | /// The node ID that the given revset was resolved to | |
28 | pub node: Node, |
|
27 | pub node: Node, | |
29 | } |
|
28 | } | |
@@ -32,7 +31,7 b' pub struct CatOutput {' | |||||
32 | fn find_item<'a, 'b, 'c, D, I: Iterator<Item = (&'a HgPath, D)>>( |
|
31 | fn find_item<'a, 'b, 'c, D, I: Iterator<Item = (&'a HgPath, D)>>( | |
33 | i: &mut PutBack<I>, |
|
32 | i: &mut PutBack<I>, | |
34 | needle: &'b HgPath, |
|
33 | needle: &'b HgPath, | |
35 |
) -> Option< |
|
34 | ) -> Option<D> { | |
36 | loop { |
|
35 | loop { | |
37 | match i.next() { |
|
36 | match i.next() { | |
38 | None => return None, |
|
37 | None => return None, | |
@@ -42,30 +41,30 b" fn find_item<'a, 'b, 'c, D, I: Iterator<" | |||||
42 | return None; |
|
41 | return None; | |
43 | } |
|
42 | } | |
44 | Ordering::Greater => continue, |
|
43 | Ordering::Greater => continue, | |
45 | Ordering::Equal => return Some(val), |
|
44 | Ordering::Equal => return Some(val.1), | |
46 | }, |
|
45 | }, | |
47 | } |
|
46 | } | |
48 | } |
|
47 | } | |
49 | } |
|
48 | } | |
50 |
|
49 | |||
51 | fn find_files_in_manifest< |
|
50 | fn find_files_in_manifest< | |
52 | 'a, |
|
51 | 'manifest, | |
53 |
' |
|
52 | 'query, | |
54 | D, |
|
53 | Data, | |
55 |
|
|
54 | Manifest: Iterator<Item = (&'manifest HgPath, Data)>, | |
56 |
|
|
55 | Query: Iterator<Item = &'query HgPath>, | |
57 | >( |
|
56 | >( | |
58 |
manifest: |
|
57 | manifest: Manifest, | |
59 | files: J, |
|
58 | query: Query, | |
60 |
) -> (Vec<(&' |
|
59 | ) -> (Vec<(&'query HgPath, Data)>, Vec<&'query HgPath>) { | |
61 | let mut manifest = put_back(manifest); |
|
60 | let mut manifest = put_back(manifest); | |
62 | let mut res = vec![]; |
|
61 | let mut res = vec![]; | |
63 | let mut missing = vec![]; |
|
62 | let mut missing = vec![]; | |
64 |
|
63 | |||
65 |
for file in |
|
64 | for file in query { | |
66 | match find_item(&mut manifest, file) { |
|
65 | match find_item(&mut manifest, file) { | |
67 | None => missing.push(file), |
|
66 | None => missing.push(file), | |
68 | Some(item) => res.push(item), |
|
67 | Some(item) => res.push((file, item)), | |
69 | } |
|
68 | } | |
70 | } |
|
69 | } | |
71 | return (res, missing); |
|
70 | return (res, missing); | |
@@ -79,36 +78,37 b' fn find_files_in_manifest<' | |||||
79 | pub fn cat<'a>( |
|
78 | pub fn cat<'a>( | |
80 | repo: &Repo, |
|
79 | repo: &Repo, | |
81 | revset: &str, |
|
80 | revset: &str, | |
82 |
mut files: Vec<HgPath |
|
81 | mut files: Vec<&'a HgPath>, | |
83 | ) -> Result<CatOutput, RevlogError> { |
|
82 | ) -> Result<CatOutput<'a>, RevlogError> { | |
84 | let rev = crate::revset::resolve_single(revset, repo)?; |
|
83 | let rev = crate::revset::resolve_single(revset, repo)?; | |
85 | let manifest = repo.manifest_for_rev(rev)?; |
|
84 | let manifest = repo.manifest_for_rev(rev)?; | |
86 | let node = *repo |
|
85 | let node = *repo | |
87 | .changelog()? |
|
86 | .changelog()? | |
88 | .node_from_rev(rev) |
|
87 | .node_from_rev(rev) | |
89 | .expect("should succeed when repo.manifest did"); |
|
88 | .expect("should succeed when repo.manifest did"); | |
90 |
let mut |
|
89 | let mut results: Vec<(&'a HgPath, Vec<u8>)> = vec![]; | |
91 | let mut found_any = false; |
|
90 | let mut found_any = false; | |
92 |
|
91 | |||
93 | files.sort_unstable(); |
|
92 | files.sort_unstable(); | |
94 |
|
93 | |||
95 | let (found, missing) = find_files_in_manifest( |
|
94 | let (found, missing) = find_files_in_manifest( | |
96 | manifest.files_with_nodes(), |
|
95 | manifest.files_with_nodes(), | |
97 | files.iter().map(|f| f.as_ref()), |
|
96 | files.into_iter().map(|f| f.as_ref()), | |
98 | ); |
|
97 | ); | |
99 |
|
98 | |||
100 |
for ( |
|
99 | for (file_path, node_bytes) in found { | |
101 | found_any = true; |
|
100 | found_any = true; | |
102 |
let file_log = repo.filelog( |
|
101 | let file_log = repo.filelog(file_path)?; | |
103 | let file_node = Node::from_hex_for_repo(node_bytes)?; |
|
102 | let file_node = Node::from_hex_for_repo(node_bytes)?; | |
104 | bytes.extend(file_log.data_for_node(file_node)?.data()?); |
|
103 | results.push(( | |
|
104 | file_path, | |||
|
105 | file_log.data_for_node(file_node)?.into_data()?, | |||
|
106 | )); | |||
105 | } |
|
107 | } | |
106 |
|
108 | |||
107 | let missing: Vec<HgPathBuf> = |
|
|||
108 | missing.iter().map(|file| (*file).to_owned()).collect(); |
|
|||
109 | Ok(CatOutput { |
|
109 | Ok(CatOutput { | |
110 | found_any, |
|
110 | found_any, | |
111 | concatenated: bytes, |
|
111 | results, | |
112 | missing, |
|
112 | missing, | |
113 | node, |
|
113 | node, | |
114 | }) |
|
114 | }) |
@@ -7,7 +7,6 b' use crate::revlog::Revision;' | |||||
7 | use crate::utils::files::get_path_from_bytes; |
|
7 | use crate::utils::files::get_path_from_bytes; | |
8 | use crate::utils::hg_path::HgPath; |
|
8 | use crate::utils::hg_path::HgPath; | |
9 | use crate::utils::SliceExt; |
|
9 | use crate::utils::SliceExt; | |
10 | use std::borrow::Cow; |
|
|||
11 | use std::path::PathBuf; |
|
10 | use std::path::PathBuf; | |
12 |
|
11 | |||
13 | /// A specialized `Revlog` to work with file data logs. |
|
12 | /// A specialized `Revlog` to work with file data logs. | |
@@ -40,7 +39,7 b' impl Filelog {' | |||||
40 | &self, |
|
39 | &self, | |
41 | file_rev: Revision, |
|
40 | file_rev: Revision, | |
42 | ) -> Result<FilelogEntry, RevlogError> { |
|
41 | ) -> Result<FilelogEntry, RevlogError> { | |
43 | let data = self.revlog.get_rev_data(file_rev)?; |
|
42 | let data: Vec<u8> = self.revlog.get_rev_data(file_rev)?; | |
44 | Ok(FilelogEntry(data.into())) |
|
43 | Ok(FilelogEntry(data.into())) | |
45 | } |
|
44 | } | |
46 | } |
|
45 | } | |
@@ -51,22 +50,32 b' fn store_path(hg_path: &HgPath, suffix: ' | |||||
51 | get_path_from_bytes(&encoded_bytes).into() |
|
50 | get_path_from_bytes(&encoded_bytes).into() | |
52 | } |
|
51 | } | |
53 |
|
52 | |||
54 |
pub struct FilelogEntry< |
|
53 | pub struct FilelogEntry(Vec<u8>); | |
55 |
|
54 | |||
56 |
impl |
|
55 | impl FilelogEntry { | |
57 | /// Split into metadata and data |
|
56 | /// Split into metadata and data | |
58 | pub fn split(&self) -> Result<(Option<&[u8]>, &[u8]), HgError> { |
|
57 | /// Returns None if there is no metadata, so the entire entry is data. | |
|
58 | fn split_metadata(&self) -> Result<Option<(&[u8], &[u8])>, HgError> { | |||
59 | const DELIMITER: &[u8; 2] = &[b'\x01', b'\n']; |
|
59 | const DELIMITER: &[u8; 2] = &[b'\x01', b'\n']; | |
60 |
|
60 | |||
61 | if let Some(rest) = self.0.drop_prefix(DELIMITER) { |
|
61 | if let Some(rest) = self.0.drop_prefix(DELIMITER) { | |
62 | if let Some((metadata, data)) = rest.split_2_by_slice(DELIMITER) { |
|
62 | if let Some((metadata, data)) = rest.split_2_by_slice(DELIMITER) { | |
63 |
Ok( |
|
63 | Ok(Some((metadata, data))) | |
64 | } else { |
|
64 | } else { | |
65 | Err(HgError::corrupted( |
|
65 | Err(HgError::corrupted( | |
66 | "Missing metadata end delimiter in filelog entry", |
|
66 | "Missing metadata end delimiter in filelog entry", | |
67 | )) |
|
67 | )) | |
68 | } |
|
68 | } | |
69 | } else { |
|
69 | } else { | |
|
70 | Ok(None) | |||
|
71 | } | |||
|
72 | } | |||
|
73 | ||||
|
74 | /// Split into metadata and data | |||
|
75 | pub fn split(&self) -> Result<(Option<&[u8]>, &[u8]), HgError> { | |||
|
76 | if let Some((metadata, data)) = self.split_metadata()? { | |||
|
77 | Ok((Some(metadata), data)) | |||
|
78 | } else { | |||
70 | Ok((None, &self.0)) |
|
79 | Ok((None, &self.0)) | |
71 | } |
|
80 | } | |
72 | } |
|
81 | } | |
@@ -76,4 +85,14 b" impl<'filelog> FilelogEntry<'filelog> {" | |||||
76 | let (_metadata, data) = self.split()?; |
|
85 | let (_metadata, data) = self.split()?; | |
77 | Ok(data) |
|
86 | Ok(data) | |
78 | } |
|
87 | } | |
|
88 | ||||
|
89 | /// Consume the entry, and convert it into data, discarding any metadata, | |||
|
90 | /// if present. | |||
|
91 | pub fn into_data(self) -> Result<Vec<u8>, HgError> { | |||
|
92 | if let Some((_metadata, data)) = self.split_metadata()? { | |||
|
93 | Ok(data.to_owned()) | |||
|
94 | } else { | |||
|
95 | Ok(self.0) | |||
|
96 | } | |||
|
97 | } | |||
79 | } |
|
98 | } |
@@ -66,6 +66,7 b' pub fn run(invocation: &crate::CliInvoca' | |||||
66 | .map_err(|e| CommandError::abort(e.to_string()))?; |
|
66 | .map_err(|e| CommandError::abort(e.to_string()))?; | |
67 | files.push(hg_file); |
|
67 | files.push(hg_file); | |
68 | } |
|
68 | } | |
|
69 | let files = files.iter().map(|file| file.as_ref()).collect(); | |||
69 | // TODO probably move this to a util function like `repo.default_rev` or |
|
70 | // TODO probably move this to a util function like `repo.default_rev` or | |
70 | // something when it's used somewhere else |
|
71 | // something when it's used somewhere else | |
71 | let rev = match rev { |
|
72 | let rev = match rev { | |
@@ -74,7 +75,9 b' pub fn run(invocation: &crate::CliInvoca' | |||||
74 | }; |
|
75 | }; | |
75 |
|
76 | |||
76 | let output = cat(&repo, &rev, files).map_err(|e| (e, rev.as_str()))?; |
|
77 | let output = cat(&repo, &rev, files).map_err(|e| (e, rev.as_str()))?; | |
77 | invocation.ui.write_stdout(&output.concatenated)?; |
|
78 | for (_file, contents) in output.results { | |
|
79 | invocation.ui.write_stdout(&contents)?; | |||
|
80 | } | |||
78 | if !output.missing.is_empty() { |
|
81 | if !output.missing.is_empty() { | |
79 | let short = format!("{:x}", output.node.short()).into_bytes(); |
|
82 | let short = format!("{:x}", output.node.short()).into_bytes(); | |
80 | for path in &output.missing { |
|
83 | for path in &output.missing { |
General Comments 0
You need to be logged in to leave comments.
Login now