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