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 = |
|
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 |
. |
|
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 |
|
|
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. |
|
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), |
|
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( |
|
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, |
|
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( |
|
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( |
|
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( |
|
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