Show More
@@ -16,6 +16,7 b' and O(changes) merge between branches.' | |||
|
16 | 16 | import binascii |
|
17 | 17 | import collections |
|
18 | 18 | import contextlib |
|
19 | import functools | |
|
19 | 20 | import io |
|
20 | 21 | import os |
|
21 | 22 | import struct |
@@ -224,9 +225,9 b' else:' | |||
|
224 | 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 | 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 | 233 | # corresponds to uncompressed length of indexformatng (2 gigs, 4-byte |
@@ -1694,7 +1695,9 b' class revlog:' | |||
|
1694 | 1695 | elif devel_nodemap: |
|
1695 | 1696 | self._parse_index = parse_index_v1_nodemap |
|
1696 | 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 | 1701 | try: |
|
1699 | 1702 | d = self._parse_index(index_data, self._inline) |
|
1700 | 1703 | index, chunkcache = d |
@@ -6,11 +6,10 b'' | |||
|
6 | 6 | // GNU General Public License version 2 or any later version. |
|
7 | 7 | |
|
8 | 8 | use crate::repo::Repo; |
|
9 | use crate::requirements; | |
|
10 | 9 | use crate::revlog::{Revlog, RevlogError}; |
|
11 | 10 | |
|
12 | 11 | /// Kind of data to debug |
|
13 | #[derive(Debug, Copy, Clone)] | |
|
12 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | |
|
14 | 13 | pub enum DebugDataKind { |
|
15 | 14 | Changelog, |
|
16 | 15 | Manifest, |
@@ -26,11 +25,12 b' pub fn debug_data(' | |||
|
26 | 25 | DebugDataKind::Changelog => "00changelog.i", |
|
27 | 26 | DebugDataKind::Manifest => "00manifest.i", |
|
28 | 27 | }; |
|
29 | let use_nodemap = repo | |
|
30 |
. |
|
|
31 | .contains(requirements::NODEMAP_REQUIREMENT); | |
|
32 | let revlog = | |
|
33 | Revlog::open(&repo.store_vfs(), index_file, None, use_nodemap)?; | |
|
28 | let revlog = Revlog::open( | |
|
29 | &repo.store_vfs(), | |
|
30 | index_file, | |
|
31 | None, | |
|
32 | repo.default_revlog_options(kind == DebugDataKind::Changelog)?, | |
|
33 | )?; | |
|
34 | 34 | let rev = |
|
35 | 35 | crate::revset::resolve_rev_number_or_hex_prefix(revset, &revlog)?; |
|
36 | 36 | let data = revlog.get_rev_data_for_checked_rev(rev)?; |
@@ -8,6 +8,10 b' use crate::errors::HgResultExt;' | |||
|
8 | 8 | use crate::errors::{HgError, IoResultExt}; |
|
9 | 9 | use crate::lock::{try_with_lock_no_wait, LockError}; |
|
10 | 10 | use crate::manifest::{Manifest, Manifestlog}; |
|
11 | use crate::requirements::{ | |
|
12 | CHANGELOGV2_REQUIREMENT, GENERALDELTA_REQUIREMENT, NODEMAP_REQUIREMENT, | |
|
13 | REVLOGV1_REQUIREMENT, REVLOGV2_REQUIREMENT, | |
|
14 | }; | |
|
11 | 15 | use crate::revlog::filelog::Filelog; |
|
12 | 16 | use crate::revlog::RevlogError; |
|
13 | 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 | 19 | use crate::utils::hg_path::HgPath; |
|
16 | 20 | use crate::utils::SliceExt; |
|
17 | 21 | use crate::vfs::{is_dir, is_file, Vfs}; |
|
18 | use crate::DirstateError; | |
|
19 |
|
|
|
22 | use crate::{ | |
|
23 | requirements, NodePrefix, RevlogVersionOptions, UncheckedRevision, | |
|
24 | }; | |
|
25 | use crate::{DirstateError, RevlogOpenOptions}; | |
|
20 | 26 | use std::cell::{Ref, RefCell, RefMut}; |
|
21 | 27 | use std::collections::HashSet; |
|
22 | 28 | use std::io::Seek; |
@@ -523,7 +529,7 b' impl Repo {' | |||
|
523 | 529 | } |
|
524 | 530 | |
|
525 | 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 | 535 | pub fn changelog(&self) -> Result<Ref<Changelog>, HgError> { |
@@ -535,7 +541,10 b' impl Repo {' | |||
|
535 | 541 | } |
|
536 | 542 | |
|
537 | 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 | 550 | pub fn manifestlog(&self) -> Result<Ref<Manifestlog>, HgError> { |
@@ -581,7 +590,7 b' impl Repo {' | |||
|
581 | 590 | } |
|
582 | 591 | |
|
583 | 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 | 596 | /// Write to disk any updates that were made through `dirstate_map_mut`. |
@@ -730,6 +739,35 b' impl Repo {' | |||
|
730 | 739 | } |
|
731 | 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 | 773 | /// Lazily-initialized component of `Repo` with interior mutability |
@@ -77,7 +77,7 b' const REQUIRED: &[&str] = &["revlogv1", ' | |||
|
77 | 77 | |
|
78 | 78 | /// rhg supports repository with or without these |
|
79 | 79 | const SUPPORTED: &[&str] = &[ |
|
80 | "generaldelta", | |
|
80 | GENERALDELTA_REQUIREMENT, | |
|
81 | 81 | SHARED_REQUIREMENT, |
|
82 | 82 | SHARESAFE_REQUIREMENT, |
|
83 | 83 | SPARSEREVLOG_REQUIREMENT, |
@@ -100,6 +100,7 b' const SUPPORTED: &[&str] = &[' | |||
|
100 | 100 | // Copied from mercurial/requirements.py: |
|
101 | 101 | |
|
102 | 102 | pub const DIRSTATE_V2_REQUIREMENT: &str = "dirstate-v2"; |
|
103 | pub const GENERALDELTA_REQUIREMENT: &str = "generaldelta"; | |
|
103 | 104 | |
|
104 | 105 | /// A repository that uses the tracked hint dirstate file |
|
105 | 106 | #[allow(unused)] |
@@ -128,11 +129,20 b' pub const INTERNAL_PHASE_REQUIREMENT: &s' | |||
|
128 | 129 | #[allow(unused)] |
|
129 | 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 | 136 | /// Increment the sub-version when the revlog v2 format changes to lock out old |
|
132 | 137 | /// clients. |
|
133 | 138 | #[allow(unused)] |
|
134 | 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 | 146 | /// A repository with the sparserevlog feature will have delta chains that |
|
137 | 147 | /// can spread over a larger span. Sparse reading cuts these large spans into |
|
138 | 148 | /// pieces, so that each piece isn't too big. |
@@ -4,7 +4,7 b' use crate::revlog::{Node, NodePrefix};' | |||
|
4 | 4 | use crate::revlog::{Revlog, RevlogEntry, RevlogError}; |
|
5 | 5 | use crate::utils::hg_path::HgPath; |
|
6 | 6 | use crate::vfs::Vfs; |
|
7 | use crate::{Graph, GraphError, UncheckedRevision}; | |
|
7 | use crate::{Graph, GraphError, RevlogOpenOptions, UncheckedRevision}; | |
|
8 | 8 | use itertools::Itertools; |
|
9 | 9 | use std::ascii::escape_default; |
|
10 | 10 | use std::borrow::Cow; |
@@ -18,9 +18,11 b' pub struct Changelog {' | |||
|
18 | 18 | |
|
19 | 19 | impl Changelog { |
|
20 | 20 | /// Open the `changelog` of a repository given by its root. |
|
21 | pub fn open(store_vfs: &Vfs, use_nodemap: bool) -> Result<Self, HgError> { | |
|
22 | let revlog = | |
|
23 | Revlog::open(store_vfs, "00changelog.i", None, use_nodemap)?; | |
|
21 | pub fn open( | |
|
22 | store_vfs: &Vfs, | |
|
23 | options: RevlogOpenOptions, | |
|
24 | ) -> Result<Self, HgError> { | |
|
25 | let revlog = Revlog::open(store_vfs, "00changelog.i", None, options)?; | |
|
24 | 26 | Ok(Self { revlog }) |
|
25 | 27 | } |
|
26 | 28 | |
@@ -342,7 +344,9 b' message",' | |||
|
342 | 344 | let temp = tempfile::tempdir().unwrap(); |
|
343 | 345 | let vfs = Vfs { base: temp.path() }; |
|
344 | 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 | 351 | let changelog = Changelog { revlog }; |
|
348 | 352 | assert_eq!( |
@@ -11,6 +11,7 b' use crate::utils::hg_path::HgPath;' | |||
|
11 | 11 | use crate::utils::SliceExt; |
|
12 | 12 | use crate::Graph; |
|
13 | 13 | use crate::GraphError; |
|
14 | use crate::RevlogOpenOptions; | |
|
14 | 15 | use crate::UncheckedRevision; |
|
15 | 16 | use std::path::PathBuf; |
|
16 | 17 | |
@@ -30,16 +31,21 b' impl Filelog {' | |||
|
30 | 31 | pub fn open_vfs( |
|
31 | 32 | store_vfs: &crate::vfs::Vfs<'_>, |
|
32 | 33 | file_path: &HgPath, |
|
34 | options: RevlogOpenOptions, | |
|
33 | 35 | ) -> Result<Self, HgError> { |
|
34 | 36 | let index_path = store_path(file_path, b".i"); |
|
35 | 37 | let data_path = store_path(file_path, b".d"); |
|
36 | 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 | 40 | Ok(Self { revlog }) |
|
39 | 41 | } |
|
40 | 42 | |
|
41 | pub fn open(repo: &Repo, file_path: &HgPath) -> Result<Self, HgError> { | |
|
42 | Self::open_vfs(&repo.store_vfs(), file_path) | |
|
43 | pub fn open( | |
|
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 | 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 | 15 | pub const COMPRESSION_MODE_INLINE: u8 = 2; |
|
16 | 16 | |
|
17 | 17 | pub struct IndexHeader { |
|
18 | header_bytes: [u8; 4], | |
|
18 | pub(super) header_bytes: [u8; 4], | |
|
19 | 19 | } |
|
20 | 20 | |
|
21 | 21 | #[derive(Copy, Clone)] |
@@ -54,32 +54,22 b' impl IndexHeader {' | |||
|
54 | 54 | BigEndian::read_u16(&self.header_bytes[2..4]) |
|
55 | 55 | } |
|
56 | 56 | |
|
57 | const EMPTY_INDEX_HEADER: IndexHeader = IndexHeader { | |
|
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> { | |
|
57 | pub fn parse(index_bytes: &[u8]) -> Result<Option<IndexHeader>, HgError> { | |
|
68 | 58 | if index_bytes.is_empty() { |
|
69 |
return Ok( |
|
|
59 | return Ok(None); | |
|
70 | 60 | } |
|
71 | 61 | if index_bytes.len() < 4 { |
|
72 | 62 | return Err(HgError::corrupted( |
|
73 | 63 | "corrupted revlog: can't read the index format header", |
|
74 | 64 | )); |
|
75 | 65 | } |
|
76 | Ok(IndexHeader { | |
|
66 | Ok(Some(IndexHeader { | |
|
77 | 67 | header_bytes: { |
|
78 | 68 | let bytes: [u8; 4] = |
|
79 | 69 | index_bytes[0..4].try_into().expect("impossible"); |
|
80 | 70 | bytes |
|
81 | 71 | }, |
|
82 | }) | |
|
72 | })) | |
|
83 | 73 | } |
|
84 | 74 | } |
|
85 | 75 | |
@@ -239,8 +229,10 b' impl Index {' | |||
|
239 | 229 | /// Calculate the start of each entry when is_inline is true. |
|
240 | 230 | pub fn new( |
|
241 | 231 | bytes: Box<dyn Deref<Target = [u8]> + Send>, |
|
232 | default_header: IndexHeader, | |
|
242 | 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 | 237 | if header.format_version() != IndexHeader::REVLOGV1 { |
|
246 | 238 | // A proper new version should have had a repo/store |
@@ -598,6 +590,7 b' mod tests {' | |||
|
598 | 590 | pub fn is_inline(index_bytes: &[u8]) -> bool { |
|
599 | 591 | IndexHeader::parse(index_bytes) |
|
600 | 592 | .expect("too short") |
|
593 | .unwrap() | |
|
601 | 594 | .format_flags() |
|
602 | 595 | .is_inline() |
|
603 | 596 | } |
@@ -605,6 +598,7 b' mod tests {' | |||
|
605 | 598 | pub fn uses_generaldelta(index_bytes: &[u8]) -> bool { |
|
606 | 599 | IndexHeader::parse(index_bytes) |
|
607 | 600 | .expect("too short") |
|
601 | .unwrap() | |
|
608 | 602 | .format_flags() |
|
609 | 603 | .uses_generaldelta() |
|
610 | 604 | } |
@@ -612,6 +606,7 b' mod tests {' | |||
|
612 | 606 | pub fn get_version(index_bytes: &[u8]) -> u16 { |
|
613 | 607 | IndexHeader::parse(index_bytes) |
|
614 | 608 | .expect("too short") |
|
609 | .unwrap() | |
|
615 | 610 | .format_version() |
|
616 | 611 | } |
|
617 | 612 |
@@ -4,12 +4,14 b' use crate::revlog::{Revlog, RevlogError}' | |||
|
4 | 4 | use crate::utils::hg_path::HgPath; |
|
5 | 5 | use crate::utils::SliceExt; |
|
6 | 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 | 11 | /// A specialized `Revlog` to work with `manifest` data format. |
|
10 | 12 | pub struct Manifestlog { |
|
11 | 13 | /// The generic `revlog` format. |
|
12 | revlog: Revlog, | |
|
14 | pub(crate) revlog: Revlog, | |
|
13 | 15 | } |
|
14 | 16 | |
|
15 | 17 | impl Graph for Manifestlog { |
@@ -20,9 +22,11 b' impl Graph for Manifestlog {' | |||
|
20 | 22 | |
|
21 | 23 | impl Manifestlog { |
|
22 | 24 | /// Open the `manifest` of a repository given by its root. |
|
23 | pub fn open(store_vfs: &Vfs, use_nodemap: bool) -> Result<Self, HgError> { | |
|
24 | let revlog = | |
|
25 | Revlog::open(store_vfs, "00manifest.i", None, use_nodemap)?; | |
|
25 | pub fn open( | |
|
26 | store_vfs: &Vfs, | |
|
27 | options: RevlogOpenOptions, | |
|
28 | ) -> Result<Self, HgError> { | |
|
29 | let revlog = Revlog::open(store_vfs, "00manifest.i", None, options)?; | |
|
26 | 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 | 277 | impl Revlog { |
|
229 | 278 | /// Open a revlog index file. |
|
230 | 279 | /// |
@@ -234,24 +283,30 b' impl Revlog {' | |||
|
234 | 283 | store_vfs: &Vfs, |
|
235 | 284 | index_path: impl AsRef<Path>, |
|
236 | 285 | data_path: Option<&Path>, |
|
237 | use_nodemap: bool, | |
|
286 | options: RevlogOpenOptions, | |
|
238 | 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 | 291 | fn open_gen( |
|
243 | 292 | store_vfs: &Vfs, |
|
244 | 293 | index_path: impl AsRef<Path>, |
|
245 | 294 | data_path: Option<&Path>, |
|
246 | use_nodemap: bool, | |
|
295 | options: RevlogOpenOptions, | |
|
247 | 296 | nodemap_for_test: Option<nodemap::NodeTree>, |
|
248 | 297 | ) -> Result<Self, HgError> { |
|
249 | 298 | let index_path = index_path.as_ref(); |
|
250 | 299 | let index = { |
|
251 | 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 | 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 | 310 | Ok(index) |
|
256 | 311 | } |
|
257 | 312 | } |
@@ -270,7 +325,7 b' impl Revlog {' | |||
|
270 | 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 | 329 | None |
|
275 | 330 | } else { |
|
276 | 331 | NodeMapDocket::read_from_file(store_vfs, index_path)?.map( |
@@ -809,7 +864,9 b' mod tests {' | |||
|
809 | 864 | let temp = tempfile::tempdir().unwrap(); |
|
810 | 865 | let vfs = Vfs { base: temp.path() }; |
|
811 | 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 | 870 | assert!(revlog.is_empty()); |
|
814 | 871 | assert_eq!(revlog.len(), 0); |
|
815 | 872 | assert!(revlog.get_entry(0.into()).is_err()); |
@@ -855,7 +912,9 b' mod tests {' | |||
|
855 | 912 | .flatten() |
|
856 | 913 | .collect_vec(); |
|
857 | 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 | 919 | let entry0 = revlog.get_entry(0.into()).ok().unwrap(); |
|
861 | 920 | assert_eq!(entry0.revision(), Revision(0)); |
@@ -926,8 +985,14 b' mod tests {' | |||
|
926 | 985 | idx.insert_node(Revision(0), node0).unwrap(); |
|
927 | 986 | idx.insert_node(Revision(1), node1).unwrap(); |
|
928 | 987 | |
|
929 | let revlog = | |
|
930 | Revlog::open_gen(&vfs, "foo.i", None, true, Some(idx.nt)).unwrap(); | |
|
988 | let revlog = Revlog::open_gen( | |
|
989 | &vfs, | |
|
990 | "foo.i", | |
|
991 | None, | |
|
992 | RevlogOpenOptions::new(), | |
|
993 | Some(idx.nt), | |
|
994 | ) | |
|
995 | .unwrap(); | |
|
931 | 996 | |
|
932 | 997 | // accessing the data shows the corruption |
|
933 | 998 | revlog.get_entry(0.into()).unwrap().data().unwrap_err(); |
@@ -17,6 +17,7 b' use cpython::{' | |||
|
17 | 17 | PyObject, PyResult, PyString, PyTuple, Python, PythonObject, ToPyObject, |
|
18 | 18 | }; |
|
19 | 19 | use hg::{ |
|
20 | index::IndexHeader, | |
|
20 | 21 | nodemap::{Block, NodeMapError, NodeTree}, |
|
21 | 22 | revlog::{nodemap::NodeMap, NodePrefix, RevlogIndex}, |
|
22 | 23 | BaseRevision, Revision, UncheckedRevision, |
@@ -47,9 +48,10 b' py_class!(pub class MixedIndex |py| {' | |||
|
47 | 48 | def __new__( |
|
48 | 49 | _cls, |
|
49 | 50 | cindex: PyObject, |
|
50 | data: PyObject | |
|
51 | data: PyObject, | |
|
52 | default_header: u32, | |
|
51 | 53 | ) -> PyResult<MixedIndex> { |
|
52 | Self::new(py, cindex, data) | |
|
54 | Self::new(py, cindex, data, default_header) | |
|
53 | 55 | } |
|
54 | 56 | |
|
55 | 57 | /// Compatibility layer used for Python consumers needing access to the C index |
@@ -364,6 +366,7 b' impl MixedIndex {' | |||
|
364 | 366 | py: Python, |
|
365 | 367 | cindex: PyObject, |
|
366 | 368 | data: PyObject, |
|
369 | header: u32, | |
|
367 | 370 | ) -> PyResult<MixedIndex> { |
|
368 | 371 | // Safety: we keep the buffer around inside the class as `index_mmap` |
|
369 | 372 | let (buf, bytes) = unsafe { mmap_keeparound(py, data)? }; |
@@ -371,7 +374,15 b' impl MixedIndex {' | |||
|
371 | 374 | Self::create_instance( |
|
372 | 375 | py, |
|
373 | 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 | 386 | RefCell::new(None), |
|
376 | 387 | RefCell::new(None), |
|
377 | 388 | RefCell::new(None), |
@@ -28,12 +28,12 b' use hg::utils::files::{' | |||
|
28 | 28 | get_bytes_from_os_str, get_bytes_from_os_string, get_path_from_bytes, |
|
29 | 29 | }; |
|
30 | 30 | use hg::utils::hg_path::{hg_path_to_path_buf, HgPath}; |
|
31 | use hg::DirstateStatus; | |
|
32 | 31 | use hg::PatternFileWarning; |
|
33 | 32 | use hg::Revision; |
|
34 | 33 | use hg::StatusError; |
|
35 | 34 | use hg::StatusOptions; |
|
36 | 35 | use hg::{self, narrow, sparse}; |
|
36 | use hg::{DirstateStatus, RevlogOpenOptions}; | |
|
37 | 37 | use log::info; |
|
38 | 38 | use rayon::prelude::*; |
|
39 | 39 | use std::borrow::Cow; |
@@ -383,6 +383,7 b' pub fn run(invocation: &crate::CliInvoca' | |||
|
383 | 383 | })?; |
|
384 | 384 | let working_directory_vfs = repo.working_directory_vfs(); |
|
385 | 385 | let store_vfs = repo.store_vfs(); |
|
386 | let revlog_open_options = repo.default_revlog_options(false)?; | |
|
386 | 387 | let res: Vec<_> = take(&mut ds_status.unsure) |
|
387 | 388 | .into_par_iter() |
|
388 | 389 | .map(|to_check| { |
@@ -396,6 +397,7 b' pub fn run(invocation: &crate::CliInvoca' | |||
|
396 | 397 | check_exec, |
|
397 | 398 | &manifest, |
|
398 | 399 | &to_check.path, |
|
400 | revlog_open_options, | |
|
399 | 401 | ) { |
|
400 | 402 | Err(HgError::IoError { .. }) => { |
|
401 | 403 | // IO errors most likely stem from the file being |
@@ -747,6 +749,7 b' fn unsure_is_modified(' | |||
|
747 | 749 | check_exec: bool, |
|
748 | 750 | manifest: &Manifest, |
|
749 | 751 | hg_path: &HgPath, |
|
752 | revlog_open_options: RevlogOpenOptions, | |
|
750 | 753 | ) -> Result<UnsureOutcome, HgError> { |
|
751 | 754 | let vfs = working_directory_vfs; |
|
752 | 755 | let fs_path = hg_path_to_path_buf(hg_path).expect("HgPath conversion"); |
@@ -778,7 +781,11 b' fn unsure_is_modified(' | |||
|
778 | 781 | if entry_flags != fs_flags { |
|
779 | 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 | 789 | let fs_len = fs_metadata.len(); |
|
783 | 790 | let file_node = entry.node_id()?; |
|
784 | 791 | let filelog_entry = filelog.entry_for_node(file_node).map_err(|_| { |
@@ -1,3 +1,4 b'' | |||
|
1 | import struct | |
|
1 | 2 | import unittest |
|
2 | 3 | |
|
3 | 4 | try: |
@@ -14,6 +15,8 b' else:' | |||
|
14 | 15 | |
|
15 | 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 | 21 | @unittest.skipIf( |
|
19 | 22 | rustext is None, |
@@ -22,24 +25,24 b' from mercurial.testing import revlog as ' | |||
|
22 | 25 | class RustRevlogIndexTest(revlogtesting.RevlogBasedTestBase): |
|
23 | 26 | def test_heads(self): |
|
24 | 27 | idx = self.parseindex() |
|
25 | rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined) | |
|
28 | rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined, header) | |
|
26 | 29 | self.assertEqual(rustidx.headrevs(), idx.headrevs()) |
|
27 | 30 | |
|
28 | 31 | def test_get_cindex(self): |
|
29 | 32 | # drop me once we no longer need the method for shortest node |
|
30 | 33 | idx = self.parseindex() |
|
31 | rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined) | |
|
34 | rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined, header) | |
|
32 | 35 | cidx = rustidx.get_cindex() |
|
33 | 36 | self.assertTrue(idx is cidx) |
|
34 | 37 | |
|
35 | 38 | def test_len(self): |
|
36 | 39 | idx = self.parseindex() |
|
37 | rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined) | |
|
40 | rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined, header) | |
|
38 | 41 | self.assertEqual(len(rustidx), len(idx)) |
|
39 | 42 | |
|
40 | 43 | def test_ancestors(self): |
|
41 | 44 | idx = self.parseindex() |
|
42 | rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined) | |
|
45 | rustidx = revlog.MixedIndex(idx, revlogtesting.data_non_inlined, header) | |
|
43 | 46 | lazy = LazyAncestors(rustidx, [3], 0, True) |
|
44 | 47 | # we have two more references to the index: |
|
45 | 48 | # - in its inner iterator for __contains__ and __bool__ |
General Comments 0
You need to be logged in to leave comments.
Login now