Show More
@@ -0,0 +1,79 b'' | |||
|
1 | use crate::errors::HgError; | |
|
2 | use crate::repo::Repo; | |
|
3 | use crate::revlog::path_encode::path_encode; | |
|
4 | use crate::revlog::revlog::{Revlog, RevlogError}; | |
|
5 | use crate::revlog::NodePrefix; | |
|
6 | use crate::revlog::Revision; | |
|
7 | use crate::utils::files::get_path_from_bytes; | |
|
8 | use crate::utils::hg_path::HgPath; | |
|
9 | use crate::utils::SliceExt; | |
|
10 | use std::borrow::Cow; | |
|
11 | use std::path::PathBuf; | |
|
12 | ||
|
13 | /// A specialized `Revlog` to work with file data logs. | |
|
14 | pub struct Filelog { | |
|
15 | /// The generic `revlog` format. | |
|
16 | revlog: Revlog, | |
|
17 | } | |
|
18 | ||
|
19 | impl Filelog { | |
|
20 | pub fn open(repo: &Repo, file_path: &HgPath) -> Result<Self, RevlogError> { | |
|
21 | let index_path = store_path(file_path, b".i"); | |
|
22 | let data_path = store_path(file_path, b".d"); | |
|
23 | let revlog = Revlog::open(repo, index_path, Some(&data_path))?; | |
|
24 | Ok(Self { revlog }) | |
|
25 | } | |
|
26 | ||
|
27 | /// The given node ID is that of the file as found in a manifest, not of a | |
|
28 | /// changeset. | |
|
29 | pub fn get_node( | |
|
30 | &self, | |
|
31 | file_node: impl Into<NodePrefix>, | |
|
32 | ) -> Result<FilelogEntry, RevlogError> { | |
|
33 | let file_rev = self.revlog.get_node_rev(file_node.into())?; | |
|
34 | self.get_rev(file_rev) | |
|
35 | } | |
|
36 | ||
|
37 | /// The given revision is that of the file as found in a manifest, not of a | |
|
38 | /// changeset. | |
|
39 | pub fn get_rev( | |
|
40 | &self, | |
|
41 | file_rev: Revision, | |
|
42 | ) -> Result<FilelogEntry, RevlogError> { | |
|
43 | let data = self.revlog.get_rev_data(file_rev)?; | |
|
44 | Ok(FilelogEntry(data.into())) | |
|
45 | } | |
|
46 | } | |
|
47 | ||
|
48 | fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf { | |
|
49 | let encoded_bytes = | |
|
50 | path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat()); | |
|
51 | get_path_from_bytes(&encoded_bytes).into() | |
|
52 | } | |
|
53 | ||
|
54 | pub struct FilelogEntry<'filelog>(Cow<'filelog, [u8]>); | |
|
55 | ||
|
56 | impl<'filelog> FilelogEntry<'filelog> { | |
|
57 | /// Split into metadata and data | |
|
58 | pub fn split(&self) -> Result<(Option<&[u8]>, &[u8]), HgError> { | |
|
59 | const DELIMITER: &[u8; 2] = &[b'\x01', b'\n']; | |
|
60 | ||
|
61 | if let Some(rest) = self.0.drop_prefix(DELIMITER) { | |
|
62 | if let Some((metadata, data)) = rest.split_2_by_slice(DELIMITER) { | |
|
63 | Ok((Some(metadata), data)) | |
|
64 | } else { | |
|
65 | Err(HgError::corrupted( | |
|
66 | "Missing metadata end delimiter in filelog entry", | |
|
67 | )) | |
|
68 | } | |
|
69 | } else { | |
|
70 | Ok((None, &self.0)) | |
|
71 | } | |
|
72 | } | |
|
73 | ||
|
74 | /// Returns the file contents at this revision, stripped of any metadata | |
|
75 | pub fn data(&self) -> Result<&[u8], HgError> { | |
|
76 | let (_metadata, data) = self.split()?; | |
|
77 | Ok(data) | |
|
78 | } | |
|
79 | } |
@@ -5,15 +5,11 b'' | |||
|
5 | 5 | // This software may be used and distributed according to the terms of the |
|
6 | 6 | // GNU General Public License version 2 or any later version. |
|
7 | 7 | |
|
8 | use std::path::PathBuf; | |
|
9 | ||
|
10 | 8 | use crate::repo::Repo; |
|
11 | use crate::revlog::path_encode::path_encode; | |
|
12 | use crate::revlog::revlog::Revlog; | |
|
13 | 9 | use crate::revlog::revlog::RevlogError; |
|
14 | 10 | use crate::revlog::Node; |
|
15 | use crate::utils::files::get_path_from_bytes; | |
|
16 |
use crate::utils::hg_path:: |
|
|
11 | ||
|
12 | use crate::utils::hg_path::HgPathBuf; | |
|
17 | 13 | |
|
18 | 14 | pub struct CatOutput { |
|
19 | 15 | /// Whether any file in the manifest matched the paths given as CLI |
@@ -27,8 +23,6 b' pub struct CatOutput {' | |||
|
27 | 23 | pub node: Node, |
|
28 | 24 | } |
|
29 | 25 | |
|
30 | const METADATA_DELIMITER: [u8; 2] = [b'\x01', b'\n']; | |
|
31 | ||
|
32 | 26 | /// Output the given revision of files |
|
33 | 27 | /// |
|
34 | 28 | /// * `root`: Repository root |
@@ -54,26 +48,10 b" pub fn cat<'a>(" | |||
|
54 | 48 | if cat_file.as_bytes() == manifest_file.as_bytes() { |
|
55 | 49 | *is_matched = true; |
|
56 | 50 | found_any = true; |
|
57 |
let |
|
|
58 | let data_path = store_path(manifest_file, b".d"); | |
|
59 | ||
|
60 | let file_log = | |
|
61 | Revlog::open(repo, &index_path, Some(&data_path))?; | |
|
51 | let file_log = repo.filelog(manifest_file)?; | |
|
62 | 52 | let file_node = Node::from_hex_for_repo(node_bytes)?; |
|
63 |
let |
|
|
64 | let data = file_log.get_rev_data(file_rev)?; | |
|
65 | if data.starts_with(&METADATA_DELIMITER) { | |
|
66 | let end_delimiter_position = data | |
|
67 | [METADATA_DELIMITER.len()..] | |
|
68 | .windows(METADATA_DELIMITER.len()) | |
|
69 | .position(|bytes| bytes == METADATA_DELIMITER); | |
|
70 | if let Some(position) = end_delimiter_position { | |
|
71 | let offset = METADATA_DELIMITER.len() * 2; | |
|
72 | bytes.extend(data[position + offset..].iter()); | |
|
73 | } | |
|
74 | } else { | |
|
75 | bytes.extend(data); | |
|
76 | } | |
|
53 | let entry = file_log.get_node(file_node)?; | |
|
54 | bytes.extend(entry.data()?) | |
|
77 | 55 | } |
|
78 | 56 | } |
|
79 | 57 | } |
@@ -91,9 +69,3 b" pub fn cat<'a>(" | |||
|
91 | 69 | node, |
|
92 | 70 | }) |
|
93 | 71 | } |
|
94 | ||
|
95 | fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf { | |
|
96 | let encoded_bytes = | |
|
97 | path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat()); | |
|
98 | get_path_from_bytes(&encoded_bytes).into() | |
|
99 | } |
@@ -7,8 +7,10 b' use crate::errors::HgError;' | |||
|
7 | 7 | use crate::errors::HgResultExt; |
|
8 | 8 | use crate::manifest::{Manifest, Manifestlog}; |
|
9 | 9 | use crate::requirements; |
|
10 | use crate::revlog::filelog::Filelog; | |
|
10 | 11 | use crate::revlog::revlog::RevlogError; |
|
11 | 12 | use crate::utils::files::get_path_from_bytes; |
|
13 | use crate::utils::hg_path::HgPath; | |
|
12 | 14 | use crate::utils::SliceExt; |
|
13 | 15 | use crate::vfs::{is_dir, is_file, Vfs}; |
|
14 | 16 | use crate::{exit_codes, Node}; |
@@ -346,6 +348,10 b' impl Repo {' | |||
|
346 | 348 | Node::from_hex_for_repo(&changelog_entry.manifest_node()?)?; |
|
347 | 349 | manifest.get_node(manifest_node.into()) |
|
348 | 350 | } |
|
351 | ||
|
352 | pub fn filelog(&self, path: &HgPath) -> Result<Filelog, RevlogError> { | |
|
353 | Filelog::open(self, path) | |
|
354 | } | |
|
349 | 355 | } |
|
350 | 356 | |
|
351 | 357 | /// Lazily-initialized component of `Repo` with interior mutability |
@@ -11,6 +11,7 b' mod nodemap_docket;' | |||
|
11 | 11 | pub mod path_encode; |
|
12 | 12 | pub use node::{FromHexError, Node, NodePrefix}; |
|
13 | 13 | pub mod changelog; |
|
14 | pub mod filelog; | |
|
14 | 15 | pub mod index; |
|
15 | 16 | pub mod manifest; |
|
16 | 17 | pub mod patch; |
@@ -74,6 +74,7 b' pub trait SliceExt {' | |||
|
74 | 74 | fn trim(&self) -> &Self; |
|
75 | 75 | fn drop_prefix(&self, needle: &Self) -> Option<&Self>; |
|
76 | 76 | fn split_2(&self, separator: u8) -> Option<(&[u8], &[u8])>; |
|
77 | fn split_2_by_slice(&self, separator: &[u8]) -> Option<(&[u8], &[u8])>; | |
|
77 | 78 | } |
|
78 | 79 | |
|
79 | 80 | impl SliceExt for [u8] { |
@@ -134,6 +135,14 b' impl SliceExt for [u8] {' | |||
|
134 | 135 | let b = iter.next()?; |
|
135 | 136 | Some((a, b)) |
|
136 | 137 | } |
|
138 | ||
|
139 | fn split_2_by_slice(&self, separator: &[u8]) -> Option<(&[u8], &[u8])> { | |
|
140 | if let Some(pos) = find_slice_in_slice(self, separator) { | |
|
141 | Some((&self[..pos], &self[pos + separator.len()..])) | |
|
142 | } else { | |
|
143 | None | |
|
144 | } | |
|
145 | } | |
|
137 | 146 | } |
|
138 | 147 | |
|
139 | 148 | pub trait Escaped { |
General Comments 0
You need to be logged in to leave comments.
Login now