Mercurial > forks > mercurial
changeset 53237:1951f36f0327
branching: merge with stable
author | Pierre-Yves David <pierre-yves.david@octobus.net> |
---|---|
date | Wed, 30 Apr 2025 02:15:23 +0200 |
parents | 5392f4fd6764 (current diff) 4ac82bcae209 (diff) |
children | b09ae3ab76be |
files | rust/hg-core/src/revlog/mod.rs tests/test-persistent-nodemap.t |
diffstat | 4 files changed, 95 insertions(+), 25 deletions(-) [+] |
line wrap: on
line diff
--- a/rust/hg-core/src/revlog/mod.rs Tue Apr 01 10:42:42 2025 +0200 +++ b/rust/hg-core/src/revlog/mod.rs Wed Apr 30 02:15:23 2025 +0200 @@ -14,6 +14,7 @@ use inner_revlog::RevisionBuffer; use memmap2::MmapOptions; pub use node::{FromHexError, Node, NodePrefix, NULL_NODE, NULL_NODE_ID}; +use nodemap::read_persistent_nodemap; use options::RevlogOpenOptions; pub mod changelog; pub mod compression; @@ -346,14 +347,7 @@ let nodemap = if index.is_inline() || !options.use_nodemap { None } else { - NodeMapDocket::read_from_file(store_vfs, index_path)?.map( - |(docket, data)| { - nodemap::NodeTree::load_bytes( - Box::new(data), - docket.data_length, - ) - }, - ) + read_persistent_nodemap(store_vfs, index_path, &index)? }; let nodemap = nodemap_for_test.or(nodemap);
--- a/rust/hg-core/src/revlog/nodemap.rs Tue Apr 01 10:42:42 2025 +0200 +++ b/rust/hg-core/src/revlog/nodemap.rs Wed Apr 30 02:15:23 2025 +0200 @@ -12,8 +12,12 @@ //! Following existing implicit conventions, the "nodemap" terminology //! is used in a more abstract context. +use crate::errors::HgError; +use crate::revlog::NodeMapDocket; +use crate::vfs::VfsImpl; use crate::UncheckedRevision; +use super::BaseRevision; use super::{ node::NULL_NODE, Node, NodePrefix, Revision, RevlogIndex, NULL_REVISION, }; @@ -24,10 +28,11 @@ use std::mem::{self, align_of, size_of}; use std::ops::Deref; use std::ops::Index; +use std::path::Path; type NodeTreeBuffer = Box<dyn Deref<Target = [u8]> + Send + Sync>; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, derive_more::Display)] pub enum NodeMapError { /// A `NodePrefix` matches several [`Revision`]s. /// @@ -297,6 +302,42 @@ } } +/// Read the persistent nodemap corresponding to the `index` at `index_path`. +/// `index` and `index_path` MUST reference the same index. +pub(super) fn read_persistent_nodemap( + store_vfs: &VfsImpl, + index_path: &Path, + index: &impl RevlogIndex, +) -> Result<Option<NodeTree>, HgError> { + if let Some((docket, data)) = + NodeMapDocket::read_from_file(store_vfs, index_path)? + { + let mut nodemap = + NodeTree::load_bytes(Box::new(data), docket.data_length); + if let Some(valid_tip_rev) = index.check_revision(docket.tip_rev) { + let valid_node = + index.node(valid_tip_rev) == Some(&docket.tip_node); + if valid_node && (valid_tip_rev.0 as usize) < index.len() { + // The index moved forward but wasn't rewritten + nodemap + .catch_up_to_index(index, valid_tip_rev) + .map_err(|e| HgError::abort_simple(e.to_string()))?; + return Ok(Some(nodemap)); + } + } + if !index.is_empty() { + // The nodemap exists but is invalid somehow (strip, corruption...) + // so we rebuild it from scratch. + let mut nodemap = NodeTree::new(Box::<Vec<_>>::default()); + nodemap + .catch_up_to_index(index, Revision(0)) + .map_err(|e| HgError::abort_simple(e.to_string()))?; + return Ok(Some(nodemap)); + } + } + Ok(None) +} + impl NodeTree { /// Initiate a NodeTree from an immutable slice-like of `Block` /// @@ -529,6 +570,30 @@ Ok(()) } + /// Insert all [`Revision`] from `from` inclusive, up to + /// [`RevlogIndex::len`] exclusive. + /// + /// `from` must be a valid revision for `index`, most likely should be the + /// tip of the nodemap docket. + /// + /// Useful for updating the [`NodeTree`] when the index has moved forward. + pub fn catch_up_to_index( + &mut self, + index: &impl RevlogIndex, + from: Revision, + ) -> Result<(), NodeMapError> { + for r in (from.0)..index.len() as BaseRevision { + let rev = Revision(r); + // in this case node() won't ever return None + self.insert( + index, + index.node(rev).expect("node should exist"), + rev, + )?; + } + Ok(()) + } + /// Make the whole `NodeTree` logically empty, without touching the /// immutable part. pub fn invalidate_all(&mut self) {
--- a/rust/hg-core/src/revlog/nodemap_docket.rs Tue Apr 01 10:42:42 2025 +0200 +++ b/rust/hg-core/src/revlog/nodemap_docket.rs Wed Apr 30 02:15:23 2025 +0200 @@ -1,14 +1,23 @@ -use crate::errors::{HgError, HgResultExt}; +use crate::{ + errors::{HgError, HgResultExt}, + BaseRevision, +}; use bytes_cast::{unaligned, BytesCast}; use memmap2::Mmap; use std::path::{Path, PathBuf}; use crate::vfs::VfsImpl; +use super::{Node, UncheckedRevision}; + const ONDISK_VERSION: u8 = 1; pub(super) struct NodeMapDocket { pub data_length: usize, + /// This is [`UncheckedRevision`] and not [`Revision`] because the nodemap + /// can be out-of-date (because of strip for example) + pub tip_rev: UncheckedRevision, + pub tip_node: Node, // TODO: keep here more of the data from `parse()` when we need it } @@ -16,7 +25,7 @@ #[repr(C)] struct DocketHeader { uid_size: u8, - _tip_rev: unaligned::U64Be, + tip_rev: unaligned::U64Be, data_length: unaligned::U64Be, _data_unused: unaligned::U64Be, tip_node_size: unaligned::U64Be, @@ -66,10 +75,16 @@ let tip_node_size = header.tip_node_size.get() as usize; let data_length = header.data_length.get() as usize; let (uid, rest) = parse(u8::slice_from_bytes(rest, uid_size))?; - let (_tip_node, _rest) = + let (tip_node, _rest) = parse(u8::slice_from_bytes(rest, tip_node_size))?; let uid = parse(std::str::from_utf8(uid))?; - let docket = NodeMapDocket { data_length }; + let tip_node = parse(Node::from_bytes(tip_node))?.0.to_owned(); + let revnum: BaseRevision = parse(header.tip_rev.get().try_into())?; + let docket = NodeMapDocket { + data_length, + tip_rev: revnum.into(), + tip_node, + }; let data_path = rawdata_path(&docket_path, uid); // TODO: use `vfs.read()` here when the `persistent-nodemap.mmap`
--- a/tests/test-persistent-nodemap.t Tue Apr 01 10:42:42 2025 +0200 +++ b/tests/test-persistent-nodemap.t Wed Apr 30 02:15:23 2025 +0200 @@ -364,8 +364,8 @@ data-length: 121088 data-unused: 0 data-unused: 0.000% - $ hg log -r "$NODE" -T '{rev}\n' - 5003 + $ env RHG_ON_UNSUPPORTED=abort hg cat -r $NODE bar + bar2 changelog altered ----------------- @@ -410,8 +410,9 @@ data-length: 121088 data-unused: 0 data-unused: 0.000% - $ hg log -r "$OTHERNODE" -T '{rev}\n' - 5002 + $ env RHG_ON_UNSUPPORTED=abort hg cat -r $OTHERNODE babar + bar + missing data file ----------------- @@ -424,14 +425,9 @@ mercurial don't crash - $ hg log -r . - changeset: 5002:b355ef8adce0 - tag: tip - parent: 4998:d918ad6d18d3 - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: babar - + $ env RHG_ON_UNSUPPORTED=abort hg cat -r $OTHERNODE babar + bar + $ hg debugnodemap --metadata $ hg debugupdatecache