##// END OF EJS Templates
rhg: fallback when encountering ellipsis revisions...
Raphaël Gomès -
r50454:7787174f stable
parent child Browse files
Show More
@@ -1,639 +1,644 b''
1 use std::borrow::Cow;
1 use std::borrow::Cow;
2 use std::convert::TryFrom;
2 use std::convert::TryFrom;
3 use std::io::Read;
3 use std::io::Read;
4 use std::ops::Deref;
4 use std::ops::Deref;
5 use std::path::Path;
5 use std::path::Path;
6
6
7 use flate2::read::ZlibDecoder;
7 use flate2::read::ZlibDecoder;
8 use sha1::{Digest, Sha1};
8 use sha1::{Digest, Sha1};
9 use zstd;
9 use zstd;
10
10
11 use super::index::Index;
11 use super::index::Index;
12 use super::node::{NodePrefix, NODE_BYTES_LENGTH, NULL_NODE};
12 use super::node::{NodePrefix, NODE_BYTES_LENGTH, NULL_NODE};
13 use super::nodemap;
13 use super::nodemap;
14 use super::nodemap::{NodeMap, NodeMapError};
14 use super::nodemap::{NodeMap, NodeMapError};
15 use super::nodemap_docket::NodeMapDocket;
15 use super::nodemap_docket::NodeMapDocket;
16 use super::patch;
16 use super::patch;
17 use crate::errors::HgError;
17 use crate::errors::HgError;
18 use crate::revlog::Revision;
18 use crate::revlog::Revision;
19 use crate::vfs::Vfs;
19 use crate::vfs::Vfs;
20 use crate::{Node, NULL_REVISION};
20 use crate::{Node, NULL_REVISION};
21
21
22 const REVISION_FLAG_CENSORED: u16 = 1 << 15;
22 const REVISION_FLAG_CENSORED: u16 = 1 << 15;
23 const REVISION_FLAG_ELLIPSIS: u16 = 1 << 14;
23 const REVISION_FLAG_ELLIPSIS: u16 = 1 << 14;
24 const REVISION_FLAG_EXTSTORED: u16 = 1 << 13;
24 const REVISION_FLAG_EXTSTORED: u16 = 1 << 13;
25 const REVISION_FLAG_HASCOPIESINFO: u16 = 1 << 12;
25 const REVISION_FLAG_HASCOPIESINFO: u16 = 1 << 12;
26
26
27 // Keep this in sync with REVIDX_KNOWN_FLAGS in
27 // Keep this in sync with REVIDX_KNOWN_FLAGS in
28 // mercurial/revlogutils/flagutil.py
28 // mercurial/revlogutils/flagutil.py
29 const REVIDX_KNOWN_FLAGS: u16 = REVISION_FLAG_CENSORED
29 const REVIDX_KNOWN_FLAGS: u16 = REVISION_FLAG_CENSORED
30 | REVISION_FLAG_ELLIPSIS
30 | REVISION_FLAG_ELLIPSIS
31 | REVISION_FLAG_EXTSTORED
31 | REVISION_FLAG_EXTSTORED
32 | REVISION_FLAG_HASCOPIESINFO;
32 | REVISION_FLAG_HASCOPIESINFO;
33
33
34 const NULL_REVLOG_ENTRY_FLAGS: u16 = 0;
34 const NULL_REVLOG_ENTRY_FLAGS: u16 = 0;
35
35
36 #[derive(Debug, derive_more::From)]
36 #[derive(Debug, derive_more::From)]
37 pub enum RevlogError {
37 pub enum RevlogError {
38 InvalidRevision,
38 InvalidRevision,
39 /// Working directory is not supported
39 /// Working directory is not supported
40 WDirUnsupported,
40 WDirUnsupported,
41 /// Found more than one entry whose ID match the requested prefix
41 /// Found more than one entry whose ID match the requested prefix
42 AmbiguousPrefix,
42 AmbiguousPrefix,
43 #[from]
43 #[from]
44 Other(HgError),
44 Other(HgError),
45 }
45 }
46
46
47 impl From<NodeMapError> for RevlogError {
47 impl From<NodeMapError> for RevlogError {
48 fn from(error: NodeMapError) -> Self {
48 fn from(error: NodeMapError) -> Self {
49 match error {
49 match error {
50 NodeMapError::MultipleResults => RevlogError::AmbiguousPrefix,
50 NodeMapError::MultipleResults => RevlogError::AmbiguousPrefix,
51 NodeMapError::RevisionNotInIndex(rev) => RevlogError::corrupted(
51 NodeMapError::RevisionNotInIndex(rev) => RevlogError::corrupted(
52 format!("nodemap point to revision {} not in index", rev),
52 format!("nodemap point to revision {} not in index", rev),
53 ),
53 ),
54 }
54 }
55 }
55 }
56 }
56 }
57
57
58 fn corrupted<S: AsRef<str>>(context: S) -> HgError {
58 fn corrupted<S: AsRef<str>>(context: S) -> HgError {
59 HgError::corrupted(format!("corrupted revlog, {}", context.as_ref()))
59 HgError::corrupted(format!("corrupted revlog, {}", context.as_ref()))
60 }
60 }
61
61
62 impl RevlogError {
62 impl RevlogError {
63 fn corrupted<S: AsRef<str>>(context: S) -> Self {
63 fn corrupted<S: AsRef<str>>(context: S) -> Self {
64 RevlogError::Other(corrupted(context))
64 RevlogError::Other(corrupted(context))
65 }
65 }
66 }
66 }
67
67
68 /// Read only implementation of revlog.
68 /// Read only implementation of revlog.
69 pub struct Revlog {
69 pub struct Revlog {
70 /// When index and data are not interleaved: bytes of the revlog index.
70 /// When index and data are not interleaved: bytes of the revlog index.
71 /// When index and data are interleaved: bytes of the revlog index and
71 /// When index and data are interleaved: bytes of the revlog index and
72 /// data.
72 /// data.
73 index: Index,
73 index: Index,
74 /// When index and data are not interleaved: bytes of the revlog data
74 /// When index and data are not interleaved: bytes of the revlog data
75 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
75 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
76 /// When present on disk: the persistent nodemap for this revlog
76 /// When present on disk: the persistent nodemap for this revlog
77 nodemap: Option<nodemap::NodeTree>,
77 nodemap: Option<nodemap::NodeTree>,
78 }
78 }
79
79
80 impl Revlog {
80 impl Revlog {
81 /// Open a revlog index file.
81 /// Open a revlog index file.
82 ///
82 ///
83 /// It will also open the associated data file if index and data are not
83 /// It will also open the associated data file if index and data are not
84 /// interleaved.
84 /// interleaved.
85 pub fn open(
85 pub fn open(
86 store_vfs: &Vfs,
86 store_vfs: &Vfs,
87 index_path: impl AsRef<Path>,
87 index_path: impl AsRef<Path>,
88 data_path: Option<&Path>,
88 data_path: Option<&Path>,
89 use_nodemap: bool,
89 use_nodemap: bool,
90 ) -> Result<Self, HgError> {
90 ) -> Result<Self, HgError> {
91 let index_path = index_path.as_ref();
91 let index_path = index_path.as_ref();
92 let index = {
92 let index = {
93 match store_vfs.mmap_open_opt(&index_path)? {
93 match store_vfs.mmap_open_opt(&index_path)? {
94 None => Index::new(Box::new(vec![])),
94 None => Index::new(Box::new(vec![])),
95 Some(index_mmap) => {
95 Some(index_mmap) => {
96 let index = Index::new(Box::new(index_mmap))?;
96 let index = Index::new(Box::new(index_mmap))?;
97 Ok(index)
97 Ok(index)
98 }
98 }
99 }
99 }
100 }?;
100 }?;
101
101
102 let default_data_path = index_path.with_extension("d");
102 let default_data_path = index_path.with_extension("d");
103
103
104 // type annotation required
104 // type annotation required
105 // won't recognize Mmap as Deref<Target = [u8]>
105 // won't recognize Mmap as Deref<Target = [u8]>
106 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
106 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
107 if index.is_inline() {
107 if index.is_inline() {
108 None
108 None
109 } else {
109 } else {
110 let data_path = data_path.unwrap_or(&default_data_path);
110 let data_path = data_path.unwrap_or(&default_data_path);
111 let data_mmap = store_vfs.mmap_open(data_path)?;
111 let data_mmap = store_vfs.mmap_open(data_path)?;
112 Some(Box::new(data_mmap))
112 Some(Box::new(data_mmap))
113 };
113 };
114
114
115 let nodemap = if index.is_inline() {
115 let nodemap = if index.is_inline() {
116 None
116 None
117 } else if !use_nodemap {
117 } else if !use_nodemap {
118 None
118 None
119 } else {
119 } else {
120 NodeMapDocket::read_from_file(store_vfs, index_path)?.map(
120 NodeMapDocket::read_from_file(store_vfs, index_path)?.map(
121 |(docket, data)| {
121 |(docket, data)| {
122 nodemap::NodeTree::load_bytes(
122 nodemap::NodeTree::load_bytes(
123 Box::new(data),
123 Box::new(data),
124 docket.data_length,
124 docket.data_length,
125 )
125 )
126 },
126 },
127 )
127 )
128 };
128 };
129
129
130 Ok(Revlog {
130 Ok(Revlog {
131 index,
131 index,
132 data_bytes,
132 data_bytes,
133 nodemap,
133 nodemap,
134 })
134 })
135 }
135 }
136
136
137 /// Return number of entries of the `Revlog`.
137 /// Return number of entries of the `Revlog`.
138 pub fn len(&self) -> usize {
138 pub fn len(&self) -> usize {
139 self.index.len()
139 self.index.len()
140 }
140 }
141
141
142 /// Returns `true` if the `Revlog` has zero `entries`.
142 /// Returns `true` if the `Revlog` has zero `entries`.
143 pub fn is_empty(&self) -> bool {
143 pub fn is_empty(&self) -> bool {
144 self.index.is_empty()
144 self.index.is_empty()
145 }
145 }
146
146
147 /// Returns the node ID for the given revision number, if it exists in this
147 /// Returns the node ID for the given revision number, if it exists in this
148 /// revlog
148 /// revlog
149 pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> {
149 pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> {
150 if rev == NULL_REVISION {
150 if rev == NULL_REVISION {
151 return Some(&NULL_NODE);
151 return Some(&NULL_NODE);
152 }
152 }
153 Some(self.index.get_entry(rev)?.hash())
153 Some(self.index.get_entry(rev)?.hash())
154 }
154 }
155
155
156 /// Return the revision number for the given node ID, if it exists in this
156 /// Return the revision number for the given node ID, if it exists in this
157 /// revlog
157 /// revlog
158 pub fn rev_from_node(
158 pub fn rev_from_node(
159 &self,
159 &self,
160 node: NodePrefix,
160 node: NodePrefix,
161 ) -> Result<Revision, RevlogError> {
161 ) -> Result<Revision, RevlogError> {
162 if node.is_prefix_of(&NULL_NODE) {
162 if node.is_prefix_of(&NULL_NODE) {
163 return Ok(NULL_REVISION);
163 return Ok(NULL_REVISION);
164 }
164 }
165
165
166 if let Some(nodemap) = &self.nodemap {
166 if let Some(nodemap) = &self.nodemap {
167 return nodemap
167 return nodemap
168 .find_bin(&self.index, node)?
168 .find_bin(&self.index, node)?
169 .ok_or(RevlogError::InvalidRevision);
169 .ok_or(RevlogError::InvalidRevision);
170 }
170 }
171
171
172 // Fallback to linear scan when a persistent nodemap is not present.
172 // Fallback to linear scan when a persistent nodemap is not present.
173 // This happens when the persistent-nodemap experimental feature is not
173 // This happens when the persistent-nodemap experimental feature is not
174 // enabled, or for small revlogs.
174 // enabled, or for small revlogs.
175 //
175 //
176 // TODO: consider building a non-persistent nodemap in memory to
176 // TODO: consider building a non-persistent nodemap in memory to
177 // optimize these cases.
177 // optimize these cases.
178 let mut found_by_prefix = None;
178 let mut found_by_prefix = None;
179 for rev in (0..self.len() as Revision).rev() {
179 for rev in (0..self.len() as Revision).rev() {
180 let index_entry =
180 let index_entry =
181 self.index.get_entry(rev).ok_or(HgError::corrupted(
181 self.index.get_entry(rev).ok_or(HgError::corrupted(
182 "revlog references a revision not in the index",
182 "revlog references a revision not in the index",
183 ))?;
183 ))?;
184 if node == *index_entry.hash() {
184 if node == *index_entry.hash() {
185 return Ok(rev);
185 return Ok(rev);
186 }
186 }
187 if node.is_prefix_of(index_entry.hash()) {
187 if node.is_prefix_of(index_entry.hash()) {
188 if found_by_prefix.is_some() {
188 if found_by_prefix.is_some() {
189 return Err(RevlogError::AmbiguousPrefix);
189 return Err(RevlogError::AmbiguousPrefix);
190 }
190 }
191 found_by_prefix = Some(rev)
191 found_by_prefix = Some(rev)
192 }
192 }
193 }
193 }
194 found_by_prefix.ok_or(RevlogError::InvalidRevision)
194 found_by_prefix.ok_or(RevlogError::InvalidRevision)
195 }
195 }
196
196
197 /// Returns whether the given revision exists in this revlog.
197 /// Returns whether the given revision exists in this revlog.
198 pub fn has_rev(&self, rev: Revision) -> bool {
198 pub fn has_rev(&self, rev: Revision) -> bool {
199 self.index.get_entry(rev).is_some()
199 self.index.get_entry(rev).is_some()
200 }
200 }
201
201
202 /// Return the full data associated to a revision.
202 /// Return the full data associated to a revision.
203 ///
203 ///
204 /// All entries required to build the final data out of deltas will be
204 /// All entries required to build the final data out of deltas will be
205 /// retrieved as needed, and the deltas will be applied to the inital
205 /// retrieved as needed, and the deltas will be applied to the inital
206 /// snapshot to rebuild the final data.
206 /// snapshot to rebuild the final data.
207 pub fn get_rev_data(
207 pub fn get_rev_data(
208 &self,
208 &self,
209 rev: Revision,
209 rev: Revision,
210 ) -> Result<Cow<[u8]>, RevlogError> {
210 ) -> Result<Cow<[u8]>, RevlogError> {
211 if rev == NULL_REVISION {
211 if rev == NULL_REVISION {
212 return Ok(Cow::Borrowed(&[]));
212 return Ok(Cow::Borrowed(&[]));
213 };
213 };
214 Ok(self.get_entry(rev)?.data()?)
214 Ok(self.get_entry(rev)?.data()?)
215 }
215 }
216
216
217 /// Check the hash of some given data against the recorded hash.
217 /// Check the hash of some given data against the recorded hash.
218 pub fn check_hash(
218 pub fn check_hash(
219 &self,
219 &self,
220 p1: Revision,
220 p1: Revision,
221 p2: Revision,
221 p2: Revision,
222 expected: &[u8],
222 expected: &[u8],
223 data: &[u8],
223 data: &[u8],
224 ) -> bool {
224 ) -> bool {
225 let e1 = self.index.get_entry(p1);
225 let e1 = self.index.get_entry(p1);
226 let h1 = match e1 {
226 let h1 = match e1 {
227 Some(ref entry) => entry.hash(),
227 Some(ref entry) => entry.hash(),
228 None => &NULL_NODE,
228 None => &NULL_NODE,
229 };
229 };
230 let e2 = self.index.get_entry(p2);
230 let e2 = self.index.get_entry(p2);
231 let h2 = match e2 {
231 let h2 = match e2 {
232 Some(ref entry) => entry.hash(),
232 Some(ref entry) => entry.hash(),
233 None => &NULL_NODE,
233 None => &NULL_NODE,
234 };
234 };
235
235
236 &hash(data, h1.as_bytes(), h2.as_bytes()) == expected
236 &hash(data, h1.as_bytes(), h2.as_bytes()) == expected
237 }
237 }
238
238
239 /// Build the full data of a revision out its snapshot
239 /// Build the full data of a revision out its snapshot
240 /// and its deltas.
240 /// and its deltas.
241 fn build_data_from_deltas(
241 fn build_data_from_deltas(
242 snapshot: RevlogEntry,
242 snapshot: RevlogEntry,
243 deltas: &[RevlogEntry],
243 deltas: &[RevlogEntry],
244 ) -> Result<Vec<u8>, HgError> {
244 ) -> Result<Vec<u8>, HgError> {
245 let snapshot = snapshot.data_chunk()?;
245 let snapshot = snapshot.data_chunk()?;
246 let deltas = deltas
246 let deltas = deltas
247 .iter()
247 .iter()
248 .rev()
248 .rev()
249 .map(RevlogEntry::data_chunk)
249 .map(RevlogEntry::data_chunk)
250 .collect::<Result<Vec<_>, _>>()?;
250 .collect::<Result<Vec<_>, _>>()?;
251 let patches: Vec<_> =
251 let patches: Vec<_> =
252 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
252 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
253 let patch = patch::fold_patch_lists(&patches);
253 let patch = patch::fold_patch_lists(&patches);
254 Ok(patch.apply(&snapshot))
254 Ok(patch.apply(&snapshot))
255 }
255 }
256
256
257 /// Return the revlog data.
257 /// Return the revlog data.
258 fn data(&self) -> &[u8] {
258 fn data(&self) -> &[u8] {
259 match self.data_bytes {
259 match self.data_bytes {
260 Some(ref data_bytes) => &data_bytes,
260 Some(ref data_bytes) => &data_bytes,
261 None => panic!(
261 None => panic!(
262 "forgot to load the data or trying to access inline data"
262 "forgot to load the data or trying to access inline data"
263 ),
263 ),
264 }
264 }
265 }
265 }
266
266
267 pub fn make_null_entry(&self) -> RevlogEntry {
267 pub fn make_null_entry(&self) -> RevlogEntry {
268 RevlogEntry {
268 RevlogEntry {
269 revlog: self,
269 revlog: self,
270 rev: NULL_REVISION,
270 rev: NULL_REVISION,
271 bytes: b"",
271 bytes: b"",
272 compressed_len: 0,
272 compressed_len: 0,
273 uncompressed_len: 0,
273 uncompressed_len: 0,
274 base_rev_or_base_of_delta_chain: None,
274 base_rev_or_base_of_delta_chain: None,
275 p1: NULL_REVISION,
275 p1: NULL_REVISION,
276 p2: NULL_REVISION,
276 p2: NULL_REVISION,
277 flags: NULL_REVLOG_ENTRY_FLAGS,
277 flags: NULL_REVLOG_ENTRY_FLAGS,
278 hash: NULL_NODE,
278 hash: NULL_NODE,
279 }
279 }
280 }
280 }
281
281
282 /// Get an entry of the revlog.
282 /// Get an entry of the revlog.
283 pub fn get_entry(
283 pub fn get_entry(
284 &self,
284 &self,
285 rev: Revision,
285 rev: Revision,
286 ) -> Result<RevlogEntry, RevlogError> {
286 ) -> Result<RevlogEntry, RevlogError> {
287 if rev == NULL_REVISION {
287 if rev == NULL_REVISION {
288 return Ok(self.make_null_entry());
288 return Ok(self.make_null_entry());
289 }
289 }
290 let index_entry = self
290 let index_entry = self
291 .index
291 .index
292 .get_entry(rev)
292 .get_entry(rev)
293 .ok_or(RevlogError::InvalidRevision)?;
293 .ok_or(RevlogError::InvalidRevision)?;
294 let start = index_entry.offset();
294 let start = index_entry.offset();
295 let end = start + index_entry.compressed_len() as usize;
295 let end = start + index_entry.compressed_len() as usize;
296 let data = if self.index.is_inline() {
296 let data = if self.index.is_inline() {
297 self.index.data(start, end)
297 self.index.data(start, end)
298 } else {
298 } else {
299 &self.data()[start..end]
299 &self.data()[start..end]
300 };
300 };
301 let entry = RevlogEntry {
301 let entry = RevlogEntry {
302 revlog: self,
302 revlog: self,
303 rev,
303 rev,
304 bytes: data,
304 bytes: data,
305 compressed_len: index_entry.compressed_len(),
305 compressed_len: index_entry.compressed_len(),
306 uncompressed_len: index_entry.uncompressed_len(),
306 uncompressed_len: index_entry.uncompressed_len(),
307 base_rev_or_base_of_delta_chain: if index_entry
307 base_rev_or_base_of_delta_chain: if index_entry
308 .base_revision_or_base_of_delta_chain()
308 .base_revision_or_base_of_delta_chain()
309 == rev
309 == rev
310 {
310 {
311 None
311 None
312 } else {
312 } else {
313 Some(index_entry.base_revision_or_base_of_delta_chain())
313 Some(index_entry.base_revision_or_base_of_delta_chain())
314 },
314 },
315 p1: index_entry.p1(),
315 p1: index_entry.p1(),
316 p2: index_entry.p2(),
316 p2: index_entry.p2(),
317 flags: index_entry.flags(),
317 flags: index_entry.flags(),
318 hash: *index_entry.hash(),
318 hash: *index_entry.hash(),
319 };
319 };
320 Ok(entry)
320 Ok(entry)
321 }
321 }
322
322
323 /// when resolving internal references within revlog, any errors
323 /// when resolving internal references within revlog, any errors
324 /// should be reported as corruption, instead of e.g. "invalid revision"
324 /// should be reported as corruption, instead of e.g. "invalid revision"
325 fn get_entry_internal(
325 fn get_entry_internal(
326 &self,
326 &self,
327 rev: Revision,
327 rev: Revision,
328 ) -> Result<RevlogEntry, HgError> {
328 ) -> Result<RevlogEntry, HgError> {
329 self.get_entry(rev)
329 self.get_entry(rev)
330 .map_err(|_| corrupted(format!("revision {} out of range", rev)))
330 .map_err(|_| corrupted(format!("revision {} out of range", rev)))
331 }
331 }
332 }
332 }
333
333
334 /// The revlog entry's bytes and the necessary informations to extract
334 /// The revlog entry's bytes and the necessary informations to extract
335 /// the entry's data.
335 /// the entry's data.
336 #[derive(Clone)]
336 #[derive(Clone)]
337 pub struct RevlogEntry<'a> {
337 pub struct RevlogEntry<'a> {
338 revlog: &'a Revlog,
338 revlog: &'a Revlog,
339 rev: Revision,
339 rev: Revision,
340 bytes: &'a [u8],
340 bytes: &'a [u8],
341 compressed_len: u32,
341 compressed_len: u32,
342 uncompressed_len: i32,
342 uncompressed_len: i32,
343 base_rev_or_base_of_delta_chain: Option<Revision>,
343 base_rev_or_base_of_delta_chain: Option<Revision>,
344 p1: Revision,
344 p1: Revision,
345 p2: Revision,
345 p2: Revision,
346 flags: u16,
346 flags: u16,
347 hash: Node,
347 hash: Node,
348 }
348 }
349
349
350 impl<'a> RevlogEntry<'a> {
350 impl<'a> RevlogEntry<'a> {
351 pub fn revision(&self) -> Revision {
351 pub fn revision(&self) -> Revision {
352 self.rev
352 self.rev
353 }
353 }
354
354
355 pub fn node(&self) -> &Node {
355 pub fn node(&self) -> &Node {
356 &self.hash
356 &self.hash
357 }
357 }
358
358
359 pub fn uncompressed_len(&self) -> Option<u32> {
359 pub fn uncompressed_len(&self) -> Option<u32> {
360 u32::try_from(self.uncompressed_len).ok()
360 u32::try_from(self.uncompressed_len).ok()
361 }
361 }
362
362
363 pub fn has_p1(&self) -> bool {
363 pub fn has_p1(&self) -> bool {
364 self.p1 != NULL_REVISION
364 self.p1 != NULL_REVISION
365 }
365 }
366
366
367 pub fn p1_entry(&self) -> Result<Option<RevlogEntry>, RevlogError> {
367 pub fn p1_entry(&self) -> Result<Option<RevlogEntry>, RevlogError> {
368 if self.p1 == NULL_REVISION {
368 if self.p1 == NULL_REVISION {
369 Ok(None)
369 Ok(None)
370 } else {
370 } else {
371 Ok(Some(self.revlog.get_entry(self.p1)?))
371 Ok(Some(self.revlog.get_entry(self.p1)?))
372 }
372 }
373 }
373 }
374
374
375 pub fn p2_entry(&self) -> Result<Option<RevlogEntry>, RevlogError> {
375 pub fn p2_entry(&self) -> Result<Option<RevlogEntry>, RevlogError> {
376 if self.p2 == NULL_REVISION {
376 if self.p2 == NULL_REVISION {
377 Ok(None)
377 Ok(None)
378 } else {
378 } else {
379 Ok(Some(self.revlog.get_entry(self.p2)?))
379 Ok(Some(self.revlog.get_entry(self.p2)?))
380 }
380 }
381 }
381 }
382
382
383 pub fn p1(&self) -> Option<Revision> {
383 pub fn p1(&self) -> Option<Revision> {
384 if self.p1 == NULL_REVISION {
384 if self.p1 == NULL_REVISION {
385 None
385 None
386 } else {
386 } else {
387 Some(self.p1)
387 Some(self.p1)
388 }
388 }
389 }
389 }
390
390
391 pub fn p2(&self) -> Option<Revision> {
391 pub fn p2(&self) -> Option<Revision> {
392 if self.p2 == NULL_REVISION {
392 if self.p2 == NULL_REVISION {
393 None
393 None
394 } else {
394 } else {
395 Some(self.p2)
395 Some(self.p2)
396 }
396 }
397 }
397 }
398
398
399 pub fn is_censored(&self) -> bool {
399 pub fn is_censored(&self) -> bool {
400 (self.flags & REVISION_FLAG_CENSORED) != 0
400 (self.flags & REVISION_FLAG_CENSORED) != 0
401 }
401 }
402
402
403 pub fn has_length_affecting_flag_processor(&self) -> bool {
403 pub fn has_length_affecting_flag_processor(&self) -> bool {
404 // Relevant Python code: revlog.size()
404 // Relevant Python code: revlog.size()
405 // note: ELLIPSIS is known to not change the content
405 // note: ELLIPSIS is known to not change the content
406 (self.flags & (REVIDX_KNOWN_FLAGS ^ REVISION_FLAG_ELLIPSIS)) != 0
406 (self.flags & (REVIDX_KNOWN_FLAGS ^ REVISION_FLAG_ELLIPSIS)) != 0
407 }
407 }
408
408
409 /// The data for this entry, after resolving deltas if any.
409 /// The data for this entry, after resolving deltas if any.
410 pub fn rawdata(&self) -> Result<Cow<'a, [u8]>, HgError> {
410 pub fn rawdata(&self) -> Result<Cow<'a, [u8]>, HgError> {
411 let mut entry = self.clone();
411 let mut entry = self.clone();
412 let mut delta_chain = vec![];
412 let mut delta_chain = vec![];
413
413
414 // The meaning of `base_rev_or_base_of_delta_chain` depends on
414 // The meaning of `base_rev_or_base_of_delta_chain` depends on
415 // generaldelta. See the doc on `ENTRY_DELTA_BASE` in
415 // generaldelta. See the doc on `ENTRY_DELTA_BASE` in
416 // `mercurial/revlogutils/constants.py` and the code in
416 // `mercurial/revlogutils/constants.py` and the code in
417 // [_chaininfo] and in [index_deltachain].
417 // [_chaininfo] and in [index_deltachain].
418 let uses_generaldelta = self.revlog.index.uses_generaldelta();
418 let uses_generaldelta = self.revlog.index.uses_generaldelta();
419 while let Some(base_rev) = entry.base_rev_or_base_of_delta_chain {
419 while let Some(base_rev) = entry.base_rev_or_base_of_delta_chain {
420 let base_rev = if uses_generaldelta {
420 let base_rev = if uses_generaldelta {
421 base_rev
421 base_rev
422 } else {
422 } else {
423 entry.rev - 1
423 entry.rev - 1
424 };
424 };
425 delta_chain.push(entry);
425 delta_chain.push(entry);
426 entry = self.revlog.get_entry_internal(base_rev)?;
426 entry = self.revlog.get_entry_internal(base_rev)?;
427 }
427 }
428
428
429 let data = if delta_chain.is_empty() {
429 let data = if delta_chain.is_empty() {
430 entry.data_chunk()?
430 entry.data_chunk()?
431 } else {
431 } else {
432 Revlog::build_data_from_deltas(entry, &delta_chain)?.into()
432 Revlog::build_data_from_deltas(entry, &delta_chain)?.into()
433 };
433 };
434
434
435 Ok(data)
435 Ok(data)
436 }
436 }
437
437
438 fn check_data(
438 fn check_data(
439 &self,
439 &self,
440 data: Cow<'a, [u8]>,
440 data: Cow<'a, [u8]>,
441 ) -> Result<Cow<'a, [u8]>, HgError> {
441 ) -> Result<Cow<'a, [u8]>, HgError> {
442 if self.revlog.check_hash(
442 if self.revlog.check_hash(
443 self.p1,
443 self.p1,
444 self.p2,
444 self.p2,
445 self.hash.as_bytes(),
445 self.hash.as_bytes(),
446 &data,
446 &data,
447 ) {
447 ) {
448 Ok(data)
448 Ok(data)
449 } else {
449 } else {
450 if (self.flags & REVISION_FLAG_ELLIPSIS) != 0 {
451 return Err(HgError::unsupported(
452 "ellipsis revisions are not supported by rhg",
453 ));
454 }
450 Err(corrupted(format!(
455 Err(corrupted(format!(
451 "hash check failed for revision {}",
456 "hash check failed for revision {}",
452 self.rev
457 self.rev
453 )))
458 )))
454 }
459 }
455 }
460 }
456
461
457 pub fn data(&self) -> Result<Cow<'a, [u8]>, HgError> {
462 pub fn data(&self) -> Result<Cow<'a, [u8]>, HgError> {
458 let data = self.rawdata()?;
463 let data = self.rawdata()?;
459 if self.is_censored() {
464 if self.is_censored() {
460 return Err(HgError::CensoredNodeError);
465 return Err(HgError::CensoredNodeError);
461 }
466 }
462 self.check_data(data)
467 self.check_data(data)
463 }
468 }
464
469
465 /// Extract the data contained in the entry.
470 /// Extract the data contained in the entry.
466 /// This may be a delta. (See `is_delta`.)
471 /// This may be a delta. (See `is_delta`.)
467 fn data_chunk(&self) -> Result<Cow<'a, [u8]>, HgError> {
472 fn data_chunk(&self) -> Result<Cow<'a, [u8]>, HgError> {
468 if self.bytes.is_empty() {
473 if self.bytes.is_empty() {
469 return Ok(Cow::Borrowed(&[]));
474 return Ok(Cow::Borrowed(&[]));
470 }
475 }
471 match self.bytes[0] {
476 match self.bytes[0] {
472 // Revision data is the entirety of the entry, including this
477 // Revision data is the entirety of the entry, including this
473 // header.
478 // header.
474 b'\0' => Ok(Cow::Borrowed(self.bytes)),
479 b'\0' => Ok(Cow::Borrowed(self.bytes)),
475 // Raw revision data follows.
480 // Raw revision data follows.
476 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
481 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
477 // zlib (RFC 1950) data.
482 // zlib (RFC 1950) data.
478 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
483 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
479 // zstd data.
484 // zstd data.
480 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
485 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
481 // A proper new format should have had a repo/store requirement.
486 // A proper new format should have had a repo/store requirement.
482 format_type => Err(corrupted(format!(
487 format_type => Err(corrupted(format!(
483 "unknown compression header '{}'",
488 "unknown compression header '{}'",
484 format_type
489 format_type
485 ))),
490 ))),
486 }
491 }
487 }
492 }
488
493
489 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, HgError> {
494 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, HgError> {
490 let mut decoder = ZlibDecoder::new(self.bytes);
495 let mut decoder = ZlibDecoder::new(self.bytes);
491 if self.is_delta() {
496 if self.is_delta() {
492 let mut buf = Vec::with_capacity(self.compressed_len as usize);
497 let mut buf = Vec::with_capacity(self.compressed_len as usize);
493 decoder
498 decoder
494 .read_to_end(&mut buf)
499 .read_to_end(&mut buf)
495 .map_err(|e| corrupted(e.to_string()))?;
500 .map_err(|e| corrupted(e.to_string()))?;
496 Ok(buf)
501 Ok(buf)
497 } else {
502 } else {
498 let cap = self.uncompressed_len.max(0) as usize;
503 let cap = self.uncompressed_len.max(0) as usize;
499 let mut buf = vec![0; cap];
504 let mut buf = vec![0; cap];
500 decoder
505 decoder
501 .read_exact(&mut buf)
506 .read_exact(&mut buf)
502 .map_err(|e| corrupted(e.to_string()))?;
507 .map_err(|e| corrupted(e.to_string()))?;
503 Ok(buf)
508 Ok(buf)
504 }
509 }
505 }
510 }
506
511
507 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, HgError> {
512 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, HgError> {
508 if self.is_delta() {
513 if self.is_delta() {
509 let mut buf = Vec::with_capacity(self.compressed_len as usize);
514 let mut buf = Vec::with_capacity(self.compressed_len as usize);
510 zstd::stream::copy_decode(self.bytes, &mut buf)
515 zstd::stream::copy_decode(self.bytes, &mut buf)
511 .map_err(|e| corrupted(e.to_string()))?;
516 .map_err(|e| corrupted(e.to_string()))?;
512 Ok(buf)
517 Ok(buf)
513 } else {
518 } else {
514 let cap = self.uncompressed_len.max(0) as usize;
519 let cap = self.uncompressed_len.max(0) as usize;
515 let mut buf = vec![0; cap];
520 let mut buf = vec![0; cap];
516 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
521 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
517 .map_err(|e| corrupted(e.to_string()))?;
522 .map_err(|e| corrupted(e.to_string()))?;
518 if len != self.uncompressed_len as usize {
523 if len != self.uncompressed_len as usize {
519 Err(corrupted("uncompressed length does not match"))
524 Err(corrupted("uncompressed length does not match"))
520 } else {
525 } else {
521 Ok(buf)
526 Ok(buf)
522 }
527 }
523 }
528 }
524 }
529 }
525
530
526 /// Tell if the entry is a snapshot or a delta
531 /// Tell if the entry is a snapshot or a delta
527 /// (influences on decompression).
532 /// (influences on decompression).
528 fn is_delta(&self) -> bool {
533 fn is_delta(&self) -> bool {
529 self.base_rev_or_base_of_delta_chain.is_some()
534 self.base_rev_or_base_of_delta_chain.is_some()
530 }
535 }
531 }
536 }
532
537
533 /// Calculate the hash of a revision given its data and its parents.
538 /// Calculate the hash of a revision given its data and its parents.
534 fn hash(
539 fn hash(
535 data: &[u8],
540 data: &[u8],
536 p1_hash: &[u8],
541 p1_hash: &[u8],
537 p2_hash: &[u8],
542 p2_hash: &[u8],
538 ) -> [u8; NODE_BYTES_LENGTH] {
543 ) -> [u8; NODE_BYTES_LENGTH] {
539 let mut hasher = Sha1::new();
544 let mut hasher = Sha1::new();
540 let (a, b) = (p1_hash, p2_hash);
545 let (a, b) = (p1_hash, p2_hash);
541 if a > b {
546 if a > b {
542 hasher.update(b);
547 hasher.update(b);
543 hasher.update(a);
548 hasher.update(a);
544 } else {
549 } else {
545 hasher.update(a);
550 hasher.update(a);
546 hasher.update(b);
551 hasher.update(b);
547 }
552 }
548 hasher.update(data);
553 hasher.update(data);
549 *hasher.finalize().as_ref()
554 *hasher.finalize().as_ref()
550 }
555 }
551
556
552 #[cfg(test)]
557 #[cfg(test)]
553 mod tests {
558 mod tests {
554 use super::*;
559 use super::*;
555 use crate::index::{IndexEntryBuilder, INDEX_ENTRY_SIZE};
560 use crate::index::{IndexEntryBuilder, INDEX_ENTRY_SIZE};
556 use itertools::Itertools;
561 use itertools::Itertools;
557
562
558 #[test]
563 #[test]
559 fn test_empty() {
564 fn test_empty() {
560 let temp = tempfile::tempdir().unwrap();
565 let temp = tempfile::tempdir().unwrap();
561 let vfs = Vfs { base: temp.path() };
566 let vfs = Vfs { base: temp.path() };
562 std::fs::write(temp.path().join("foo.i"), b"").unwrap();
567 std::fs::write(temp.path().join("foo.i"), b"").unwrap();
563 let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap();
568 let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap();
564 assert!(revlog.is_empty());
569 assert!(revlog.is_empty());
565 assert_eq!(revlog.len(), 0);
570 assert_eq!(revlog.len(), 0);
566 assert!(revlog.get_entry(0).is_err());
571 assert!(revlog.get_entry(0).is_err());
567 assert!(!revlog.has_rev(0));
572 assert!(!revlog.has_rev(0));
568 }
573 }
569
574
570 #[test]
575 #[test]
571 fn test_inline() {
576 fn test_inline() {
572 let temp = tempfile::tempdir().unwrap();
577 let temp = tempfile::tempdir().unwrap();
573 let vfs = Vfs { base: temp.path() };
578 let vfs = Vfs { base: temp.path() };
574 let node0 = Node::from_hex("2ed2a3912a0b24502043eae84ee4b279c18b90dd")
579 let node0 = Node::from_hex("2ed2a3912a0b24502043eae84ee4b279c18b90dd")
575 .unwrap();
580 .unwrap();
576 let node1 = Node::from_hex("b004912a8510032a0350a74daa2803dadfb00e12")
581 let node1 = Node::from_hex("b004912a8510032a0350a74daa2803dadfb00e12")
577 .unwrap();
582 .unwrap();
578 let node2 = Node::from_hex("dd6ad206e907be60927b5a3117b97dffb2590582")
583 let node2 = Node::from_hex("dd6ad206e907be60927b5a3117b97dffb2590582")
579 .unwrap();
584 .unwrap();
580 let entry0_bytes = IndexEntryBuilder::new()
585 let entry0_bytes = IndexEntryBuilder::new()
581 .is_first(true)
586 .is_first(true)
582 .with_version(1)
587 .with_version(1)
583 .with_inline(true)
588 .with_inline(true)
584 .with_offset(INDEX_ENTRY_SIZE)
589 .with_offset(INDEX_ENTRY_SIZE)
585 .with_node(node0)
590 .with_node(node0)
586 .build();
591 .build();
587 let entry1_bytes = IndexEntryBuilder::new()
592 let entry1_bytes = IndexEntryBuilder::new()
588 .with_offset(INDEX_ENTRY_SIZE)
593 .with_offset(INDEX_ENTRY_SIZE)
589 .with_node(node1)
594 .with_node(node1)
590 .build();
595 .build();
591 let entry2_bytes = IndexEntryBuilder::new()
596 let entry2_bytes = IndexEntryBuilder::new()
592 .with_offset(INDEX_ENTRY_SIZE)
597 .with_offset(INDEX_ENTRY_SIZE)
593 .with_p1(0)
598 .with_p1(0)
594 .with_p2(1)
599 .with_p2(1)
595 .with_node(node2)
600 .with_node(node2)
596 .build();
601 .build();
597 let contents = vec![entry0_bytes, entry1_bytes, entry2_bytes]
602 let contents = vec![entry0_bytes, entry1_bytes, entry2_bytes]
598 .into_iter()
603 .into_iter()
599 .flatten()
604 .flatten()
600 .collect_vec();
605 .collect_vec();
601 std::fs::write(temp.path().join("foo.i"), contents).unwrap();
606 std::fs::write(temp.path().join("foo.i"), contents).unwrap();
602 let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap();
607 let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap();
603
608
604 let entry0 = revlog.get_entry(0).ok().unwrap();
609 let entry0 = revlog.get_entry(0).ok().unwrap();
605 assert_eq!(entry0.revision(), 0);
610 assert_eq!(entry0.revision(), 0);
606 assert_eq!(*entry0.node(), node0);
611 assert_eq!(*entry0.node(), node0);
607 assert!(!entry0.has_p1());
612 assert!(!entry0.has_p1());
608 assert_eq!(entry0.p1(), None);
613 assert_eq!(entry0.p1(), None);
609 assert_eq!(entry0.p2(), None);
614 assert_eq!(entry0.p2(), None);
610 let p1_entry = entry0.p1_entry().unwrap();
615 let p1_entry = entry0.p1_entry().unwrap();
611 assert!(p1_entry.is_none());
616 assert!(p1_entry.is_none());
612 let p2_entry = entry0.p2_entry().unwrap();
617 let p2_entry = entry0.p2_entry().unwrap();
613 assert!(p2_entry.is_none());
618 assert!(p2_entry.is_none());
614
619
615 let entry1 = revlog.get_entry(1).ok().unwrap();
620 let entry1 = revlog.get_entry(1).ok().unwrap();
616 assert_eq!(entry1.revision(), 1);
621 assert_eq!(entry1.revision(), 1);
617 assert_eq!(*entry1.node(), node1);
622 assert_eq!(*entry1.node(), node1);
618 assert!(!entry1.has_p1());
623 assert!(!entry1.has_p1());
619 assert_eq!(entry1.p1(), None);
624 assert_eq!(entry1.p1(), None);
620 assert_eq!(entry1.p2(), None);
625 assert_eq!(entry1.p2(), None);
621 let p1_entry = entry1.p1_entry().unwrap();
626 let p1_entry = entry1.p1_entry().unwrap();
622 assert!(p1_entry.is_none());
627 assert!(p1_entry.is_none());
623 let p2_entry = entry1.p2_entry().unwrap();
628 let p2_entry = entry1.p2_entry().unwrap();
624 assert!(p2_entry.is_none());
629 assert!(p2_entry.is_none());
625
630
626 let entry2 = revlog.get_entry(2).ok().unwrap();
631 let entry2 = revlog.get_entry(2).ok().unwrap();
627 assert_eq!(entry2.revision(), 2);
632 assert_eq!(entry2.revision(), 2);
628 assert_eq!(*entry2.node(), node2);
633 assert_eq!(*entry2.node(), node2);
629 assert!(entry2.has_p1());
634 assert!(entry2.has_p1());
630 assert_eq!(entry2.p1(), Some(0));
635 assert_eq!(entry2.p1(), Some(0));
631 assert_eq!(entry2.p2(), Some(1));
636 assert_eq!(entry2.p2(), Some(1));
632 let p1_entry = entry2.p1_entry().unwrap();
637 let p1_entry = entry2.p1_entry().unwrap();
633 assert!(p1_entry.is_some());
638 assert!(p1_entry.is_some());
634 assert_eq!(p1_entry.unwrap().revision(), 0);
639 assert_eq!(p1_entry.unwrap().revision(), 0);
635 let p2_entry = entry2.p2_entry().unwrap();
640 let p2_entry = entry2.p2_entry().unwrap();
636 assert!(p2_entry.is_some());
641 assert!(p2_entry.is_some());
637 assert_eq!(p2_entry.unwrap().revision(), 1);
642 assert_eq!(p2_entry.unwrap().revision(), 1);
638 }
643 }
639 }
644 }
General Comments 0
You need to be logged in to leave comments. Login now