Show More
@@ -84,10 +84,10 pub fn cat<'a>( | |||||
84 | mut files: Vec<&'a HgPath>, |
|
84 | mut files: Vec<&'a HgPath>, | |
85 | ) -> Result<CatOutput<'a>, RevlogError> { |
|
85 | ) -> Result<CatOutput<'a>, RevlogError> { | |
86 | let rev = crate::revset::resolve_single(revset, repo)?; |
|
86 | let rev = crate::revset::resolve_single(revset, repo)?; | |
87 | let manifest = repo.manifest_for_rev(rev)?; |
|
87 | let manifest = repo.manifest_for_rev(rev.into())?; | |
88 | let node = *repo |
|
88 | let node = *repo | |
89 | .changelog()? |
|
89 | .changelog()? | |
90 | .node_from_rev(rev) |
|
90 | .node_from_rev(rev.into()) | |
91 | .expect("should succeed when repo.manifest did"); |
|
91 | .expect("should succeed when repo.manifest did"); | |
92 | let mut results: Vec<(&'a HgPath, Vec<u8>)> = vec![]; |
|
92 | let mut results: Vec<(&'a HgPath, Vec<u8>)> = vec![]; | |
93 | let mut found_any = false; |
|
93 | let mut found_any = false; |
@@ -33,6 +33,6 pub fn debug_data( | |||||
33 | Revlog::open(&repo.store_vfs(), index_file, None, use_nodemap)?; |
|
33 | Revlog::open(&repo.store_vfs(), index_file, None, use_nodemap)?; | |
34 | let rev = |
|
34 | let rev = | |
35 | crate::revset::resolve_rev_number_or_hex_prefix(revset, &revlog)?; |
|
35 | crate::revset::resolve_rev_number_or_hex_prefix(revset, &revlog)?; | |
36 | let data = revlog.get_rev_data(rev)?; |
|
36 | let data = revlog.get_rev_data_for_checked_rev(rev)?; | |
37 | Ok(data.into_owned()) |
|
37 | Ok(data.into_owned()) | |
38 | } |
|
38 | } |
@@ -21,7 +21,7 pub fn list_rev_tracked_files( | |||||
21 | ) -> Result<FilesForRev, RevlogError> { |
|
21 | ) -> Result<FilesForRev, RevlogError> { | |
22 | let rev = crate::revset::resolve_single(revset, repo)?; |
|
22 | let rev = crate::revset::resolve_single(revset, repo)?; | |
23 | Ok(FilesForRev { |
|
23 | Ok(FilesForRev { | |
24 | manifest: repo.manifest_for_rev(rev)?, |
|
24 | manifest: repo.manifest_for_rev(rev.into())?, | |
25 | narrow_matcher, |
|
25 | narrow_matcher, | |
26 | }) |
|
26 | }) | |
27 | } |
|
27 | } |
@@ -15,8 +15,8 use crate::utils::files::get_path_from_b | |||||
15 | use crate::utils::hg_path::HgPath; |
|
15 | use crate::utils::hg_path::HgPath; | |
16 | use crate::utils::SliceExt; |
|
16 | use crate::utils::SliceExt; | |
17 | use crate::vfs::{is_dir, is_file, Vfs}; |
|
17 | use crate::vfs::{is_dir, is_file, Vfs}; | |
18 | use crate::{requirements, NodePrefix}; |
|
18 | use crate::DirstateError; | |
19 | use crate::{DirstateError, Revision}; |
|
19 | use crate::{requirements, NodePrefix, UncheckedRevision}; | |
20 | use std::cell::{Ref, RefCell, RefMut}; |
|
20 | use std::cell::{Ref, RefCell, RefMut}; | |
21 | use std::collections::HashSet; |
|
21 | use std::collections::HashSet; | |
22 | use std::io::Seek; |
|
22 | use std::io::Seek; | |
@@ -562,7 +562,7 impl Repo { | |||||
562 | /// Returns the manifest of the *changeset* with the given revision number |
|
562 | /// Returns the manifest of the *changeset* with the given revision number | |
563 | pub fn manifest_for_rev( |
|
563 | pub fn manifest_for_rev( | |
564 | &self, |
|
564 | &self, | |
565 | revision: Revision, |
|
565 | revision: UncheckedRevision, | |
566 | ) -> Result<Manifest, RevlogError> { |
|
566 | ) -> Result<Manifest, RevlogError> { | |
567 | self.manifestlog()?.data_for_node( |
|
567 | self.manifestlog()?.data_for_node( | |
568 | self.changelog()? |
|
568 | self.changelog()? |
@@ -4,6 +4,7 use crate::revlog::{Node, NodePrefix}; | |||||
4 | use crate::revlog::{Revlog, RevlogEntry, RevlogError}; |
|
4 | use crate::revlog::{Revlog, RevlogEntry, RevlogError}; | |
5 | use crate::utils::hg_path::HgPath; |
|
5 | use crate::utils::hg_path::HgPath; | |
6 | use crate::vfs::Vfs; |
|
6 | use crate::vfs::Vfs; | |
|
7 | use crate::UncheckedRevision; | |||
7 | use itertools::Itertools; |
|
8 | use itertools::Itertools; | |
8 | use std::ascii::escape_default; |
|
9 | use std::ascii::escape_default; | |
9 | use std::borrow::Cow; |
|
10 | use std::borrow::Cow; | |
@@ -29,15 +30,24 impl Changelog { | |||||
29 | node: NodePrefix, |
|
30 | node: NodePrefix, | |
30 | ) -> Result<ChangelogRevisionData, RevlogError> { |
|
31 | ) -> Result<ChangelogRevisionData, RevlogError> { | |
31 | let rev = self.revlog.rev_from_node(node)?; |
|
32 | let rev = self.revlog.rev_from_node(node)?; | |
32 |
self. |
|
33 | self.entry_for_checked_rev(rev)?.data() | |
33 | } |
|
34 | } | |
34 |
|
35 | |||
35 | /// Return the [`ChangelogEntry`] for the given revision number. |
|
36 | /// Return the [`ChangelogEntry`] for the given revision number. | |
36 | pub fn entry_for_rev( |
|
37 | pub fn entry_for_rev( | |
37 | &self, |
|
38 | &self, | |
|
39 | rev: UncheckedRevision, | |||
|
40 | ) -> Result<ChangelogEntry, RevlogError> { | |||
|
41 | let revlog_entry = self.revlog.get_entry(rev)?; | |||
|
42 | Ok(ChangelogEntry { revlog_entry }) | |||
|
43 | } | |||
|
44 | ||||
|
45 | /// Same as [`Self::entry_for_rev`] for checked revisions. | |||
|
46 | fn entry_for_checked_rev( | |||
|
47 | &self, | |||
38 | rev: Revision, |
|
48 | rev: Revision, | |
39 | ) -> Result<ChangelogEntry, RevlogError> { |
|
49 | ) -> Result<ChangelogEntry, RevlogError> { | |
40 | let revlog_entry = self.revlog.get_entry(rev)?; |
|
50 | let revlog_entry = self.revlog.get_entry_for_checked_rev(rev)?; | |
41 | Ok(ChangelogEntry { revlog_entry }) |
|
51 | Ok(ChangelogEntry { revlog_entry }) | |
42 | } |
|
52 | } | |
43 |
|
53 | |||
@@ -49,12 +59,12 impl Changelog { | |||||
49 | /// [entry_for_rev](`Self::entry_for_rev`) and doing everything from there. |
|
59 | /// [entry_for_rev](`Self::entry_for_rev`) and doing everything from there. | |
50 | pub fn data_for_rev( |
|
60 | pub fn data_for_rev( | |
51 | &self, |
|
61 | &self, | |
52 | rev: Revision, |
|
62 | rev: UncheckedRevision, | |
53 | ) -> Result<ChangelogRevisionData, RevlogError> { |
|
63 | ) -> Result<ChangelogRevisionData, RevlogError> { | |
54 | self.entry_for_rev(rev)?.data() |
|
64 | self.entry_for_rev(rev)?.data() | |
55 | } |
|
65 | } | |
56 |
|
66 | |||
57 | pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> { |
|
67 | pub fn node_from_rev(&self, rev: UncheckedRevision) -> Option<&Node> { | |
58 | self.revlog.node_from_rev(rev) |
|
68 | self.revlog.node_from_rev(rev) | |
59 | } |
|
69 | } | |
60 |
|
70 | |||
@@ -330,12 +340,12 message", | |||||
330 |
|
340 | |||
331 | let changelog = Changelog { revlog }; |
|
341 | let changelog = Changelog { revlog }; | |
332 | assert_eq!( |
|
342 | assert_eq!( | |
333 | changelog.data_for_rev(NULL_REVISION)?, |
|
343 | changelog.data_for_rev(NULL_REVISION.into())?, | |
334 | ChangelogRevisionData::null() |
|
344 | ChangelogRevisionData::null() | |
335 | ); |
|
345 | ); | |
336 | // same with the intermediate entry object |
|
346 | // same with the intermediate entry object | |
337 | assert_eq!( |
|
347 | assert_eq!( | |
338 | changelog.entry_for_rev(NULL_REVISION)?.data()?, |
|
348 | changelog.entry_for_rev(NULL_REVISION.into())?.data()?, | |
339 | ChangelogRevisionData::null() |
|
349 | ChangelogRevisionData::null() | |
340 | ); |
|
350 | ); | |
341 | Ok(()) |
|
351 | Ok(()) |
@@ -1,4 +1,5 | |||||
1 | use crate::errors::HgError; |
|
1 | use crate::errors::HgError; | |
|
2 | use crate::exit_codes; | |||
2 | use crate::repo::Repo; |
|
3 | use crate::repo::Repo; | |
3 | use crate::revlog::path_encode::path_encode; |
|
4 | use crate::revlog::path_encode::path_encode; | |
4 | use crate::revlog::NodePrefix; |
|
5 | use crate::revlog::NodePrefix; | |
@@ -8,6 +9,7 use crate::revlog::{Revlog, RevlogError} | |||||
8 | use crate::utils::files::get_path_from_bytes; |
|
9 | use crate::utils::files::get_path_from_bytes; | |
9 | use crate::utils::hg_path::HgPath; |
|
10 | use crate::utils::hg_path::HgPath; | |
10 | use crate::utils::SliceExt; |
|
11 | use crate::utils::SliceExt; | |
|
12 | use crate::UncheckedRevision; | |||
11 | use std::path::PathBuf; |
|
13 | use std::path::PathBuf; | |
12 |
|
14 | |||
13 | /// A specialized `Revlog` to work with file data logs. |
|
15 | /// A specialized `Revlog` to work with file data logs. | |
@@ -39,14 +41,14 impl Filelog { | |||||
39 | file_node: impl Into<NodePrefix>, |
|
41 | file_node: impl Into<NodePrefix>, | |
40 | ) -> Result<FilelogRevisionData, RevlogError> { |
|
42 | ) -> Result<FilelogRevisionData, RevlogError> { | |
41 | let file_rev = self.revlog.rev_from_node(file_node.into())?; |
|
43 | let file_rev = self.revlog.rev_from_node(file_node.into())?; | |
42 | self.data_for_rev(file_rev) |
|
44 | self.data_for_rev(file_rev.into()) | |
43 | } |
|
45 | } | |
44 |
|
46 | |||
45 | /// The given revision is that of the file as found in a filelog, not of a |
|
47 | /// The given revision is that of the file as found in a filelog, not of a | |
46 | /// changeset. |
|
48 | /// changeset. | |
47 | pub fn data_for_rev( |
|
49 | pub fn data_for_rev( | |
48 | &self, |
|
50 | &self, | |
49 | file_rev: Revision, |
|
51 | file_rev: UncheckedRevision, | |
50 | ) -> Result<FilelogRevisionData, RevlogError> { |
|
52 | ) -> Result<FilelogRevisionData, RevlogError> { | |
51 | let data: Vec<u8> = self.revlog.get_rev_data(file_rev)?.into_owned(); |
|
53 | let data: Vec<u8> = self.revlog.get_rev_data(file_rev)?.into_owned(); | |
52 | Ok(FilelogRevisionData(data)) |
|
54 | Ok(FilelogRevisionData(data)) | |
@@ -59,16 +61,25 impl Filelog { | |||||
59 | file_node: impl Into<NodePrefix>, |
|
61 | file_node: impl Into<NodePrefix>, | |
60 | ) -> Result<FilelogEntry, RevlogError> { |
|
62 | ) -> Result<FilelogEntry, RevlogError> { | |
61 | let file_rev = self.revlog.rev_from_node(file_node.into())?; |
|
63 | let file_rev = self.revlog.rev_from_node(file_node.into())?; | |
62 | self.entry_for_rev(file_rev) |
|
64 | self.entry_for_checked_rev(file_rev) | |
63 | } |
|
65 | } | |
64 |
|
66 | |||
65 | /// The given revision is that of the file as found in a filelog, not of a |
|
67 | /// The given revision is that of the file as found in a filelog, not of a | |
66 | /// changeset. |
|
68 | /// changeset. | |
67 | pub fn entry_for_rev( |
|
69 | pub fn entry_for_rev( | |
68 | &self, |
|
70 | &self, | |
|
71 | file_rev: UncheckedRevision, | |||
|
72 | ) -> Result<FilelogEntry, RevlogError> { | |||
|
73 | Ok(FilelogEntry(self.revlog.get_entry(file_rev)?)) | |||
|
74 | } | |||
|
75 | ||||
|
76 | fn entry_for_checked_rev( | |||
|
77 | &self, | |||
69 | file_rev: Revision, |
|
78 | file_rev: Revision, | |
70 | ) -> Result<FilelogEntry, RevlogError> { |
|
79 | ) -> Result<FilelogEntry, RevlogError> { | |
71 | Ok(FilelogEntry(self.revlog.get_entry(file_rev)?)) |
|
80 | Ok(FilelogEntry( | |
|
81 | self.revlog.get_entry_for_checked_rev(file_rev)?, | |||
|
82 | )) | |||
72 | } |
|
83 | } | |
73 | } |
|
84 | } | |
74 |
|
85 | |||
@@ -165,7 +176,19 impl FilelogEntry<'_> { | |||||
165 | } |
|
176 | } | |
166 |
|
177 | |||
167 | pub fn data(&self) -> Result<FilelogRevisionData, HgError> { |
|
178 | pub fn data(&self) -> Result<FilelogRevisionData, HgError> { | |
168 | Ok(FilelogRevisionData(self.0.data()?.into_owned())) |
|
179 | let data = self.0.data(); | |
|
180 | match data { | |||
|
181 | Ok(data) => Ok(FilelogRevisionData(data.into_owned())), | |||
|
182 | // Errors other than `HgError` should not happen at this point | |||
|
183 | Err(e) => match e { | |||
|
184 | RevlogError::Other(hg_error) => Err(hg_error), | |||
|
185 | revlog_error => Err(HgError::abort( | |||
|
186 | revlog_error.to_string(), | |||
|
187 | exit_codes::ABORT, | |||
|
188 | None, | |||
|
189 | )), | |||
|
190 | }, | |||
|
191 | } | |||
169 | } |
|
192 | } | |
170 | } |
|
193 | } | |
171 |
|
194 |
@@ -1,3 +1,4 | |||||
|
1 | use std::fmt::Debug; | |||
1 | use std::ops::Deref; |
|
2 | use std::ops::Deref; | |
2 |
|
3 | |||
3 | use byteorder::{BigEndian, ByteOrder}; |
|
4 | use byteorder::{BigEndian, ByteOrder}; | |
@@ -5,6 +6,7 use byteorder::{BigEndian, ByteOrder}; | |||||
5 | use crate::errors::HgError; |
|
6 | use crate::errors::HgError; | |
6 | use crate::revlog::node::Node; |
|
7 | use crate::revlog::node::Node; | |
7 | use crate::revlog::{Revision, NULL_REVISION}; |
|
8 | use crate::revlog::{Revision, NULL_REVISION}; | |
|
9 | use crate::UncheckedRevision; | |||
8 |
|
10 | |||
9 | pub const INDEX_ENTRY_SIZE: usize = 64; |
|
11 | pub const INDEX_ENTRY_SIZE: usize = 64; | |
10 |
|
12 | |||
@@ -86,6 +88,15 pub struct Index { | |||||
86 | uses_generaldelta: bool, |
|
88 | uses_generaldelta: bool, | |
87 | } |
|
89 | } | |
88 |
|
90 | |||
|
91 | impl Debug for Index { | |||
|
92 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |||
|
93 | f.debug_struct("Index") | |||
|
94 | .field("offsets", &self.offsets) | |||
|
95 | .field("uses_generaldelta", &self.uses_generaldelta) | |||
|
96 | .finish() | |||
|
97 | } | |||
|
98 | } | |||
|
99 | ||||
89 | impl Index { |
|
100 | impl Index { | |
90 | /// Create an index from bytes. |
|
101 | /// Create an index from bytes. | |
91 | /// Calculate the start of each entry when is_inline is true. |
|
102 | /// Calculate the start of each entry when is_inline is true. | |
@@ -175,36 +186,32 impl Index { | |||||
175 | if rev == NULL_REVISION { |
|
186 | if rev == NULL_REVISION { | |
176 | return None; |
|
187 | return None; | |
177 | } |
|
188 | } | |
178 | if let Some(offsets) = &self.offsets { |
|
189 | Some(if let Some(offsets) = &self.offsets { | |
179 | self.get_entry_inline(rev, offsets) |
|
190 | self.get_entry_inline(rev, offsets) | |
180 | } else { |
|
191 | } else { | |
181 | self.get_entry_separated(rev) |
|
192 | self.get_entry_separated(rev) | |
182 | } |
|
193 | }) | |
183 | } |
|
194 | } | |
184 |
|
195 | |||
185 | fn get_entry_inline( |
|
196 | fn get_entry_inline( | |
186 | &self, |
|
197 | &self, | |
187 | rev: Revision, |
|
198 | rev: Revision, | |
188 | offsets: &[usize], |
|
199 | offsets: &[usize], | |
189 |
) -> |
|
200 | ) -> IndexEntry { | |
190 |
let start = |
|
201 | let start = offsets[rev as usize]; | |
191 |
let end = start |
|
202 | let end = start + INDEX_ENTRY_SIZE; | |
192 | let bytes = &self.bytes[start..end]; |
|
203 | let bytes = &self.bytes[start..end]; | |
193 |
|
204 | |||
194 | // See IndexEntry for an explanation of this override. |
|
205 | // See IndexEntry for an explanation of this override. | |
195 | let offset_override = Some(end); |
|
206 | let offset_override = Some(end); | |
196 |
|
207 | |||
197 |
|
|
208 | IndexEntry { | |
198 | bytes, |
|
209 | bytes, | |
199 | offset_override, |
|
210 | offset_override, | |
200 |
} |
|
211 | } | |
201 | } |
|
212 | } | |
202 |
|
213 | |||
203 |
fn get_entry_separated(&self, rev: Revision) -> |
|
214 | fn get_entry_separated(&self, rev: Revision) -> IndexEntry { | |
204 | let max_rev = self.bytes.len() / INDEX_ENTRY_SIZE; |
|
|||
205 | if rev as usize >= max_rev { |
|
|||
206 | return None; |
|
|||
207 | } |
|
|||
208 | let start = rev as usize * INDEX_ENTRY_SIZE; |
|
215 | let start = rev as usize * INDEX_ENTRY_SIZE; | |
209 | let end = start + INDEX_ENTRY_SIZE; |
|
216 | let end = start + INDEX_ENTRY_SIZE; | |
210 | let bytes = &self.bytes[start..end]; |
|
217 | let bytes = &self.bytes[start..end]; | |
@@ -213,10 +220,10 impl Index { | |||||
213 | // for the index's metadata (saving space because it is always 0) |
|
220 | // for the index's metadata (saving space because it is always 0) | |
214 | let offset_override = if rev == 0 { Some(0) } else { None }; |
|
221 | let offset_override = if rev == 0 { Some(0) } else { None }; | |
215 |
|
222 | |||
216 |
|
|
223 | IndexEntry { | |
217 | bytes, |
|
224 | bytes, | |
218 | offset_override, |
|
225 | offset_override, | |
219 |
} |
|
226 | } | |
220 | } |
|
227 | } | |
221 | } |
|
228 | } | |
222 |
|
229 | |||
@@ -273,23 +280,23 impl<'a> IndexEntry<'a> { | |||||
273 | } |
|
280 | } | |
274 |
|
281 | |||
275 | /// Return the revision upon which the data has been derived. |
|
282 | /// Return the revision upon which the data has been derived. | |
276 | pub fn base_revision_or_base_of_delta_chain(&self) -> Revision { |
|
283 | pub fn base_revision_or_base_of_delta_chain(&self) -> UncheckedRevision { | |
277 | // TODO Maybe return an Option when base_revision == rev? |
|
284 | // TODO Maybe return an Option when base_revision == rev? | |
278 | // Requires to add rev to IndexEntry |
|
285 | // Requires to add rev to IndexEntry | |
279 |
|
286 | |||
280 | BigEndian::read_i32(&self.bytes[16..]) |
|
287 | BigEndian::read_i32(&self.bytes[16..]).into() | |
281 | } |
|
288 | } | |
282 |
|
289 | |||
283 | pub fn link_revision(&self) -> Revision { |
|
290 | pub fn link_revision(&self) -> UncheckedRevision { | |
284 | BigEndian::read_i32(&self.bytes[20..]) |
|
291 | BigEndian::read_i32(&self.bytes[20..]).into() | |
285 | } |
|
292 | } | |
286 |
|
293 | |||
287 | pub fn p1(&self) -> Revision { |
|
294 | pub fn p1(&self) -> UncheckedRevision { | |
288 | BigEndian::read_i32(&self.bytes[24..]) |
|
295 | BigEndian::read_i32(&self.bytes[24..]).into() | |
289 | } |
|
296 | } | |
290 |
|
297 | |||
291 | pub fn p2(&self) -> Revision { |
|
298 | pub fn p2(&self) -> UncheckedRevision { | |
292 | BigEndian::read_i32(&self.bytes[28..]) |
|
299 | BigEndian::read_i32(&self.bytes[28..]).into() | |
293 | } |
|
300 | } | |
294 |
|
301 | |||
295 | /// Return the hash of revision's full text. |
|
302 | /// Return the hash of revision's full text. | |
@@ -547,7 +554,7 mod tests { | |||||
547 | offset_override: None, |
|
554 | offset_override: None, | |
548 | }; |
|
555 | }; | |
549 |
|
556 | |||
550 | assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1) |
|
557 | assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1.into()) | |
551 | } |
|
558 | } | |
552 |
|
559 | |||
553 | #[test] |
|
560 | #[test] | |
@@ -559,7 +566,7 mod tests { | |||||
559 | offset_override: None, |
|
566 | offset_override: None, | |
560 | }; |
|
567 | }; | |
561 |
|
568 | |||
562 | assert_eq!(entry.link_revision(), 123); |
|
569 | assert_eq!(entry.link_revision(), 123.into()); | |
563 | } |
|
570 | } | |
564 |
|
571 | |||
565 | #[test] |
|
572 | #[test] | |
@@ -571,7 +578,7 mod tests { | |||||
571 | offset_override: None, |
|
578 | offset_override: None, | |
572 | }; |
|
579 | }; | |
573 |
|
580 | |||
574 | assert_eq!(entry.p1(), 123); |
|
581 | assert_eq!(entry.p1(), 123.into()); | |
575 | } |
|
582 | } | |
576 |
|
583 | |||
577 | #[test] |
|
584 | #[test] | |
@@ -583,7 +590,7 mod tests { | |||||
583 | offset_override: None, |
|
590 | offset_override: None, | |
584 | }; |
|
591 | }; | |
585 |
|
592 | |||
586 | assert_eq!(entry.p2(), 123); |
|
593 | assert_eq!(entry.p2(), 123.into()); | |
587 | } |
|
594 | } | |
588 |
|
595 | |||
589 | #[test] |
|
596 | #[test] |
@@ -1,10 +1,10 | |||||
1 | use crate::errors::HgError; |
|
1 | use crate::errors::HgError; | |
2 | use crate::revlog::Revision; |
|
|||
3 | use crate::revlog::{Node, NodePrefix}; |
|
2 | use crate::revlog::{Node, NodePrefix}; | |
4 | use crate::revlog::{Revlog, RevlogError}; |
|
3 | use crate::revlog::{Revlog, RevlogError}; | |
5 | use crate::utils::hg_path::HgPath; |
|
4 | use crate::utils::hg_path::HgPath; | |
6 | use crate::utils::SliceExt; |
|
5 | use crate::utils::SliceExt; | |
7 | use crate::vfs::Vfs; |
|
6 | use crate::vfs::Vfs; | |
|
7 | use crate::{Revision, UncheckedRevision}; | |||
8 |
|
8 | |||
9 | /// A specialized `Revlog` to work with `manifest` data format. |
|
9 | /// A specialized `Revlog` to work with `manifest` data format. | |
10 | pub struct Manifestlog { |
|
10 | pub struct Manifestlog { | |
@@ -32,7 +32,7 impl Manifestlog { | |||||
32 | node: NodePrefix, |
|
32 | node: NodePrefix, | |
33 | ) -> Result<Manifest, RevlogError> { |
|
33 | ) -> Result<Manifest, RevlogError> { | |
34 | let rev = self.revlog.rev_from_node(node)?; |
|
34 | let rev = self.revlog.rev_from_node(node)?; | |
35 | self.data_for_rev(rev) |
|
35 | self.data_for_checked_rev(rev) | |
36 | } |
|
36 | } | |
37 |
|
37 | |||
38 | /// Return the `Manifest` of a given revision number. |
|
38 | /// Return the `Manifest` of a given revision number. | |
@@ -43,9 +43,18 impl Manifestlog { | |||||
43 | /// See also `Repo::manifest_for_rev` |
|
43 | /// See also `Repo::manifest_for_rev` | |
44 | pub fn data_for_rev( |
|
44 | pub fn data_for_rev( | |
45 | &self, |
|
45 | &self, | |
|
46 | rev: UncheckedRevision, | |||
|
47 | ) -> Result<Manifest, RevlogError> { | |||
|
48 | let bytes = self.revlog.get_rev_data(rev)?.into_owned(); | |||
|
49 | Ok(Manifest { bytes }) | |||
|
50 | } | |||
|
51 | ||||
|
52 | pub fn data_for_checked_rev( | |||
|
53 | &self, | |||
46 | rev: Revision, |
|
54 | rev: Revision, | |
47 | ) -> Result<Manifest, RevlogError> { |
|
55 | ) -> Result<Manifest, RevlogError> { | |
48 | let bytes = self.revlog.get_rev_data(rev)?.into_owned(); |
|
56 | let bytes = | |
|
57 | self.revlog.get_rev_data_for_checked_rev(rev)?.into_owned(); | |||
49 | Ok(Manifest { bytes }) |
|
58 | Ok(Manifest { bytes }) | |
50 | } |
|
59 | } | |
51 | } |
|
60 | } |
@@ -47,7 +47,24 pub type Revision = i32; | |||||
47 | /// |
|
47 | /// | |
48 | /// As noted in revlog.c, revision numbers are actually encoded in |
|
48 | /// As noted in revlog.c, revision numbers are actually encoded in | |
49 | /// 4 bytes, and are liberally converted to ints, whence the i32 |
|
49 | /// 4 bytes, and are liberally converted to ints, whence the i32 | |
50 | pub type UncheckedRevision = i32; |
|
50 | #[derive( | |
|
51 | Debug, | |||
|
52 | derive_more::Display, | |||
|
53 | Clone, | |||
|
54 | Copy, | |||
|
55 | Hash, | |||
|
56 | PartialEq, | |||
|
57 | Eq, | |||
|
58 | PartialOrd, | |||
|
59 | Ord, | |||
|
60 | )] | |||
|
61 | pub struct UncheckedRevision(i32); | |||
|
62 | ||||
|
63 | impl From<Revision> for UncheckedRevision { | |||
|
64 | fn from(value: Revision) -> Self { | |||
|
65 | Self(value) | |||
|
66 | } | |||
|
67 | } | |||
51 |
|
68 | |||
52 | /// Marker expressing the absence of a parent |
|
69 | /// Marker expressing the absence of a parent | |
53 | /// |
|
70 | /// | |
@@ -60,7 +77,8 pub const NULL_REVISION: Revision = -1; | |||||
60 | /// This is also equal to `i32::max_value()`, but it's better to spell |
|
77 | /// This is also equal to `i32::max_value()`, but it's better to spell | |
61 | /// it out explicitely, same as in `mercurial.node` |
|
78 | /// it out explicitely, same as in `mercurial.node` | |
62 | #[allow(clippy::unreadable_literal)] |
|
79 | #[allow(clippy::unreadable_literal)] | |
63 |
pub const WORKING_DIRECTORY_REVISION: Revision = |
|
80 | pub const WORKING_DIRECTORY_REVISION: UncheckedRevision = | |
|
81 | UncheckedRevision(0x7fffffff); | |||
64 |
|
82 | |||
65 | pub const WORKING_DIRECTORY_HEX: &str = |
|
83 | pub const WORKING_DIRECTORY_HEX: &str = | |
66 | "ffffffffffffffffffffffffffffffffffffffff"; |
|
84 | "ffffffffffffffffffffffffffffffffffffffff"; | |
@@ -90,14 +108,14 pub trait RevlogIndex { | |||||
90 | self.len() == 0 |
|
108 | self.len() == 0 | |
91 | } |
|
109 | } | |
92 |
|
110 | |||
93 |
/// Return a reference to the Node or `None` |
|
111 | /// Return a reference to the Node or `None` for `NULL_REVISION` | |
94 | /// |
|
|||
95 | /// `NULL_REVISION` is not considered to be out of bounds. |
|
|||
96 | fn node(&self, rev: Revision) -> Option<&Node>; |
|
112 | fn node(&self, rev: Revision) -> Option<&Node>; | |
97 |
|
113 | |||
98 | /// Return a [`Revision`] if `rev` is a valid revision number for this |
|
114 | /// Return a [`Revision`] if `rev` is a valid revision number for this | |
99 | /// index |
|
115 | /// index | |
100 | fn check_revision(&self, rev: UncheckedRevision) -> Option<Revision> { |
|
116 | fn check_revision(&self, rev: UncheckedRevision) -> Option<Revision> { | |
|
117 | let rev = rev.0; | |||
|
118 | ||||
101 | if rev == NULL_REVISION || (rev >= 0 && (rev as usize) < self.len()) { |
|
119 | if rev == NULL_REVISION || (rev >= 0 && (rev as usize) < self.len()) { | |
102 | Some(rev) |
|
120 | Some(rev) | |
103 | } else { |
|
121 | } else { | |
@@ -120,7 +138,7 const REVIDX_KNOWN_FLAGS: u16 = REVISION | |||||
120 |
|
138 | |||
121 | const NULL_REVLOG_ENTRY_FLAGS: u16 = 0; |
|
139 | const NULL_REVLOG_ENTRY_FLAGS: u16 = 0; | |
122 |
|
140 | |||
123 | #[derive(Debug, derive_more::From)] |
|
141 | #[derive(Debug, derive_more::From, derive_more::Display)] | |
124 | pub enum RevlogError { |
|
142 | pub enum RevlogError { | |
125 | InvalidRevision, |
|
143 | InvalidRevision, | |
126 | /// Working directory is not supported |
|
144 | /// Working directory is not supported | |
@@ -231,10 +249,11 impl Revlog { | |||||
231 |
|
249 | |||
232 | /// Returns the node ID for the given revision number, if it exists in this |
|
250 | /// Returns the node ID for the given revision number, if it exists in this | |
233 | /// revlog |
|
251 | /// revlog | |
234 | pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> { |
|
252 | pub fn node_from_rev(&self, rev: UncheckedRevision) -> Option<&Node> { | |
235 | if rev == NULL_REVISION { |
|
253 | if rev == NULL_REVISION.into() { | |
236 | return Some(&NULL_NODE); |
|
254 | return Some(&NULL_NODE); | |
237 | } |
|
255 | } | |
|
256 | let rev = self.index.check_revision(rev)?; | |||
238 | Some(self.index.get_entry(rev)?.hash()) |
|
257 | Some(self.index.get_entry(rev)?.hash()) | |
239 | } |
|
258 | } | |
240 |
|
259 | |||
@@ -296,8 +315,8 impl Revlog { | |||||
296 | } |
|
315 | } | |
297 |
|
316 | |||
298 | /// Returns whether the given revision exists in this revlog. |
|
317 | /// Returns whether the given revision exists in this revlog. | |
299 | pub fn has_rev(&self, rev: Revision) -> bool { |
|
318 | pub fn has_rev(&self, rev: UncheckedRevision) -> bool { | |
300 |
self.index. |
|
319 | self.index.check_revision(rev).is_some() | |
301 | } |
|
320 | } | |
302 |
|
321 | |||
303 | /// Return the full data associated to a revision. |
|
322 | /// Return the full data associated to a revision. | |
@@ -307,12 +326,23 impl Revlog { | |||||
307 | /// snapshot to rebuild the final data. |
|
326 | /// snapshot to rebuild the final data. | |
308 | pub fn get_rev_data( |
|
327 | pub fn get_rev_data( | |
309 | &self, |
|
328 | &self, | |
|
329 | rev: UncheckedRevision, | |||
|
330 | ) -> Result<Cow<[u8]>, RevlogError> { | |||
|
331 | if rev == NULL_REVISION.into() { | |||
|
332 | return Ok(Cow::Borrowed(&[])); | |||
|
333 | }; | |||
|
334 | self.get_entry(rev)?.data() | |||
|
335 | } | |||
|
336 | ||||
|
337 | /// [`Self::get_rev_data`] for checked revisions. | |||
|
338 | pub fn get_rev_data_for_checked_rev( | |||
|
339 | &self, | |||
310 | rev: Revision, |
|
340 | rev: Revision, | |
311 | ) -> Result<Cow<[u8]>, RevlogError> { |
|
341 | ) -> Result<Cow<[u8]>, RevlogError> { | |
312 | if rev == NULL_REVISION { |
|
342 | if rev == NULL_REVISION { | |
313 | return Ok(Cow::Borrowed(&[])); |
|
343 | return Ok(Cow::Borrowed(&[])); | |
314 | }; |
|
344 | }; | |
315 |
|
|
345 | self.get_entry_for_checked_rev(rev)?.data() | |
316 | } |
|
346 | } | |
317 |
|
347 | |||
318 | /// Check the hash of some given data against the recorded hash. |
|
348 | /// Check the hash of some given data against the recorded hash. | |
@@ -380,8 +410,7 impl Revlog { | |||||
380 | } |
|
410 | } | |
381 | } |
|
411 | } | |
382 |
|
412 | |||
383 | /// Get an entry of the revlog. |
|
413 | fn get_entry_for_checked_rev( | |
384 | pub fn get_entry( |
|
|||
385 | &self, |
|
414 | &self, | |
386 | rev: Revision, |
|
415 | rev: Revision, | |
387 | ) -> Result<RevlogEntry, RevlogError> { |
|
416 | ) -> Result<RevlogEntry, RevlogError> { | |
@@ -399,36 +428,60 impl Revlog { | |||||
399 | } else { |
|
428 | } else { | |
400 | &self.data()[start..end] |
|
429 | &self.data()[start..end] | |
401 | }; |
|
430 | }; | |
|
431 | let base_rev = self | |||
|
432 | .index | |||
|
433 | .check_revision(index_entry.base_revision_or_base_of_delta_chain()) | |||
|
434 | .ok_or_else(|| { | |||
|
435 | RevlogError::corrupted(format!( | |||
|
436 | "base revision for rev {} is invalid", | |||
|
437 | rev | |||
|
438 | )) | |||
|
439 | })?; | |||
|
440 | let p1 = | |||
|
441 | self.index.check_revision(index_entry.p1()).ok_or_else(|| { | |||
|
442 | RevlogError::corrupted(format!( | |||
|
443 | "p1 for rev {} is invalid", | |||
|
444 | rev | |||
|
445 | )) | |||
|
446 | })?; | |||
|
447 | let p2 = | |||
|
448 | self.index.check_revision(index_entry.p2()).ok_or_else(|| { | |||
|
449 | RevlogError::corrupted(format!( | |||
|
450 | "p2 for rev {} is invalid", | |||
|
451 | rev | |||
|
452 | )) | |||
|
453 | })?; | |||
402 | let entry = RevlogEntry { |
|
454 | let entry = RevlogEntry { | |
403 | revlog: self, |
|
455 | revlog: self, | |
404 | rev, |
|
456 | rev, | |
405 | bytes: data, |
|
457 | bytes: data, | |
406 | compressed_len: index_entry.compressed_len(), |
|
458 | compressed_len: index_entry.compressed_len(), | |
407 | uncompressed_len: index_entry.uncompressed_len(), |
|
459 | uncompressed_len: index_entry.uncompressed_len(), | |
408 |
base_rev_or_base_of_delta_chain: if |
|
460 | base_rev_or_base_of_delta_chain: if base_rev == rev { | |
409 | .base_revision_or_base_of_delta_chain() |
|
|||
410 | == rev |
|
|||
411 | { |
|
|||
412 | None |
|
461 | None | |
413 | } else { |
|
462 | } else { | |
414 | Some(index_entry.base_revision_or_base_of_delta_chain()) |
|
463 | Some(base_rev) | |
415 | }, |
|
464 | }, | |
416 |
p1 |
|
465 | p1, | |
417 |
p2 |
|
466 | p2, | |
418 | flags: index_entry.flags(), |
|
467 | flags: index_entry.flags(), | |
419 | hash: *index_entry.hash(), |
|
468 | hash: *index_entry.hash(), | |
420 | }; |
|
469 | }; | |
421 | Ok(entry) |
|
470 | Ok(entry) | |
422 | } |
|
471 | } | |
423 |
|
472 | |||
424 | /// when resolving internal references within revlog, any errors |
|
473 | /// Get an entry of the revlog. | |
425 | /// should be reported as corruption, instead of e.g. "invalid revision" |
|
474 | pub fn get_entry( | |
426 | fn get_entry_internal( |
|
|||
427 | &self, |
|
475 | &self, | |
428 | rev: Revision, |
|
476 | rev: UncheckedRevision, | |
429 |
) -> Result<RevlogEntry, |
|
477 | ) -> Result<RevlogEntry, RevlogError> { | |
430 | self.get_entry(rev) |
|
478 | if rev == NULL_REVISION.into() { | |
431 | .map_err(|_| corrupted(format!("revision {} out of range", rev))) |
|
479 | return Ok(self.make_null_entry()); | |
|
480 | } | |||
|
481 | let rev = self.index.check_revision(rev).ok_or_else(|| { | |||
|
482 | RevlogError::corrupted(format!("rev {} is invalid", rev)) | |||
|
483 | })?; | |||
|
484 | self.get_entry_for_checked_rev(rev) | |||
432 | } |
|
485 | } | |
433 | } |
|
486 | } | |
434 |
|
487 | |||
@@ -486,7 +539,7 impl<'revlog> RevlogEntry<'revlog> { | |||||
486 | if self.p1 == NULL_REVISION { |
|
539 | if self.p1 == NULL_REVISION { | |
487 | Ok(None) |
|
540 | Ok(None) | |
488 | } else { |
|
541 | } else { | |
489 | Ok(Some(self.revlog.get_entry(self.p1)?)) |
|
542 | Ok(Some(self.revlog.get_entry_for_checked_rev(self.p1)?)) | |
490 | } |
|
543 | } | |
491 | } |
|
544 | } | |
492 |
|
545 | |||
@@ -496,7 +549,7 impl<'revlog> RevlogEntry<'revlog> { | |||||
496 | if self.p2 == NULL_REVISION { |
|
549 | if self.p2 == NULL_REVISION { | |
497 | Ok(None) |
|
550 | Ok(None) | |
498 | } else { |
|
551 | } else { | |
499 | Ok(Some(self.revlog.get_entry(self.p2)?)) |
|
552 | Ok(Some(self.revlog.get_entry_for_checked_rev(self.p2)?)) | |
500 | } |
|
553 | } | |
501 | } |
|
554 | } | |
502 |
|
555 | |||
@@ -527,7 +580,7 impl<'revlog> RevlogEntry<'revlog> { | |||||
527 | } |
|
580 | } | |
528 |
|
581 | |||
529 | /// The data for this entry, after resolving deltas if any. |
|
582 | /// The data for this entry, after resolving deltas if any. | |
530 |
pub fn rawdata(&self) -> Result<Cow<'revlog, [u8]>, |
|
583 | pub fn rawdata(&self) -> Result<Cow<'revlog, [u8]>, RevlogError> { | |
531 | let mut entry = self.clone(); |
|
584 | let mut entry = self.clone(); | |
532 | let mut delta_chain = vec![]; |
|
585 | let mut delta_chain = vec![]; | |
533 |
|
586 | |||
@@ -539,11 +592,11 impl<'revlog> RevlogEntry<'revlog> { | |||||
539 | while let Some(base_rev) = entry.base_rev_or_base_of_delta_chain { |
|
592 | while let Some(base_rev) = entry.base_rev_or_base_of_delta_chain { | |
540 | entry = if uses_generaldelta { |
|
593 | entry = if uses_generaldelta { | |
541 | delta_chain.push(entry); |
|
594 | delta_chain.push(entry); | |
542 |
self.revlog.get_entry_ |
|
595 | self.revlog.get_entry_for_checked_rev(base_rev)? | |
543 | } else { |
|
596 | } else { | |
544 | let base_rev = entry.rev - 1; |
|
597 | let base_rev = UncheckedRevision(entry.rev - 1); | |
545 | delta_chain.push(entry); |
|
598 | delta_chain.push(entry); | |
546 |
self.revlog.get_entry |
|
599 | self.revlog.get_entry(base_rev)? | |
547 | }; |
|
600 | }; | |
548 | } |
|
601 | } | |
549 |
|
602 | |||
@@ -559,7 +612,7 impl<'revlog> RevlogEntry<'revlog> { | |||||
559 | fn check_data( |
|
612 | fn check_data( | |
560 | &self, |
|
613 | &self, | |
561 | data: Cow<'revlog, [u8]>, |
|
614 | data: Cow<'revlog, [u8]>, | |
562 |
) -> Result<Cow<'revlog, [u8]>, |
|
615 | ) -> Result<Cow<'revlog, [u8]>, RevlogError> { | |
563 | if self.revlog.check_hash( |
|
616 | if self.revlog.check_hash( | |
564 | self.p1, |
|
617 | self.p1, | |
565 | self.p2, |
|
618 | self.p2, | |
@@ -571,22 +624,24 impl<'revlog> RevlogEntry<'revlog> { | |||||
571 | if (self.flags & REVISION_FLAG_ELLIPSIS) != 0 { |
|
624 | if (self.flags & REVISION_FLAG_ELLIPSIS) != 0 { | |
572 | return Err(HgError::unsupported( |
|
625 | return Err(HgError::unsupported( | |
573 | "ellipsis revisions are not supported by rhg", |
|
626 | "ellipsis revisions are not supported by rhg", | |
574 |
) |
|
627 | ) | |
|
628 | .into()); | |||
575 | } |
|
629 | } | |
576 | Err(corrupted(format!( |
|
630 | Err(corrupted(format!( | |
577 | "hash check failed for revision {}", |
|
631 | "hash check failed for revision {}", | |
578 | self.rev |
|
632 | self.rev | |
579 |
)) |
|
633 | )) | |
|
634 | .into()) | |||
580 | } |
|
635 | } | |
581 | } |
|
636 | } | |
582 |
|
637 | |||
583 |
pub fn data(&self) -> Result<Cow<'revlog, [u8]>, |
|
638 | pub fn data(&self) -> Result<Cow<'revlog, [u8]>, RevlogError> { | |
584 | let data = self.rawdata()?; |
|
639 | let data = self.rawdata()?; | |
585 | if self.rev == NULL_REVISION { |
|
640 | if self.rev == NULL_REVISION { | |
586 | return Ok(data); |
|
641 | return Ok(data); | |
587 | } |
|
642 | } | |
588 | if self.is_censored() { |
|
643 | if self.is_censored() { | |
589 | return Err(HgError::CensoredNodeError); |
|
644 | return Err(HgError::CensoredNodeError.into()); | |
590 | } |
|
645 | } | |
591 | self.check_data(data) |
|
646 | self.check_data(data) | |
592 | } |
|
647 | } | |
@@ -705,13 +760,13 mod tests { | |||||
705 | let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap(); |
|
760 | let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap(); | |
706 | assert!(revlog.is_empty()); |
|
761 | assert!(revlog.is_empty()); | |
707 | assert_eq!(revlog.len(), 0); |
|
762 | assert_eq!(revlog.len(), 0); | |
708 | assert!(revlog.get_entry(0).is_err()); |
|
763 | assert!(revlog.get_entry(0.into()).is_err()); | |
709 | assert!(!revlog.has_rev(0)); |
|
764 | assert!(!revlog.has_rev(0.into())); | |
710 | assert_eq!( |
|
765 | assert_eq!( | |
711 | revlog.rev_from_node(NULL_NODE.into()).unwrap(), |
|
766 | revlog.rev_from_node(NULL_NODE.into()).unwrap(), | |
712 | NULL_REVISION |
|
767 | NULL_REVISION | |
713 | ); |
|
768 | ); | |
714 | let null_entry = revlog.get_entry(NULL_REVISION).ok().unwrap(); |
|
769 | let null_entry = revlog.get_entry(NULL_REVISION.into()).ok().unwrap(); | |
715 | assert_eq!(null_entry.revision(), NULL_REVISION); |
|
770 | assert_eq!(null_entry.revision(), NULL_REVISION); | |
716 | assert!(null_entry.data().unwrap().is_empty()); |
|
771 | assert!(null_entry.data().unwrap().is_empty()); | |
717 | } |
|
772 | } | |
@@ -750,7 +805,7 mod tests { | |||||
750 | std::fs::write(temp.path().join("foo.i"), contents).unwrap(); |
|
805 | std::fs::write(temp.path().join("foo.i"), contents).unwrap(); | |
751 | let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap(); |
|
806 | let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap(); | |
752 |
|
807 | |||
753 | let entry0 = revlog.get_entry(0).ok().unwrap(); |
|
808 | let entry0 = revlog.get_entry(0.into()).ok().unwrap(); | |
754 | assert_eq!(entry0.revision(), 0); |
|
809 | assert_eq!(entry0.revision(), 0); | |
755 | assert_eq!(*entry0.node(), node0); |
|
810 | assert_eq!(*entry0.node(), node0); | |
756 | assert!(!entry0.has_p1()); |
|
811 | assert!(!entry0.has_p1()); | |
@@ -761,7 +816,7 mod tests { | |||||
761 | let p2_entry = entry0.p2_entry().unwrap(); |
|
816 | let p2_entry = entry0.p2_entry().unwrap(); | |
762 | assert!(p2_entry.is_none()); |
|
817 | assert!(p2_entry.is_none()); | |
763 |
|
818 | |||
764 | let entry1 = revlog.get_entry(1).ok().unwrap(); |
|
819 | let entry1 = revlog.get_entry(1.into()).ok().unwrap(); | |
765 | assert_eq!(entry1.revision(), 1); |
|
820 | assert_eq!(entry1.revision(), 1); | |
766 | assert_eq!(*entry1.node(), node1); |
|
821 | assert_eq!(*entry1.node(), node1); | |
767 | assert!(!entry1.has_p1()); |
|
822 | assert!(!entry1.has_p1()); | |
@@ -772,7 +827,7 mod tests { | |||||
772 | let p2_entry = entry1.p2_entry().unwrap(); |
|
827 | let p2_entry = entry1.p2_entry().unwrap(); | |
773 | assert!(p2_entry.is_none()); |
|
828 | assert!(p2_entry.is_none()); | |
774 |
|
829 | |||
775 | let entry2 = revlog.get_entry(2).ok().unwrap(); |
|
830 | let entry2 = revlog.get_entry(2.into()).ok().unwrap(); | |
776 | assert_eq!(entry2.revision(), 2); |
|
831 | assert_eq!(entry2.revision(), 2); | |
777 | assert_eq!(*entry2.node(), node2); |
|
832 | assert_eq!(*entry2.node(), node2); | |
778 | assert!(entry2.has_p1()); |
|
833 | assert!(entry2.has_p1()); | |
@@ -817,7 +872,7 mod tests { | |||||
817 | let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap(); |
|
872 | let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap(); | |
818 |
|
873 | |||
819 | // accessing the data shows the corruption |
|
874 | // accessing the data shows the corruption | |
820 | revlog.get_entry(0).unwrap().data().unwrap_err(); |
|
875 | revlog.get_entry(0.into()).unwrap().data().unwrap_err(); | |
821 |
|
876 | |||
822 | assert_eq!(revlog.rev_from_node(NULL_NODE.into()).unwrap(), -1); |
|
877 | assert_eq!(revlog.rev_from_node(NULL_NODE.into()).unwrap(), -1); | |
823 | assert_eq!(revlog.rev_from_node(node0.into()).unwrap(), 0); |
|
878 | assert_eq!(revlog.rev_from_node(node0.into()).unwrap(), 0); |
@@ -12,6 +12,8 | |||||
12 | //! Following existing implicit conventions, the "nodemap" terminology |
|
12 | //! Following existing implicit conventions, the "nodemap" terminology | |
13 | //! is used in a more abstract context. |
|
13 | //! is used in a more abstract context. | |
14 |
|
14 | |||
|
15 | use crate::UncheckedRevision; | |||
|
16 | ||||
15 | use super::{ |
|
17 | use super::{ | |
16 | node::NULL_NODE, Node, NodePrefix, Revision, RevlogIndex, NULL_REVISION, |
|
18 | node::NULL_NODE, Node, NodePrefix, Revision, RevlogIndex, NULL_REVISION, | |
17 | }; |
|
19 | }; | |
@@ -30,7 +32,7 pub enum NodeMapError { | |||||
30 | /// This can be returned by methods meant for (at most) one match. |
|
32 | /// This can be returned by methods meant for (at most) one match. | |
31 | MultipleResults, |
|
33 | MultipleResults, | |
32 | /// A `Revision` stored in the nodemap could not be found in the index |
|
34 | /// A `Revision` stored in the nodemap could not be found in the index | |
33 | RevisionNotInIndex(Revision), |
|
35 | RevisionNotInIndex(UncheckedRevision), | |
34 | } |
|
36 | } | |
35 |
|
37 | |||
36 | /// Mapping system from Mercurial nodes to revision numbers. |
|
38 | /// Mapping system from Mercurial nodes to revision numbers. | |
@@ -125,7 +127,9 type RawElement = unaligned::I32Be; | |||||
125 | /// use. |
|
127 | /// use. | |
126 | #[derive(Clone, Debug, Eq, PartialEq)] |
|
128 | #[derive(Clone, Debug, Eq, PartialEq)] | |
127 | enum Element { |
|
129 | enum Element { | |
128 | Rev(Revision), |
|
130 | // This is not a Mercurial revision. It's a `i32` because this is the | |
|
131 | // right type for this structure. | |||
|
132 | Rev(i32), | |||
129 | Block(usize), |
|
133 | Block(usize), | |
130 | None, |
|
134 | None, | |
131 | } |
|
135 | } | |
@@ -245,17 +249,21 impl Index<usize> for NodeTree { | |||||
245 | fn has_prefix_or_none( |
|
249 | fn has_prefix_or_none( | |
246 | idx: &impl RevlogIndex, |
|
250 | idx: &impl RevlogIndex, | |
247 | prefix: NodePrefix, |
|
251 | prefix: NodePrefix, | |
248 | rev: Revision, |
|
252 | rev: UncheckedRevision, | |
249 | ) -> Result<Option<Revision>, NodeMapError> { |
|
253 | ) -> Result<Option<Revision>, NodeMapError> { | |
250 | idx.node(rev) |
|
254 | match idx.check_revision(rev) { | |
251 | .ok_or(NodeMapError::RevisionNotInIndex(rev)) |
|
255 | Some(checked) => idx | |
252 | .map(|node| { |
|
256 | .node(checked) | |
253 | if prefix.is_prefix_of(node) { |
|
257 | .ok_or(NodeMapError::RevisionNotInIndex(rev)) | |
254 | Some(rev) |
|
258 | .map(|node| { | |
255 | } else { |
|
259 | if prefix.is_prefix_of(node) { | |
256 |
|
|
260 | Some(checked) | |
257 | } |
|
261 | } else { | |
258 | }) |
|
262 | None | |
|
263 | } | |||
|
264 | }), | |||
|
265 | None => Err(NodeMapError::RevisionNotInIndex(rev)), | |||
|
266 | } | |||
259 | } |
|
267 | } | |
260 |
|
268 | |||
261 | /// validate that the candidate's node starts indeed with given prefix, |
|
269 | /// validate that the candidate's node starts indeed with given prefix, | |
@@ -266,7 +274,7 fn has_prefix_or_none( | |||||
266 | fn validate_candidate( |
|
274 | fn validate_candidate( | |
267 | idx: &impl RevlogIndex, |
|
275 | idx: &impl RevlogIndex, | |
268 | prefix: NodePrefix, |
|
276 | prefix: NodePrefix, | |
269 | candidate: (Option<Revision>, usize), |
|
277 | candidate: (Option<UncheckedRevision>, usize), | |
270 | ) -> Result<(Option<Revision>, usize), NodeMapError> { |
|
278 | ) -> Result<(Option<Revision>, usize), NodeMapError> { | |
271 | let (rev, steps) = candidate; |
|
279 | let (rev, steps) = candidate; | |
272 | if let Some(nz_nybble) = prefix.first_different_nybble(&NULL_NODE) { |
|
280 | if let Some(nz_nybble) = prefix.first_different_nybble(&NULL_NODE) { | |
@@ -384,6 +392,8 impl NodeTree { | |||||
384 | /// be inferred from |
|
392 | /// be inferred from | |
385 | /// the `NodeTree` data is that `rev` is the revision with the longest |
|
393 | /// the `NodeTree` data is that `rev` is the revision with the longest | |
386 | /// common node prefix with the given prefix. |
|
394 | /// common node prefix with the given prefix. | |
|
395 | /// We return an [`UncheckedRevision`] because we have no guarantee that | |||
|
396 | /// the revision we found is valid for the index. | |||
387 | /// |
|
397 | /// | |
388 | /// The second returned value is the size of the smallest subprefix |
|
398 | /// The second returned value is the size of the smallest subprefix | |
389 | /// of `prefix` that would give the same result, i.e. not the |
|
399 | /// of `prefix` that would give the same result, i.e. not the | |
@@ -392,7 +402,7 impl NodeTree { | |||||
392 | fn lookup( |
|
402 | fn lookup( | |
393 | &self, |
|
403 | &self, | |
394 | prefix: NodePrefix, |
|
404 | prefix: NodePrefix, | |
395 | ) -> Result<(Option<Revision>, usize), NodeMapError> { |
|
405 | ) -> Result<(Option<UncheckedRevision>, usize), NodeMapError> { | |
396 | for (i, visit_item) in self.visit(prefix).enumerate() { |
|
406 | for (i, visit_item) in self.visit(prefix).enumerate() { | |
397 | if let Some(opt) = visit_item.final_revision() { |
|
407 | if let Some(opt) = visit_item.final_revision() { | |
398 | return Ok((opt, i + 1)); |
|
408 | return Ok((opt, i + 1)); | |
@@ -464,9 +474,9 impl NodeTree { | |||||
464 | self.mutable_block(deepest.block_idx); |
|
474 | self.mutable_block(deepest.block_idx); | |
465 |
|
475 | |||
466 | if let Element::Rev(old_rev) = deepest.element { |
|
476 | if let Element::Rev(old_rev) = deepest.element { | |
467 | let old_node = index |
|
477 | let old_node = index.node(old_rev).ok_or_else(|| { | |
468 | .node(old_rev) |
|
478 | NodeMapError::RevisionNotInIndex(old_rev.into()) | |
469 | .ok_or(NodeMapError::RevisionNotInIndex(old_rev))?; |
|
479 | })?; | |
470 | if old_node == node { |
|
480 | if old_node == node { | |
471 | return Ok(()); // avoid creating lots of useless blocks |
|
481 | return Ok(()); // avoid creating lots of useless blocks | |
472 | } |
|
482 | } | |
@@ -623,13 +633,13 impl<'n> Iterator for NodeTreeVisitor<'n | |||||
623 |
|
633 | |||
624 | impl NodeTreeVisitItem { |
|
634 | impl NodeTreeVisitItem { | |
625 | // Return `Some(opt)` if this item is final, with `opt` being the |
|
635 | // Return `Some(opt)` if this item is final, with `opt` being the | |
626 | // `Revision` that it may represent. |
|
636 | // `UncheckedRevision` that it may represent. | |
627 | // |
|
637 | // | |
628 | // If the item is not terminal, return `None` |
|
638 | // If the item is not terminal, return `None` | |
629 | fn final_revision(&self) -> Option<Option<Revision>> { |
|
639 | fn final_revision(&self) -> Option<Option<UncheckedRevision>> { | |
630 | match self.element { |
|
640 | match self.element { | |
631 | Element::Block(_) => None, |
|
641 | Element::Block(_) => None, | |
632 | Element::Rev(r) => Some(Some(r)), |
|
642 | Element::Rev(r) => Some(Some(r.into())), | |
633 | Element::None => Some(None), |
|
643 | Element::None => Some(None), | |
634 | } |
|
644 | } | |
635 | } |
|
645 | } | |
@@ -733,16 +743,20 mod tests { | |||||
733 | assert_eq!(block.get(4), Element::Rev(1)); |
|
743 | assert_eq!(block.get(4), Element::Rev(1)); | |
734 | } |
|
744 | } | |
735 |
|
745 | |||
736 | type TestIndex = HashMap<Revision, Node>; |
|
746 | type TestIndex = HashMap<UncheckedRevision, Node>; | |
737 |
|
747 | |||
738 | impl RevlogIndex for TestIndex { |
|
748 | impl RevlogIndex for TestIndex { | |
739 | fn node(&self, rev: Revision) -> Option<&Node> { |
|
749 | fn node(&self, rev: Revision) -> Option<&Node> { | |
740 | self.get(&rev) |
|
750 | self.get(&rev.into()) | |
741 | } |
|
751 | } | |
742 |
|
752 | |||
743 | fn len(&self) -> usize { |
|
753 | fn len(&self) -> usize { | |
744 | self.len() |
|
754 | self.len() | |
745 | } |
|
755 | } | |
|
756 | ||||
|
757 | fn check_revision(&self, rev: UncheckedRevision) -> Option<Revision> { | |||
|
758 | self.get(&rev).map(|_| rev.0) | |||
|
759 | } | |||
746 | } |
|
760 | } | |
747 |
|
761 | |||
748 | /// Pad hexadecimal Node prefix with zeros on the right |
|
762 | /// Pad hexadecimal Node prefix with zeros on the right | |
@@ -756,7 +770,7 mod tests { | |||||
756 |
|
770 | |||
757 | /// Pad hexadecimal Node prefix with zeros on the right, then insert |
|
771 | /// Pad hexadecimal Node prefix with zeros on the right, then insert | |
758 | fn pad_insert(idx: &mut TestIndex, rev: Revision, hex: &str) { |
|
772 | fn pad_insert(idx: &mut TestIndex, rev: Revision, hex: &str) { | |
759 | idx.insert(rev, pad_node(hex)); |
|
773 | idx.insert(rev.into(), pad_node(hex)); | |
760 | } |
|
774 | } | |
761 |
|
775 | |||
762 | fn sample_nodetree() -> NodeTree { |
|
776 | fn sample_nodetree() -> NodeTree { | |
@@ -796,7 +810,7 mod tests { | |||||
796 | assert_eq!(nt.find_bin(&idx, hex("ab"))?, None); |
|
810 | assert_eq!(nt.find_bin(&idx, hex("ab"))?, None); | |
797 |
|
811 | |||
798 | // and with full binary Nodes |
|
812 | // and with full binary Nodes | |
799 | assert_eq!(nt.find_node(&idx, idx.get(&1).unwrap())?, Some(1)); |
|
813 | assert_eq!(nt.find_node(&idx, idx.get(&1.into()).unwrap())?, Some(1)); | |
800 | let unknown = Node::from_hex(&hex_pad_right("3d")).unwrap(); |
|
814 | let unknown = Node::from_hex(&hex_pad_right("3d")).unwrap(); | |
801 | assert_eq!(nt.find_node(&idx, &unknown)?, None); |
|
815 | assert_eq!(nt.find_node(&idx, &unknown)?, None); | |
802 | Ok(()) |
|
816 | Ok(()) | |
@@ -857,14 +871,15 mod tests { | |||||
857 | } |
|
871 | } | |
858 | } |
|
872 | } | |
859 |
|
873 | |||
860 | fn insert( |
|
874 | fn insert(&mut self, rev: i32, hex: &str) -> Result<(), NodeMapError> { | |
861 | &mut self, |
|
|||
862 | rev: Revision, |
|
|||
863 | hex: &str, |
|
|||
864 | ) -> Result<(), NodeMapError> { |
|
|||
865 | let node = pad_node(hex); |
|
875 | let node = pad_node(hex); | |
|
876 | let rev: UncheckedRevision = rev.into(); | |||
866 | self.index.insert(rev, node); |
|
877 | self.index.insert(rev, node); | |
867 |
self.nt.insert( |
|
878 | self.nt.insert( | |
|
879 | &self.index, | |||
|
880 | &node, | |||
|
881 | self.index.check_revision(rev).unwrap(), | |||
|
882 | )?; | |||
868 | Ok(()) |
|
883 | Ok(()) | |
869 | } |
|
884 | } | |
870 |
|
885 | |||
@@ -971,9 +986,9 mod tests { | |||||
971 | let node0 = Node::from_hex(&node0_hex).unwrap(); |
|
986 | let node0 = Node::from_hex(&node0_hex).unwrap(); | |
972 | let node1 = Node::from_hex(&node1_hex).unwrap(); |
|
987 | let node1 = Node::from_hex(&node1_hex).unwrap(); | |
973 |
|
988 | |||
974 | idx.insert(0, node0); |
|
989 | idx.insert(0.into(), node0); | |
975 | nt.insert(idx, &node0, 0)?; |
|
990 | nt.insert(idx, &node0, 0)?; | |
976 | idx.insert(1, node1); |
|
991 | idx.insert(1.into(), node1); | |
977 | nt.insert(idx, &node1, 1)?; |
|
992 | nt.insert(idx, &node1, 1)?; | |
978 |
|
993 | |||
979 | assert_eq!(nt.find_bin(idx, (&node0).into())?, Some(0)); |
|
994 | assert_eq!(nt.find_bin(idx, (&node0).into())?, Some(0)); |
@@ -53,7 +53,7 pub fn resolve_rev_number_or_hex_prefix( | |||||
53 | if let Ok(integer) = input.parse::<i32>() { |
|
53 | if let Ok(integer) = input.parse::<i32>() { | |
54 | if integer.to_string() == input |
|
54 | if integer.to_string() == input | |
55 | && integer >= 0 |
|
55 | && integer >= 0 | |
56 | && revlog.has_rev(integer) |
|
56 | && revlog.has_rev(integer.into()) | |
57 | { |
|
57 | { | |
58 | return Ok(integer); |
|
58 | return Ok(integer); | |
59 | } |
|
59 | } |
@@ -18,7 +18,7 use cpython::{ | |||||
18 | use hg::{ |
|
18 | use hg::{ | |
19 | nodemap::{Block, NodeMapError, NodeTree}, |
|
19 | nodemap::{Block, NodeMapError, NodeTree}, | |
20 | revlog::{nodemap::NodeMap, NodePrefix, RevlogIndex}, |
|
20 | revlog::{nodemap::NodeMap, NodePrefix, RevlogIndex}, | |
21 | Revision, |
|
21 | Revision, UncheckedRevision, | |
22 | }; |
|
22 | }; | |
23 | use std::cell::RefCell; |
|
23 | use std::cell::RefCell; | |
24 |
|
24 | |||
@@ -252,7 +252,7 py_class!(pub class MixedIndex |py| { | |||||
252 | // Note that we don't seem to have a direct way to call |
|
252 | // Note that we don't seem to have a direct way to call | |
253 | // PySequence_GetItem (does the job), which would possibly be better |
|
253 | // PySequence_GetItem (does the job), which would possibly be better | |
254 | // for performance |
|
254 | // for performance | |
255 |
let key = match key.extract::< |
|
255 | let key = match key.extract::<i32>(py) { | |
256 | Ok(rev) => rev.to_py_object(py).into_object(), |
|
256 | Ok(rev) => rev.to_py_object(py).into_object(), | |
257 | Err(_) => key, |
|
257 | Err(_) => key, | |
258 | }; |
|
258 | }; | |
@@ -268,7 +268,7 py_class!(pub class MixedIndex |py| { | |||||
268 | // this is an equivalent implementation of the index_contains() |
|
268 | // this is an equivalent implementation of the index_contains() | |
269 | // defined in revlog.c |
|
269 | // defined in revlog.c | |
270 | let cindex = self.cindex(py).borrow(); |
|
270 | let cindex = self.cindex(py).borrow(); | |
271 |
match item.extract::< |
|
271 | match item.extract::<i32>(py) { | |
272 | Ok(rev) => { |
|
272 | Ok(rev) => { | |
273 | Ok(rev >= -1 && rev < cindex.inner().len(py)? as Revision) |
|
273 | Ok(rev >= -1 && rev < cindex.inner().len(py)? as Revision) | |
274 | } |
|
274 | } | |
@@ -448,9 +448,12 impl MixedIndex { | |||||
448 | let mut nt = NodeTree::load_bytes(Box::new(bytes), len); |
|
448 | let mut nt = NodeTree::load_bytes(Box::new(bytes), len); | |
449 |
|
449 | |||
450 | let data_tip = |
|
450 | let data_tip = | |
451 |
docket.getattr(py, "tip_rev")?.extract::< |
|
451 | docket.getattr(py, "tip_rev")?.extract::<i32>(py)?.into(); | |
452 | self.docket(py).borrow_mut().replace(docket.clone_ref(py)); |
|
452 | self.docket(py).borrow_mut().replace(docket.clone_ref(py)); | |
453 | let idx = self.cindex(py).borrow(); |
|
453 | let idx = self.cindex(py).borrow(); | |
|
454 | let data_tip = idx.check_revision(data_tip).ok_or_else(|| { | |||
|
455 | nodemap_error(py, NodeMapError::RevisionNotInIndex(data_tip)) | |||
|
456 | })?; | |||
454 | let current_tip = idx.len(); |
|
457 | let current_tip = idx.len(); | |
455 |
|
458 | |||
456 | for r in (data_tip + 1)..current_tip as Revision { |
|
459 | for r in (data_tip + 1)..current_tip as Revision { | |
@@ -479,7 +482,7 fn revlog_error(py: Python) -> PyErr { | |||||
479 | } |
|
482 | } | |
480 | } |
|
483 | } | |
481 |
|
484 | |||
482 | fn rev_not_in_index(py: Python, rev: Revision) -> PyErr { |
|
485 | fn rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr { | |
483 | PyErr::new::<ValueError, _>( |
|
486 | PyErr::new::<ValueError, _>( | |
484 | py, |
|
487 | py, | |
485 | format!( |
|
488 | format!( |
General Comments 0
You need to be logged in to leave comments.
Login now