##// END OF EJS Templates
rust-revlog: teach the revlog opening code to read the repo options...
Raphaël Gomès -
r52084:13f58ce7 default
parent child Browse files
Show More
@@ -16,6 +16,7 b' and O(changes) merge between branches.'
16 import binascii
16 import binascii
17 import collections
17 import collections
18 import contextlib
18 import contextlib
19 import functools
19 import io
20 import io
20 import os
21 import os
21 import struct
22 import struct
@@ -224,9 +225,9 b' else:'
224 parse_index_v1_nodemap = None
225 parse_index_v1_nodemap = None
225
226
226
227
227 def parse_index_v1_mixed(data, inline):
228 def parse_index_v1_mixed(data, inline, default_header):
228 index, cache = parse_index_v1(data, inline)
229 index, cache = parse_index_v1(data, inline)
229 return rustrevlog.MixedIndex(index, data), cache
230 return rustrevlog.MixedIndex(index, data, default_header), cache
230
231
231
232
232 # corresponds to uncompressed length of indexformatng (2 gigs, 4-byte
233 # corresponds to uncompressed length of indexformatng (2 gigs, 4-byte
@@ -1694,7 +1695,9 b' class revlog:'
1694 elif devel_nodemap:
1695 elif devel_nodemap:
1695 self._parse_index = parse_index_v1_nodemap
1696 self._parse_index = parse_index_v1_nodemap
1696 elif use_rust_index:
1697 elif use_rust_index:
1697 self._parse_index = parse_index_v1_mixed
1698 self._parse_index = functools.partial(
1699 parse_index_v1_mixed, default_header=new_header
1700 )
1698 try:
1701 try:
1699 d = self._parse_index(index_data, self._inline)
1702 d = self._parse_index(index_data, self._inline)
1700 index, chunkcache = d
1703 index, chunkcache = d
@@ -6,11 +6,10 b''
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 use crate::repo::Repo;
8 use crate::repo::Repo;
9 use crate::requirements;
10 use crate::revlog::{Revlog, RevlogError};
9 use crate::revlog::{Revlog, RevlogError};
11
10
12 /// Kind of data to debug
11 /// Kind of data to debug
13 #[derive(Debug, Copy, Clone)]
12 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
14 pub enum DebugDataKind {
13 pub enum DebugDataKind {
15 Changelog,
14 Changelog,
16 Manifest,
15 Manifest,
@@ -26,11 +25,12 b' pub fn debug_data('
26 DebugDataKind::Changelog => "00changelog.i",
25 DebugDataKind::Changelog => "00changelog.i",
27 DebugDataKind::Manifest => "00manifest.i",
26 DebugDataKind::Manifest => "00manifest.i",
28 };
27 };
29 let use_nodemap = repo
28 let revlog = Revlog::open(
30 .requirements()
29 &repo.store_vfs(),
31 .contains(requirements::NODEMAP_REQUIREMENT);
30 index_file,
32 let revlog =
31 None,
33 Revlog::open(&repo.store_vfs(), index_file, None, use_nodemap)?;
32 repo.default_revlog_options(kind == DebugDataKind::Changelog)?,
33 )?;
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_for_checked_rev(rev)?;
36 let data = revlog.get_rev_data_for_checked_rev(rev)?;
@@ -8,6 +8,10 b' use crate::errors::HgResultExt;'
8 use crate::errors::{HgError, IoResultExt};
8 use crate::errors::{HgError, IoResultExt};
9 use crate::lock::{try_with_lock_no_wait, LockError};
9 use crate::lock::{try_with_lock_no_wait, LockError};
10 use crate::manifest::{Manifest, Manifestlog};
10 use crate::manifest::{Manifest, Manifestlog};
11 use crate::requirements::{
12 CHANGELOGV2_REQUIREMENT, GENERALDELTA_REQUIREMENT, NODEMAP_REQUIREMENT,
13 REVLOGV1_REQUIREMENT, REVLOGV2_REQUIREMENT,
14 };
11 use crate::revlog::filelog::Filelog;
15 use crate::revlog::filelog::Filelog;
12 use crate::revlog::RevlogError;
16 use crate::revlog::RevlogError;
13 use crate::utils::debug::debug_wait_for_file_or_print;
17 use crate::utils::debug::debug_wait_for_file_or_print;
@@ -15,8 +19,10 b' use crate::utils::files::get_path_from_b'
15 use crate::utils::hg_path::HgPath;
19 use crate::utils::hg_path::HgPath;
16 use crate::utils::SliceExt;
20 use crate::utils::SliceExt;
17 use crate::vfs::{is_dir, is_file, Vfs};
21 use crate::vfs::{is_dir, is_file, Vfs};
18 use crate::DirstateError;
22 use crate::{
19 use crate::{requirements, NodePrefix, UncheckedRevision};
23 requirements, NodePrefix, RevlogVersionOptions, UncheckedRevision,
24 };
25 use crate::{DirstateError, RevlogOpenOptions};
20 use std::cell::{Ref, RefCell, RefMut};
26 use std::cell::{Ref, RefCell, RefMut};
21 use std::collections::HashSet;
27 use std::collections::HashSet;
22 use std::io::Seek;
28 use std::io::Seek;
@@ -523,7 +529,7 b' impl Repo {'
523 }
529 }
524
530
525 fn new_changelog(&self) -> Result<Changelog, HgError> {
531 fn new_changelog(&self) -> Result<Changelog, HgError> {
526 Changelog::open(&self.store_vfs(), self.has_nodemap())
532 Changelog::open(&self.store_vfs(), self.default_revlog_options(true)?)
527 }
533 }
528
534
529 pub fn changelog(&self) -> Result<Ref<Changelog>, HgError> {
535 pub fn changelog(&self) -> Result<Ref<Changelog>, HgError> {
@@ -535,7 +541,10 b' impl Repo {'
535 }
541 }
536
542
537 fn new_manifestlog(&self) -> Result<Manifestlog, HgError> {
543 fn new_manifestlog(&self) -> Result<Manifestlog, HgError> {
538 Manifestlog::open(&self.store_vfs(), self.has_nodemap())
544 Manifestlog::open(
545 &self.store_vfs(),
546 self.default_revlog_options(false)?,
547 )
539 }
548 }
540
549
541 pub fn manifestlog(&self) -> Result<Ref<Manifestlog>, HgError> {
550 pub fn manifestlog(&self) -> Result<Ref<Manifestlog>, HgError> {
@@ -581,7 +590,7 b' impl Repo {'
581 }
590 }
582
591
583 pub fn filelog(&self, path: &HgPath) -> Result<Filelog, HgError> {
592 pub fn filelog(&self, path: &HgPath) -> Result<Filelog, HgError> {
584 Filelog::open(self, path)
593 Filelog::open(self, path, self.default_revlog_options(false)?)
585 }
594 }
586
595
587 /// Write to disk any updates that were made through `dirstate_map_mut`.
596 /// Write to disk any updates that were made through `dirstate_map_mut`.
@@ -730,6 +739,35 b' impl Repo {'
730 }
739 }
731 Ok(())
740 Ok(())
732 }
741 }
742
743 pub fn default_revlog_options(
744 &self,
745 changelog: bool,
746 ) -> Result<RevlogOpenOptions, HgError> {
747 let requirements = self.requirements();
748 let version = if changelog
749 && requirements.contains(CHANGELOGV2_REQUIREMENT)
750 {
751 let compute_rank = self
752 .config()
753 .get_bool(b"experimental", b"changelog-v2.compute-rank")?;
754 RevlogVersionOptions::ChangelogV2 { compute_rank }
755 } else if requirements.contains(REVLOGV2_REQUIREMENT) {
756 RevlogVersionOptions::V2
757 } else if requirements.contains(REVLOGV1_REQUIREMENT) {
758 RevlogVersionOptions::V1 {
759 generaldelta: requirements.contains(GENERALDELTA_REQUIREMENT),
760 }
761 } else {
762 RevlogVersionOptions::V0
763 };
764 Ok(RevlogOpenOptions {
765 version,
766 // We don't need to dance around the slow path like in the Python
767 // implementation since we know we have access to the fast code.
768 use_nodemap: requirements.contains(NODEMAP_REQUIREMENT),
769 })
770 }
733 }
771 }
734
772
735 /// Lazily-initialized component of `Repo` with interior mutability
773 /// Lazily-initialized component of `Repo` with interior mutability
@@ -77,7 +77,7 b' const REQUIRED: &[&str] = &["revlogv1", '
77
77
78 /// rhg supports repository with or without these
78 /// rhg supports repository with or without these
79 const SUPPORTED: &[&str] = &[
79 const SUPPORTED: &[&str] = &[
80 "generaldelta",
80 GENERALDELTA_REQUIREMENT,
81 SHARED_REQUIREMENT,
81 SHARED_REQUIREMENT,
82 SHARESAFE_REQUIREMENT,
82 SHARESAFE_REQUIREMENT,
83 SPARSEREVLOG_REQUIREMENT,
83 SPARSEREVLOG_REQUIREMENT,
@@ -100,6 +100,7 b' const SUPPORTED: &[&str] = &['
100 // Copied from mercurial/requirements.py:
100 // Copied from mercurial/requirements.py:
101
101
102 pub const DIRSTATE_V2_REQUIREMENT: &str = "dirstate-v2";
102 pub const DIRSTATE_V2_REQUIREMENT: &str = "dirstate-v2";
103 pub const GENERALDELTA_REQUIREMENT: &str = "generaldelta";
103
104
104 /// A repository that uses the tracked hint dirstate file
105 /// A repository that uses the tracked hint dirstate file
105 #[allow(unused)]
106 #[allow(unused)]
@@ -128,11 +129,20 b' pub const INTERNAL_PHASE_REQUIREMENT: &s'
128 #[allow(unused)]
129 #[allow(unused)]
129 pub const TREEMANIFEST_REQUIREMENT: &str = "treemanifest";
130 pub const TREEMANIFEST_REQUIREMENT: &str = "treemanifest";
130
131
132 /// Whether to use the "RevlogNG" or V1 of the revlog format
133 #[allow(unused)]
134 pub const REVLOGV1_REQUIREMENT: &str = "revlogv1";
135
131 /// Increment the sub-version when the revlog v2 format changes to lock out old
136 /// Increment the sub-version when the revlog v2 format changes to lock out old
132 /// clients.
137 /// clients.
133 #[allow(unused)]
138 #[allow(unused)]
134 pub const REVLOGV2_REQUIREMENT: &str = "exp-revlogv2.1";
139 pub const REVLOGV2_REQUIREMENT: &str = "exp-revlogv2.1";
135
140
141 /// Increment the sub-version when the revlog v2 format changes to lock out old
142 /// clients.
143 #[allow(unused)]
144 pub const CHANGELOGV2_REQUIREMENT: &str = "exp-changelog-v2";
145
136 /// A repository with the sparserevlog feature will have delta chains that
146 /// A repository with the sparserevlog feature will have delta chains that
137 /// can spread over a larger span. Sparse reading cuts these large spans into
147 /// can spread over a larger span. Sparse reading cuts these large spans into
138 /// pieces, so that each piece isn't too big.
148 /// pieces, so that each piece isn't too big.
@@ -4,7 +4,7 b' 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::{Graph, GraphError, UncheckedRevision};
7 use crate::{Graph, GraphError, RevlogOpenOptions, UncheckedRevision};
8 use itertools::Itertools;
8 use itertools::Itertools;
9 use std::ascii::escape_default;
9 use std::ascii::escape_default;
10 use std::borrow::Cow;
10 use std::borrow::Cow;
@@ -18,9 +18,11 b' pub struct Changelog {'
18
18
19 impl Changelog {
19 impl Changelog {
20 /// Open the `changelog` of a repository given by its root.
20 /// Open the `changelog` of a repository given by its root.
21 pub fn open(store_vfs: &Vfs, use_nodemap: bool) -> Result<Self, HgError> {
21 pub fn open(
22 let revlog =
22 store_vfs: &Vfs,
23 Revlog::open(store_vfs, "00changelog.i", None, use_nodemap)?;
23 options: RevlogOpenOptions,
24 ) -> Result<Self, HgError> {
25 let revlog = Revlog::open(store_vfs, "00changelog.i", None, options)?;
24 Ok(Self { revlog })
26 Ok(Self { revlog })
25 }
27 }
26
28
@@ -342,7 +344,9 b' message",'
342 let temp = tempfile::tempdir().unwrap();
344 let temp = tempfile::tempdir().unwrap();
343 let vfs = Vfs { base: temp.path() };
345 let vfs = Vfs { base: temp.path() };
344 std::fs::write(temp.path().join("foo.i"), b"").unwrap();
346 std::fs::write(temp.path().join("foo.i"), b"").unwrap();
345 let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap();
347 let revlog =
348 Revlog::open(&vfs, "foo.i", None, RevlogOpenOptions::new())
349 .unwrap();
346
350
347 let changelog = Changelog { revlog };
351 let changelog = Changelog { revlog };
348 assert_eq!(
352 assert_eq!(
@@ -11,6 +11,7 b' use crate::utils::hg_path::HgPath;'
11 use crate::utils::SliceExt;
11 use crate::utils::SliceExt;
12 use crate::Graph;
12 use crate::Graph;
13 use crate::GraphError;
13 use crate::GraphError;
14 use crate::RevlogOpenOptions;
14 use crate::UncheckedRevision;
15 use crate::UncheckedRevision;
15 use std::path::PathBuf;
16 use std::path::PathBuf;
16
17
@@ -30,16 +31,21 b' impl Filelog {'
30 pub fn open_vfs(
31 pub fn open_vfs(
31 store_vfs: &crate::vfs::Vfs<'_>,
32 store_vfs: &crate::vfs::Vfs<'_>,
32 file_path: &HgPath,
33 file_path: &HgPath,
34 options: RevlogOpenOptions,
33 ) -> Result<Self, HgError> {
35 ) -> Result<Self, HgError> {
34 let index_path = store_path(file_path, b".i");
36 let index_path = store_path(file_path, b".i");
35 let data_path = store_path(file_path, b".d");
37 let data_path = store_path(file_path, b".d");
36 let revlog =
38 let revlog =
37 Revlog::open(store_vfs, index_path, Some(&data_path), false)?;
39 Revlog::open(store_vfs, index_path, Some(&data_path), options)?;
38 Ok(Self { revlog })
40 Ok(Self { revlog })
39 }
41 }
40
42
41 pub fn open(repo: &Repo, file_path: &HgPath) -> Result<Self, HgError> {
43 pub fn open(
42 Self::open_vfs(&repo.store_vfs(), file_path)
44 repo: &Repo,
45 file_path: &HgPath,
46 options: RevlogOpenOptions,
47 ) -> Result<Self, HgError> {
48 Self::open_vfs(&repo.store_vfs(), file_path, options)
43 }
49 }
44
50
45 /// The given node ID is that of the file as found in a filelog, not of a
51 /// The given node ID is that of the file as found in a filelog, not of a
@@ -15,7 +15,7 b' pub const INDEX_ENTRY_SIZE: usize = 64;'
15 pub const COMPRESSION_MODE_INLINE: u8 = 2;
15 pub const COMPRESSION_MODE_INLINE: u8 = 2;
16
16
17 pub struct IndexHeader {
17 pub struct IndexHeader {
18 header_bytes: [u8; 4],
18 pub(super) header_bytes: [u8; 4],
19 }
19 }
20
20
21 #[derive(Copy, Clone)]
21 #[derive(Copy, Clone)]
@@ -54,32 +54,22 b' impl IndexHeader {'
54 BigEndian::read_u16(&self.header_bytes[2..4])
54 BigEndian::read_u16(&self.header_bytes[2..4])
55 }
55 }
56
56
57 const EMPTY_INDEX_HEADER: IndexHeader = IndexHeader {
57 pub fn parse(index_bytes: &[u8]) -> Result<Option<IndexHeader>, HgError> {
58 // We treat an empty file as a valid index with no entries.
59 // Here we make an arbitrary choice of what we assume the format of the
60 // index to be (V1, using generaldelta).
61 // This doesn't matter too much, since we're only doing read-only
62 // access. but the value corresponds to the `new_header` variable in
63 // `revlog.py`, `_loadindex`
64 header_bytes: [0, 3, 0, 1],
65 };
66
67 fn parse(index_bytes: &[u8]) -> Result<IndexHeader, HgError> {
68 if index_bytes.is_empty() {
58 if index_bytes.is_empty() {
69 return Ok(IndexHeader::EMPTY_INDEX_HEADER);
59 return Ok(None);
70 }
60 }
71 if index_bytes.len() < 4 {
61 if index_bytes.len() < 4 {
72 return Err(HgError::corrupted(
62 return Err(HgError::corrupted(
73 "corrupted revlog: can't read the index format header",
63 "corrupted revlog: can't read the index format header",
74 ));
64 ));
75 }
65 }
76 Ok(IndexHeader {
66 Ok(Some(IndexHeader {
77 header_bytes: {
67 header_bytes: {
78 let bytes: [u8; 4] =
68 let bytes: [u8; 4] =
79 index_bytes[0..4].try_into().expect("impossible");
69 index_bytes[0..4].try_into().expect("impossible");
80 bytes
70 bytes
81 },
71 },
82 })
72 }))
83 }
73 }
84 }
74 }
85
75
@@ -239,8 +229,10 b' impl Index {'
239 /// Calculate the start of each entry when is_inline is true.
229 /// Calculate the start of each entry when is_inline is true.
240 pub fn new(
230 pub fn new(
241 bytes: Box<dyn Deref<Target = [u8]> + Send>,
231 bytes: Box<dyn Deref<Target = [u8]> + Send>,
232 default_header: IndexHeader,
242 ) -> Result<Self, HgError> {
233 ) -> Result<Self, HgError> {
243 let header = IndexHeader::parse(bytes.as_ref())?;
234 let header =
235 IndexHeader::parse(bytes.as_ref())?.unwrap_or(default_header);
244
236
245 if header.format_version() != IndexHeader::REVLOGV1 {
237 if header.format_version() != IndexHeader::REVLOGV1 {
246 // A proper new version should have had a repo/store
238 // A proper new version should have had a repo/store
@@ -598,6 +590,7 b' mod tests {'
598 pub fn is_inline(index_bytes: &[u8]) -> bool {
590 pub fn is_inline(index_bytes: &[u8]) -> bool {
599 IndexHeader::parse(index_bytes)
591 IndexHeader::parse(index_bytes)
600 .expect("too short")
592 .expect("too short")
593 .unwrap()
601 .format_flags()
594 .format_flags()
602 .is_inline()
595 .is_inline()
603 }
596 }
@@ -605,6 +598,7 b' mod tests {'
605 pub fn uses_generaldelta(index_bytes: &[u8]) -> bool {
598 pub fn uses_generaldelta(index_bytes: &[u8]) -> bool {
606 IndexHeader::parse(index_bytes)
599 IndexHeader::parse(index_bytes)
607 .expect("too short")
600 .expect("too short")
601 .unwrap()
608 .format_flags()
602 .format_flags()
609 .uses_generaldelta()
603 .uses_generaldelta()
610 }
604 }
@@ -612,6 +606,7 b' mod tests {'
612 pub fn get_version(index_bytes: &[u8]) -> u16 {
606 pub fn get_version(index_bytes: &[u8]) -> u16 {
613 IndexHeader::parse(index_bytes)
607 IndexHeader::parse(index_bytes)
614 .expect("too short")
608 .expect("too short")
609 .unwrap()
615 .format_version()
610 .format_version()
616 }
611 }
617
612
@@ -4,12 +4,14 b' use crate::revlog::{Revlog, RevlogError}'
4 use crate::utils::hg_path::HgPath;
4 use crate::utils::hg_path::HgPath;
5 use crate::utils::SliceExt;
5 use crate::utils::SliceExt;
6 use crate::vfs::Vfs;
6 use crate::vfs::Vfs;
7 use crate::{Graph, GraphError, Revision, UncheckedRevision};
7 use crate::{
8 Graph, GraphError, Revision, RevlogOpenOptions, UncheckedRevision,
9 };
8
10
9 /// A specialized `Revlog` to work with `manifest` data format.
11 /// A specialized `Revlog` to work with `manifest` data format.
10 pub struct Manifestlog {
12 pub struct Manifestlog {
11 /// The generic `revlog` format.
13 /// The generic `revlog` format.
12 revlog: Revlog,
14 pub(crate) revlog: Revlog,
13 }
15 }
14
16
15 impl Graph for Manifestlog {
17 impl Graph for Manifestlog {
@@ -20,9 +22,11 b' impl Graph for Manifestlog {'
20
22
21 impl Manifestlog {
23 impl Manifestlog {
22 /// Open the `manifest` of a repository given by its root.
24 /// Open the `manifest` of a repository given by its root.
23 pub fn open(store_vfs: &Vfs, use_nodemap: bool) -> Result<Self, HgError> {
25 pub fn open(
24 let revlog =
26 store_vfs: &Vfs,
25 Revlog::open(store_vfs, "00manifest.i", None, use_nodemap)?;
27 options: RevlogOpenOptions,
28 ) -> Result<Self, HgError> {
29 let revlog = Revlog::open(store_vfs, "00manifest.i", None, options)?;
26 Ok(Self { revlog })
30 Ok(Self { revlog })
27 }
31 }
28
32
@@ -225,6 +225,55 b' impl Graph for Revlog {'
225 }
225 }
226 }
226 }
227
227
228 #[derive(Debug, Copy, Clone)]
229 pub enum RevlogVersionOptions {
230 V0,
231 V1 { generaldelta: bool },
232 V2,
233 ChangelogV2 { compute_rank: bool },
234 }
235
236 /// Options to govern how a revlog should be opened, usually from the
237 /// repository configuration or requirements.
238 #[derive(Debug, Copy, Clone)]
239 pub struct RevlogOpenOptions {
240 /// The revlog version, along with any option specific to this version
241 pub version: RevlogVersionOptions,
242 /// Whether the revlog uses a persistent nodemap.
243 pub use_nodemap: bool,
244 // TODO other non-header/version options,
245 }
246
247 impl RevlogOpenOptions {
248 pub fn new() -> Self {
249 Self {
250 version: RevlogVersionOptions::V1 { generaldelta: true },
251 use_nodemap: false,
252 }
253 }
254
255 fn default_index_header(&self) -> index::IndexHeader {
256 index::IndexHeader {
257 header_bytes: match self.version {
258 RevlogVersionOptions::V0 => [0, 0, 0, 0],
259 RevlogVersionOptions::V1 { generaldelta } => {
260 [0, if generaldelta { 3 } else { 1 }, 0, 1]
261 }
262 RevlogVersionOptions::V2 => 0xDEADu32.to_be_bytes(),
263 RevlogVersionOptions::ChangelogV2 { compute_rank: _ } => {
264 0xD34Du32.to_be_bytes()
265 }
266 },
267 }
268 }
269 }
270
271 impl Default for RevlogOpenOptions {
272 fn default() -> Self {
273 Self::new()
274 }
275 }
276
228 impl Revlog {
277 impl Revlog {
229 /// Open a revlog index file.
278 /// Open a revlog index file.
230 ///
279 ///
@@ -234,24 +283,30 b' impl Revlog {'
234 store_vfs: &Vfs,
283 store_vfs: &Vfs,
235 index_path: impl AsRef<Path>,
284 index_path: impl AsRef<Path>,
236 data_path: Option<&Path>,
285 data_path: Option<&Path>,
237 use_nodemap: bool,
286 options: RevlogOpenOptions,
238 ) -> Result<Self, HgError> {
287 ) -> Result<Self, HgError> {
239 Self::open_gen(store_vfs, index_path, data_path, use_nodemap, None)
288 Self::open_gen(store_vfs, index_path, data_path, options, None)
240 }
289 }
241
290
242 fn open_gen(
291 fn open_gen(
243 store_vfs: &Vfs,
292 store_vfs: &Vfs,
244 index_path: impl AsRef<Path>,
293 index_path: impl AsRef<Path>,
245 data_path: Option<&Path>,
294 data_path: Option<&Path>,
246 use_nodemap: bool,
295 options: RevlogOpenOptions,
247 nodemap_for_test: Option<nodemap::NodeTree>,
296 nodemap_for_test: Option<nodemap::NodeTree>,
248 ) -> Result<Self, HgError> {
297 ) -> Result<Self, HgError> {
249 let index_path = index_path.as_ref();
298 let index_path = index_path.as_ref();
250 let index = {
299 let index = {
251 match store_vfs.mmap_open_opt(index_path)? {
300 match store_vfs.mmap_open_opt(index_path)? {
252 None => Index::new(Box::<Vec<_>>::default()),
301 None => Index::new(
302 Box::<Vec<_>>::default(),
303 options.default_index_header(),
304 ),
253 Some(index_mmap) => {
305 Some(index_mmap) => {
254 let index = Index::new(Box::new(index_mmap))?;
306 let index = Index::new(
307 Box::new(index_mmap),
308 options.default_index_header(),
309 )?;
255 Ok(index)
310 Ok(index)
256 }
311 }
257 }
312 }
@@ -270,7 +325,7 b' impl Revlog {'
270 Some(Box::new(data_mmap))
325 Some(Box::new(data_mmap))
271 };
326 };
272
327
273 let nodemap = if index.is_inline() || !use_nodemap {
328 let nodemap = if index.is_inline() || !options.use_nodemap {
274 None
329 None
275 } else {
330 } else {
276 NodeMapDocket::read_from_file(store_vfs, index_path)?.map(
331 NodeMapDocket::read_from_file(store_vfs, index_path)?.map(
@@ -809,7 +864,9 b' mod tests {'
809 let temp = tempfile::tempdir().unwrap();
864 let temp = tempfile::tempdir().unwrap();
810 let vfs = Vfs { base: temp.path() };
865 let vfs = Vfs { base: temp.path() };
811 std::fs::write(temp.path().join("foo.i"), b"").unwrap();
866 std::fs::write(temp.path().join("foo.i"), b"").unwrap();
812 let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap();
867 let revlog =
868 Revlog::open(&vfs, "foo.i", None, RevlogOpenOptions::new())
869 .unwrap();
813 assert!(revlog.is_empty());
870 assert!(revlog.is_empty());
814 assert_eq!(revlog.len(), 0);
871 assert_eq!(revlog.len(), 0);
815 assert!(revlog.get_entry(0.into()).is_err());
872 assert!(revlog.get_entry(0.into()).is_err());
@@ -855,7 +912,9 b' mod tests {'
855 .flatten()
912 .flatten()
856 .collect_vec();
913 .collect_vec();
857 std::fs::write(temp.path().join("foo.i"), contents).unwrap();
914 std::fs::write(temp.path().join("foo.i"), contents).unwrap();
858 let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap();
915 let revlog =
916 Revlog::open(&vfs, "foo.i", None, RevlogOpenOptions::new())
917 .unwrap();
859
918
860 let entry0 = revlog.get_entry(0.into()).ok().unwrap();
919 let entry0 = revlog.get_entry(0.into()).ok().unwrap();
861 assert_eq!(entry0.revision(), Revision(0));
920 assert_eq!(entry0.revision(), Revision(0));
@@ -926,8 +985,14 b' mod tests {'
926 idx.insert_node(Revision(0), node0).unwrap();
985 idx.insert_node(Revision(0), node0).unwrap();
927 idx.insert_node(Revision(1), node1).unwrap();
986 idx.insert_node(Revision(1), node1).unwrap();
928
987
929 let revlog =
988 let revlog = Revlog::open_gen(
930 Revlog::open_gen(&vfs, "foo.i", None, true, Some(idx.nt)).unwrap();
989 &vfs,
990 "foo.i",
991 None,
992 RevlogOpenOptions::new(),
993 Some(idx.nt),
994 )
995 .unwrap();
931
996
932 // accessing the data shows the corruption
997 // accessing the data shows the corruption
933 revlog.get_entry(0.into()).unwrap().data().unwrap_err();
998 revlog.get_entry(0.into()).unwrap().data().unwrap_err();
@@ -17,6 +17,7 b' use cpython::{'
17 PyObject, PyResult, PyString, PyTuple, Python, PythonObject, ToPyObject,
17 PyObject, PyResult, PyString, PyTuple, Python, PythonObject, ToPyObject,
18 };
18 };
19 use hg::{
19 use hg::{
20 index::IndexHeader,
20 nodemap::{Block, NodeMapError, NodeTree},
21 nodemap::{Block, NodeMapError, NodeTree},
21 revlog::{nodemap::NodeMap, NodePrefix, RevlogIndex},
22 revlog::{nodemap::NodeMap, NodePrefix, RevlogIndex},
22 BaseRevision, Revision, UncheckedRevision,
23 BaseRevision, Revision, UncheckedRevision,
@@ -47,9 +48,10 b' py_class!(pub class MixedIndex |py| {'
47 def __new__(
48 def __new__(
48 _cls,
49 _cls,
49 cindex: PyObject,
50 cindex: PyObject,
50 data: PyObject
51 data: PyObject,
52 default_header: u32,
51 ) -> PyResult<MixedIndex> {
53 ) -> PyResult<MixedIndex> {
52 Self::new(py, cindex, data)
54 Self::new(py, cindex, data, default_header)
53 }
55 }
54
56
55 /// Compatibility layer used for Python consumers needing access to the C index
57 /// Compatibility layer used for Python consumers needing access to the C index
@@ -364,6 +366,7 b' impl MixedIndex {'
364 py: Python,
366 py: Python,
365 cindex: PyObject,
367 cindex: PyObject,
366 data: PyObject,
368 data: PyObject,
369 header: u32,
367 ) -> PyResult<MixedIndex> {
370 ) -> PyResult<MixedIndex> {
368 // Safety: we keep the buffer around inside the class as `index_mmap`
371 // Safety: we keep the buffer around inside the class as `index_mmap`
369 let (buf, bytes) = unsafe { mmap_keeparound(py, data)? };
372 let (buf, bytes) = unsafe { mmap_keeparound(py, data)? };
@@ -371,7 +374,15 b' impl MixedIndex {'
371 Self::create_instance(
374 Self::create_instance(
372 py,
375 py,
373 RefCell::new(cindex::Index::new(py, cindex)?),
376 RefCell::new(cindex::Index::new(py, cindex)?),
374 RefCell::new(hg::index::Index::new(bytes).unwrap()),
377 RefCell::new(
378 hg::index::Index::new(
379 bytes,
380 IndexHeader::parse(&header.to_be_bytes())
381 .expect("default header is broken")
382 .unwrap(),
383 )
384 .unwrap(),
385 ),
375 RefCell::new(None),
386 RefCell::new(None),
376 RefCell::new(None),
387 RefCell::new(None),
377 RefCell::new(None),
388 RefCell::new(None),
@@ -28,12 +28,12 b' use hg::utils::files::{'
28 get_bytes_from_os_str, get_bytes_from_os_string, get_path_from_bytes,
28 get_bytes_from_os_str, get_bytes_from_os_string, get_path_from_bytes,
29 };
29 };
30 use hg::utils::hg_path::{hg_path_to_path_buf, HgPath};
30 use hg::utils::hg_path::{hg_path_to_path_buf, HgPath};
31 use hg::DirstateStatus;
32 use hg::PatternFileWarning;
31 use hg::PatternFileWarning;
33 use hg::Revision;
32 use hg::Revision;
34 use hg::StatusError;
33 use hg::StatusError;
35 use hg::StatusOptions;
34 use hg::StatusOptions;
36 use hg::{self, narrow, sparse};
35 use hg::{self, narrow, sparse};
36 use hg::{DirstateStatus, RevlogOpenOptions};
37 use log::info;
37 use log::info;
38 use rayon::prelude::*;
38 use rayon::prelude::*;
39 use std::borrow::Cow;
39 use std::borrow::Cow;
@@ -383,6 +383,7 b' pub fn run(invocation: &crate::CliInvoca'
383 })?;
383 })?;
384 let working_directory_vfs = repo.working_directory_vfs();
384 let working_directory_vfs = repo.working_directory_vfs();
385 let store_vfs = repo.store_vfs();
385 let store_vfs = repo.store_vfs();
386 let revlog_open_options = repo.default_revlog_options(false)?;
386 let res: Vec<_> = take(&mut ds_status.unsure)
387 let res: Vec<_> = take(&mut ds_status.unsure)
387 .into_par_iter()
388 .into_par_iter()
388 .map(|to_check| {
389 .map(|to_check| {
@@ -396,6 +397,7 b' pub fn run(invocation: &crate::CliInvoca'
396 check_exec,
397 check_exec,
397 &manifest,
398 &manifest,
398 &to_check.path,
399 &to_check.path,
400 revlog_open_options,
399 ) {
401 ) {
400 Err(HgError::IoError { .. }) => {
402 Err(HgError::IoError { .. }) => {
401 // IO errors most likely stem from the file being
403 // IO errors most likely stem from the file being
@@ -747,6 +749,7 b' fn unsure_is_modified('
747 check_exec: bool,
749 check_exec: bool,
748 manifest: &Manifest,
750 manifest: &Manifest,
749 hg_path: &HgPath,
751 hg_path: &HgPath,
752 revlog_open_options: RevlogOpenOptions,
750 ) -> Result<UnsureOutcome, HgError> {
753 ) -> Result<UnsureOutcome, HgError> {
751 let vfs = working_directory_vfs;
754 let vfs = working_directory_vfs;
752 let fs_path = hg_path_to_path_buf(hg_path).expect("HgPath conversion");
755 let fs_path = hg_path_to_path_buf(hg_path).expect("HgPath conversion");
@@ -778,7 +781,11 b' fn unsure_is_modified('
778 if entry_flags != fs_flags {
781 if entry_flags != fs_flags {
779 return Ok(UnsureOutcome::Modified);
782 return Ok(UnsureOutcome::Modified);
780 }
783 }
781 let filelog = hg::filelog::Filelog::open_vfs(&store_vfs, hg_path)?;
784 let filelog = hg::filelog::Filelog::open_vfs(
785 &store_vfs,
786 hg_path,
787 revlog_open_options,
788 )?;
782 let fs_len = fs_metadata.len();
789 let fs_len = fs_metadata.len();
783 let file_node = entry.node_id()?;
790 let file_node = entry.node_id()?;
784 let filelog_entry = filelog.entry_for_node(file_node).map_err(|_| {
791 let filelog_entry = filelog.entry_for_node(file_node).map_err(|_| {
@@ -1,3 +1,4 b''
1 import struct
1 import unittest
2 import unittest
2
3
3 try:
4 try:
@@ -14,6 +15,8 b' else:'
14
15
15 from mercurial.testing import revlog as revlogtesting
16 from mercurial.testing import revlog as revlogtesting
16
17
18 header = struct.unpack(">I", revlogtesting.data_non_inlined[:4])[0]
19
17
20
18 @unittest.skipIf(
21 @unittest.skipIf(
19 rustext is None,
22 rustext is None,
@@ -22,24 +25,24 b' from mercurial.testing import revlog as '
22 class RustRevlogIndexTest(revlogtesting.RevlogBasedTestBase):
25 class RustRevlogIndexTest(revlogtesting.RevlogBasedTestBase):
23 def test_heads(self):
26 def test_heads(self):
24 idx = self.parseindex()
27 idx = self.parseindex()
25 rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined)
28 rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined, header)
26 self.assertEqual(rustidx.headrevs(), idx.headrevs())
29 self.assertEqual(rustidx.headrevs(), idx.headrevs())
27
30
28 def test_get_cindex(self):
31 def test_get_cindex(self):
29 # drop me once we no longer need the method for shortest node
32 # drop me once we no longer need the method for shortest node
30 idx = self.parseindex()
33 idx = self.parseindex()
31 rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined)
34 rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined, header)
32 cidx = rustidx.get_cindex()
35 cidx = rustidx.get_cindex()
33 self.assertTrue(idx is cidx)
36 self.assertTrue(idx is cidx)
34
37
35 def test_len(self):
38 def test_len(self):
36 idx = self.parseindex()
39 idx = self.parseindex()
37 rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined)
40 rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined, header)
38 self.assertEqual(len(rustidx), len(idx))
41 self.assertEqual(len(rustidx), len(idx))
39
42
40 def test_ancestors(self):
43 def test_ancestors(self):
41 idx = self.parseindex()
44 idx = self.parseindex()
42 rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined)
45 rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined, header)
43 lazy = LazyAncestors(rustidx, [3], 0, True)
46 lazy = LazyAncestors(rustidx, [3], 0, True)
44 # we have two more references to the index:
47 # we have two more references to the index:
45 # - in its inner iterator for __contains__ and __bool__
48 # - in its inner iterator for __contains__ and __bool__
General Comments 0
You need to be logged in to leave comments. Login now