Show More
@@ -13,6 +13,8 b' use crate::revlog::manifest::{Manifest, ' | |||
|
13 | 13 | use crate::revlog::path_encode::path_encode; |
|
14 | 14 | use crate::revlog::revlog::Revlog; |
|
15 | 15 | use crate::revlog::revlog::RevlogError; |
|
16 | use crate::revlog::Node; | |
|
17 | use crate::revlog::NodePrefix; | |
|
16 | 18 | use crate::revlog::Revision; |
|
17 | 19 | use crate::utils::files::get_path_from_bytes; |
|
18 | 20 | use crate::utils::hg_path::{HgPath, HgPathBuf}; |
@@ -108,15 +110,16 b" impl<'a> CatRev<'a> {" | |||
|
108 | 110 | let changelog_entry = match self.rev.parse::<Revision>() { |
|
109 | 111 | Ok(rev) => self.changelog.get_rev(rev)?, |
|
110 | 112 | _ => { |
|
111 |
let changelog_node = |
|
|
113 | let changelog_node = NodePrefix::from_hex(&self.rev) | |
|
112 | 114 | .map_err(|_| CatRevErrorKind::InvalidRevision)?; |
|
113 |
self.changelog.get_node( |
|
|
115 | self.changelog.get_node(changelog_node.borrow())? | |
|
114 | 116 | } |
|
115 | 117 | }; |
|
116 |
let manifest_node = |
|
|
118 | let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?) | |
|
117 | 119 | .map_err(|_| CatRevErrorKind::CorruptedRevlog)?; |
|
118 | 120 | |
|
119 | self.manifest_entry = Some(self.manifest.get_node(&manifest_node)?); | |
|
121 | self.manifest_entry = | |
|
122 | Some(self.manifest.get_node((&manifest_node).into())?); | |
|
120 | 123 | if let Some(ref manifest_entry) = self.manifest_entry { |
|
121 | 124 | let mut bytes = vec![]; |
|
122 | 125 | |
@@ -132,9 +135,10 b" impl<'a> CatRev<'a> {" | |||
|
132 | 135 | |
|
133 | 136 | let file_log = |
|
134 | 137 | Revlog::open(&index_path, Some(&data_path))?; |
|
135 |
let file_node = |
|
|
138 | let file_node = Node::from_hex(node_bytes) | |
|
136 | 139 | .map_err(|_| CatRevErrorKind::CorruptedRevlog)?; |
|
137 |
let file_rev = |
|
|
140 | let file_rev = | |
|
141 | file_log.get_node_rev((&file_node).into())?; | |
|
138 | 142 | let data = file_log.get_rev_data(file_rev)?; |
|
139 | 143 | if data.starts_with(&METADATA_DELIMITER) { |
|
140 | 144 | let end_delimiter_position = data |
@@ -7,6 +7,7 b'' | |||
|
7 | 7 | |
|
8 | 8 | use super::find_root; |
|
9 | 9 | use crate::revlog::revlog::{Revlog, RevlogError}; |
|
10 | use crate::revlog::NodePrefix; | |
|
10 | 11 | use crate::revlog::Revision; |
|
11 | 12 | |
|
12 | 13 | /// Kind of data to debug |
@@ -107,9 +108,9 b" impl<'a> DebugData<'a> {" | |||
|
107 | 108 | let data = match self.rev.parse::<Revision>() { |
|
108 | 109 | Ok(rev) => revlog.get_rev_data(rev)?, |
|
109 | 110 | _ => { |
|
110 |
let node = |
|
|
111 | let node = NodePrefix::from_hex(&self.rev) | |
|
111 | 112 | .map_err(|_| DebugDataErrorKind::InvalidRevision)?; |
|
112 |
let rev = revlog.get_node_rev( |
|
|
113 | let rev = revlog.get_node_rev(node.borrow())?; | |
|
113 | 114 | revlog.get_rev_data(rev)? |
|
114 | 115 | } |
|
115 | 116 | }; |
@@ -8,6 +8,7 b'' | |||
|
8 | 8 | use crate::dirstate::parsers::parse_dirstate; |
|
9 | 9 | use crate::revlog::changelog::Changelog; |
|
10 | 10 | use crate::revlog::manifest::{Manifest, ManifestEntry}; |
|
11 | use crate::revlog::node::{Node, NodePrefix}; | |
|
11 | 12 | use crate::revlog::revlog::RevlogError; |
|
12 | 13 | use crate::revlog::Revision; |
|
13 | 14 | use crate::utils::hg_path::HgPath; |
@@ -171,15 +172,16 b" impl<'a> ListRevTrackedFiles<'a> {" | |||
|
171 | 172 | let changelog_entry = match self.rev.parse::<Revision>() { |
|
172 | 173 | Ok(rev) => self.changelog.get_rev(rev)?, |
|
173 | 174 | _ => { |
|
174 |
let changelog_node = |
|
|
175 | let changelog_node = NodePrefix::from_hex(&self.rev) | |
|
175 | 176 | .or(Err(ListRevTrackedFilesErrorKind::InvalidRevision))?; |
|
176 |
self.changelog.get_node( |
|
|
177 | self.changelog.get_node(changelog_node.borrow())? | |
|
177 | 178 | } |
|
178 | 179 | }; |
|
179 |
let manifest_node = |
|
|
180 | let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?) | |
|
180 | 181 | .or(Err(ListRevTrackedFilesErrorKind::CorruptedRevlog))?; |
|
181 | 182 | |
|
182 | self.manifest_entry = Some(self.manifest.get_node(&manifest_node)?); | |
|
183 | self.manifest_entry = | |
|
184 | Some(self.manifest.get_node((&manifest_node).into())?); | |
|
183 | 185 | |
|
184 | 186 | if let Some(ref manifest_entry) = self.manifest_entry { |
|
185 | 187 | Ok(manifest_entry.files()) |
@@ -1,4 +1,5 b'' | |||
|
1 | 1 | use crate::revlog::revlog::{Revlog, RevlogError}; |
|
2 | use crate::revlog::NodePrefixRef; | |
|
2 | 3 | use crate::revlog::Revision; |
|
3 | 4 | use std::path::PathBuf; |
|
4 | 5 | |
@@ -19,7 +20,7 b' impl Changelog {' | |||
|
19 | 20 | /// Return the `ChangelogEntry` a given node id. |
|
20 | 21 | pub fn get_node( |
|
21 | 22 | &self, |
|
22 |
node: |
|
|
23 | node: NodePrefixRef, | |
|
23 | 24 | ) -> Result<ChangelogEntry, RevlogError> { |
|
24 | 25 | let rev = self.revlog.get_node_rev(node)?; |
|
25 | 26 | self.get_rev(rev) |
@@ -1,7 +1,9 b'' | |||
|
1 | use std::convert::TryInto; | |
|
1 | 2 | use std::ops::Deref; |
|
2 | 3 | |
|
3 | 4 | use byteorder::{BigEndian, ByteOrder}; |
|
4 | 5 | |
|
6 | use crate::revlog::node::Node; | |
|
5 | 7 | use crate::revlog::revlog::RevlogError; |
|
6 | 8 | use crate::revlog::{Revision, NULL_REVISION}; |
|
7 | 9 | |
@@ -188,8 +190,8 b" impl<'a> IndexEntry<'a> {" | |||
|
188 | 190 | /// |
|
189 | 191 | /// Currently, SHA-1 is used and only the first 20 bytes of this field |
|
190 | 192 | /// are used. |
|
191 |
pub fn hash(&self) -> & |
|
|
192 | &self.bytes[32..52] | |
|
193 | pub fn hash(&self) -> &Node { | |
|
194 | (&self.bytes[32..52]).try_into().unwrap() | |
|
193 | 195 | } |
|
194 | 196 | } |
|
195 | 197 |
@@ -1,4 +1,5 b'' | |||
|
1 | 1 | use crate::revlog::revlog::{Revlog, RevlogError}; |
|
2 | use crate::revlog::NodePrefixRef; | |
|
2 | 3 | use crate::revlog::Revision; |
|
3 | 4 | use crate::utils::hg_path::HgPath; |
|
4 | 5 | use std::path::PathBuf; |
@@ -18,7 +19,10 b' impl Manifest {' | |||
|
18 | 19 | } |
|
19 | 20 | |
|
20 | 21 | /// Return the `ManifestEntry` of a given node id. |
|
21 | pub fn get_node(&self, node: &[u8]) -> Result<ManifestEntry, RevlogError> { | |
|
22 | pub fn get_node( | |
|
23 | &self, | |
|
24 | node: NodePrefixRef, | |
|
25 | ) -> Result<ManifestEntry, RevlogError> { | |
|
22 | 26 | let rev = self.revlog.get_node_rev(node)?; |
|
23 | 27 | self.get_rev(rev) |
|
24 | 28 | } |
@@ -9,6 +9,7 b'' | |||
|
9 | 9 | //! of a revision. |
|
10 | 10 | |
|
11 | 11 | use hex::{self, FromHex, FromHexError}; |
|
12 | use std::convert::{TryFrom, TryInto}; | |
|
12 | 13 | |
|
13 | 14 | /// The length in bytes of a `Node` |
|
14 | 15 | /// |
@@ -65,6 +66,19 b' impl From<NodeData> for Node {' | |||
|
65 | 66 | } |
|
66 | 67 | } |
|
67 | 68 | |
|
69 | /// Return an error if the slice has an unexpected length | |
|
70 | impl<'a> TryFrom<&'a [u8]> for &'a Node { | |
|
71 | type Error = std::array::TryFromSliceError; | |
|
72 | ||
|
73 | #[inline] | |
|
74 | fn try_from(bytes: &'a [u8]) -> Result<&'a Node, Self::Error> { | |
|
75 | let data = bytes.try_into()?; | |
|
76 | // Safety: `#[repr(transparent)]` makes it ok to "wrap" the target | |
|
77 | // of a reference to the type of the single field. | |
|
78 | Ok(unsafe { std::mem::transmute::<&NodeData, &Node>(data) }) | |
|
79 | } | |
|
80 | } | |
|
81 | ||
|
68 | 82 | #[derive(Debug, PartialEq)] |
|
69 | 83 | pub enum NodeError { |
|
70 | 84 | ExactLengthRequired(usize, String), |
@@ -103,8 +117,8 b' impl Node {' | |||
|
103 | 117 | /// |
|
104 | 118 | /// To be used in FFI and I/O only, in order to facilitate future |
|
105 | 119 | /// changes of hash format. |
|
106 |
pub fn from_hex(hex: |
|
|
107 | Ok(NodeData::from_hex(hex) | |
|
120 | pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Node, NodeError> { | |
|
121 | Ok(NodeData::from_hex(hex.as_ref()) | |
|
108 | 122 | .map_err(|e| NodeError::from((e, hex)))? |
|
109 | 123 | .into()) |
|
110 | 124 | } |
@@ -126,17 +140,15 b' impl Node {' | |||
|
126 | 140 | } |
|
127 | 141 | } |
|
128 | 142 | |
|
129 |
impl<T: AsRef< |
|
|
143 | impl<T: AsRef<[u8]>> From<(FromHexError, T)> for NodeError { | |
|
130 | 144 | fn from(err_offender: (FromHexError, T)) -> Self { |
|
131 | 145 | let (err, offender) = err_offender; |
|
146 | let offender = String::from_utf8_lossy(offender.as_ref()).into_owned(); | |
|
132 | 147 | match err { |
|
133 | 148 | FromHexError::InvalidStringLength => { |
|
134 | NodeError::ExactLengthRequired( | |
|
135 | NODE_NYBBLES_LENGTH, | |
|
136 | offender.as_ref().to_owned(), | |
|
137 | ) | |
|
149 | NodeError::ExactLengthRequired(NODE_NYBBLES_LENGTH, offender) | |
|
138 | 150 | } |
|
139 |
_ => NodeError::HexError(err, offender |
|
|
151 | _ => NodeError::HexError(err, offender), | |
|
140 | 152 | } |
|
141 | 153 | } |
|
142 | 154 | } |
@@ -171,8 +183,8 b' impl NodePrefix {' | |||
|
171 | 183 | |
|
172 | 184 | let is_odd = len % 2 == 1; |
|
173 | 185 | let even_part = if is_odd { &hex[..len - 1] } else { hex }; |
|
174 |
let mut buf: Vec<u8> = |
|
|
175 | .map_err(|e| (e, String::from_utf8_lossy(hex)))?; | |
|
186 | let mut buf: Vec<u8> = | |
|
187 | Vec::from_hex(&even_part).map_err(|e| (e, hex))?; | |
|
176 | 188 | |
|
177 | 189 | if is_odd { |
|
178 | 190 | let latest_char = char::from(hex[len - 1]); |
@@ -182,7 +194,7 b' impl NodePrefix {' | |||
|
182 | 194 | c: latest_char, |
|
183 | 195 | index: len - 1, |
|
184 | 196 | }, |
|
185 |
|
|
|
197 | hex, | |
|
186 | 198 | ) |
|
187 | 199 | })? as u8; |
|
188 | 200 | buf.push(latest_nybble << 4); |
@@ -278,6 +290,12 b" impl<'a> From<&'a Node> for NodePrefixRe" | |||
|
278 | 290 | } |
|
279 | 291 | } |
|
280 | 292 | |
|
293 | impl PartialEq<Node> for NodePrefixRef<'_> { | |
|
294 | fn eq(&self, other: &Node) -> bool { | |
|
295 | !self.is_odd && self.buf == other.data | |
|
296 | } | |
|
297 | } | |
|
298 | ||
|
281 | 299 | #[cfg(test)] |
|
282 | 300 | mod tests { |
|
283 | 301 | use super::*; |
@@ -292,7 +310,7 b' mod tests {' | |||
|
292 | 310 | } |
|
293 | 311 | |
|
294 | 312 | /// Pad an hexadecimal string to reach `NODE_NYBBLES_LENGTH` |
|
295 | /// | |
|
313 | ///check_hash | |
|
296 | 314 | /// The padding is made with zeros |
|
297 | 315 | pub fn hex_pad_right(hex: &str) -> String { |
|
298 | 316 | let mut res = hex.to_string(); |
@@ -13,7 +13,7 b' use micro_timer::timed;' | |||
|
13 | 13 | use zstd; |
|
14 | 14 | |
|
15 | 15 | use super::index::Index; |
|
16 |
use super::node::{NODE_BYTES_LENGTH, NULL_NODE |
|
|
16 | use super::node::{NodePrefixRef, NODE_BYTES_LENGTH, NULL_NODE}; | |
|
17 | 17 | use super::patch; |
|
18 | 18 | use crate::revlog::Revision; |
|
19 | 19 | |
@@ -92,17 +92,20 b' impl Revlog {' | |||
|
92 | 92 | |
|
93 | 93 | /// Return the full data associated to a node. |
|
94 | 94 | #[timed] |
|
95 | pub fn get_node_rev(&self, node: &[u8]) -> Result<Revision, RevlogError> { | |
|
95 | pub fn get_node_rev( | |
|
96 | &self, | |
|
97 | node: NodePrefixRef, | |
|
98 | ) -> Result<Revision, RevlogError> { | |
|
96 | 99 | // This is brute force. But it is fast enough for now. |
|
97 | 100 | // Optimization will come later. |
|
98 | 101 | let mut found_by_prefix = None; |
|
99 | 102 | for rev in (0..self.len() as Revision).rev() { |
|
100 | 103 | let index_entry = |
|
101 | 104 | self.index.get_entry(rev).ok_or(RevlogError::Corrupted)?; |
|
102 |
if index_entry.hash() |
|
|
105 | if node == *index_entry.hash() { | |
|
103 | 106 | return Ok(rev); |
|
104 | 107 | } |
|
105 |
if index_entry.hash() |
|
|
108 | if node.is_prefix_of(index_entry.hash()) { | |
|
106 | 109 | if found_by_prefix.is_some() { |
|
107 | 110 | return Err(RevlogError::AmbiguousPrefix); |
|
108 | 111 | } |
@@ -143,7 +146,7 b' impl Revlog {' | |||
|
143 | 146 | if self.check_hash( |
|
144 | 147 | index_entry.p1(), |
|
145 | 148 | index_entry.p2(), |
|
146 | index_entry.hash(), | |
|
149 | index_entry.hash().as_bytes(), | |
|
147 | 150 | &data, |
|
148 | 151 | ) { |
|
149 | 152 | Ok(data) |
@@ -163,15 +166,15 b' impl Revlog {' | |||
|
163 | 166 | let e1 = self.index.get_entry(p1); |
|
164 | 167 | let h1 = match e1 { |
|
165 | 168 | Some(ref entry) => entry.hash(), |
|
166 |
None => &NULL_NODE |
|
|
169 | None => &NULL_NODE, | |
|
167 | 170 | }; |
|
168 | 171 | let e2 = self.index.get_entry(p2); |
|
169 | 172 | let h2 = match e2 { |
|
170 | 173 | Some(ref entry) => entry.hash(), |
|
171 |
None => &NULL_NODE |
|
|
174 | None => &NULL_NODE, | |
|
172 | 175 | }; |
|
173 | 176 | |
|
174 |
hash(data, |
|
|
177 | hash(data, h1.as_bytes(), h2.as_bytes()).as_slice() == expected | |
|
175 | 178 | } |
|
176 | 179 | |
|
177 | 180 | /// Build the full data of a revision out its snapshot |
@@ -115,8 +115,10 b' Specifying revisions by changeset ID' | |||
|
115 | 115 | $ rhg cat -r cf8b83 file-2 |
|
116 | 116 | 2 |
|
117 | 117 | $ rhg cat -r c file-2 |
|
118 |
abort: |
|
|
118 | abort: ambiguous revision identifier c | |
|
119 | 119 | [255] |
|
120 | $ rhg cat -r d file-2 | |
|
121 | 2 | |
|
120 | 122 | |
|
121 | 123 | Cat files |
|
122 | 124 | $ cd $TESTTMP |
General Comments 0
You need to be logged in to leave comments.
Login now