diff --git a/mercurial/dirstateutils/v2.py b/mercurial/dirstateutils/v2.py --- a/mercurial/dirstateutils/v2.py +++ b/mercurial/dirstateutils/v2.py @@ -18,7 +18,7 @@ # Must match the constant of the same name in # `rust/hg-core/src/dirstate_tree/on_disk.rs` TREE_METADATA_SIZE = 44 -NODE_SIZE = 43 +NODE_SIZE = 44 # Must match the `TreeMetadata` Rust struct in @@ -50,7 +50,7 @@ # * 4 bytes: expected size # * 4 bytes: mtime seconds # * 4 bytes: mtime nanoseconds -NODE = struct.Struct('>LHHLHLLLLBlll') +NODE = struct.Struct('>LHHLHLLLLHlll') assert TREE_METADATA_SIZE == TREE_METADATA.size diff --git a/mercurial/helptext/internals/dirstate-v2.txt b/mercurial/helptext/internals/dirstate-v2.txt --- a/mercurial/helptext/internals/dirstate-v2.txt +++ b/mercurial/helptext/internals/dirstate-v2.txt @@ -372,7 +372,7 @@ This counter is used to implement `has_tracked_dir`. * Offset 30: - A single `flags` byte that packs some boolean values as bits. + A `flags` fields that packs some boolean values as bits of a 16-bit integer. Starting from least-significant, bit masks are:: WDIR_TRACKED = 1 << 0 @@ -384,22 +384,29 @@ MODE_IS_SYMLINK = 1 << 6 The meaning of each bit is described below. - Other bits are unset. -* Offset 31: + Other bits are unset. + They may be assigned meaning if the future, + with the limitation that Mercurial versions that pre-date such meaning + will always reset those bits to unset when writing nodes. + (A new node is written for any mutation in its subtree, + leaving the bytes of the old node unreachable + until the data file is rewritten entirely.) + +* Offset 32: A `size` field described below, as a 32-bit integer. Unlike in dirstate-v1, negative values are not used. -* Offset 35: +* Offset 36: The seconds component of an `mtime` field described below, as a 32-bit integer. Unlike in dirstate-v1, negative values are not used. -* Offset 39: +* Offset 40: The nanoseconds component of an `mtime` field described below, as a 32-bit integer. -* (Offset 43: end of this node) +* (Offset 44: end of this node) The meaning of the boolean values packed in `flags` is: diff --git a/rust/hg-core/src/dirstate_tree/on_disk.rs b/rust/hg-core/src/dirstate_tree/on_disk.rs --- a/rust/hg-core/src/dirstate_tree/on_disk.rs +++ b/rust/hg-core/src/dirstate_tree/on_disk.rs @@ -33,7 +33,7 @@ /// Must match constants of the same names in `mercurial/dirstateutils/v2.py` const TREE_METADATA_SIZE: usize = 44; -const NODE_SIZE: usize = 43; +const NODE_SIZE: usize = 44; /// Make sure that size-affecting changes are made knowingly #[allow(unused)] @@ -94,15 +94,14 @@ children: ChildNodes, pub(super) descendants_with_entry_count: Size, pub(super) tracked_descendants_count: Size, - flags: Flags, + flags: U16Be, size: U32Be, mtime: PackedTruncatedTimestamp, } bitflags! { - #[derive(BytesCast)] #[repr(C)] - struct Flags: u8 { + struct Flags: u16 { const WDIR_TRACKED = 1 << 0; const P1_TRACKED = 1 << 1; const P2_INFO = 1 << 2; @@ -296,8 +295,12 @@ }) } + fn flags(&self) -> Flags { + Flags::from_bits_truncate(self.flags.get()) + } + fn has_entry(&self) -> bool { - self.flags.intersects( + self.flags().intersects( Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO, ) } @@ -318,7 +321,7 @@ &self, ) -> Result, DirstateV2ParseError> { Ok( - if self.flags.contains(Flags::HAS_MTIME) && !self.has_entry() { + if self.flags().contains(Flags::HAS_MTIME) && !self.has_entry() { Some(self.mtime.try_into()?) } else { None @@ -327,12 +330,12 @@ } fn synthesize_unix_mode(&self) -> u32 { - let file_type = if self.flags.contains(Flags::MODE_IS_SYMLINK) { + let file_type = if self.flags().contains(Flags::MODE_IS_SYMLINK) { libc::S_IFLNK } else { libc::S_IFREG }; - let permisions = if self.flags.contains(Flags::MODE_EXEC_PERM) { + let permisions = if self.flags().contains(Flags::MODE_EXEC_PERM) { 0o755 } else { 0o644 @@ -342,15 +345,15 @@ fn assume_entry(&self) -> DirstateEntry { // TODO: convert through raw bits instead? - let wdir_tracked = self.flags.contains(Flags::WDIR_TRACKED); - let p1_tracked = self.flags.contains(Flags::P1_TRACKED); - let p2_info = self.flags.contains(Flags::P2_INFO); - let mode_size = if self.flags.contains(Flags::HAS_MODE_AND_SIZE) { + let wdir_tracked = self.flags().contains(Flags::WDIR_TRACKED); + let p1_tracked = self.flags().contains(Flags::P1_TRACKED); + let p2_info = self.flags().contains(Flags::P2_INFO); + let mode_size = if self.flags().contains(Flags::HAS_MODE_AND_SIZE) { Some((self.synthesize_unix_mode(), self.size.into())) } else { None }; - let mtime = if self.flags.contains(Flags::HAS_MTIME) { + let mtime = if self.flags().contains(Flags::HAS_MTIME) { Some(self.mtime.truncated_seconds.into()) } else { None @@ -600,7 +603,7 @@ tracked_descendants_count: node .tracked_descendants_count .into(), - flags, + flags: flags.bits().into(), size, mtime, }