1 //! The "version 2" disk representation of the dirstate |
1 //! The "version 2" disk representation of the dirstate |
2 //! |
2 //! |
3 //! See `mercurial/helptext/internals/dirstate-v2.txt` |
3 //! See `mercurial/helptext/internals/dirstate-v2.txt` |
4 |
4 |
5 use crate::dirstate::TruncatedTimestamp; |
5 use crate::dirstate::{DirstateV2Data, TruncatedTimestamp}; |
6 use crate::dirstate_tree::dirstate_map::DirstateVersion; |
6 use crate::dirstate_tree::dirstate_map::DirstateVersion; |
7 use crate::dirstate_tree::dirstate_map::{self, DirstateMap, NodeRef}; |
7 use crate::dirstate_tree::dirstate_map::{self, DirstateMap, NodeRef}; |
8 use crate::dirstate_tree::path_with_basename::WithBasename; |
8 use crate::dirstate_tree::path_with_basename::WithBasename; |
9 use crate::errors::HgError; |
9 use crate::errors::HgError; |
10 use crate::utils::hg_path::HgPath; |
10 use crate::utils::hg_path::HgPath; |
83 ignore_patterns_hash: IgnorePatternsHash, |
83 ignore_patterns_hash: IgnorePatternsHash, |
84 } |
84 } |
85 |
85 |
86 /// Fields are documented in the *The data file format* |
86 /// Fields are documented in the *The data file format* |
87 /// section of `mercurial/helptext/internals/dirstate-v2.txt` |
87 /// section of `mercurial/helptext/internals/dirstate-v2.txt` |
88 #[derive(BytesCast)] |
88 #[derive(BytesCast, Debug)] |
89 #[repr(C)] |
89 #[repr(C)] |
90 pub(super) struct Node { |
90 pub(super) struct Node { |
91 full_path: PathSlice, |
91 full_path: PathSlice, |
92 |
92 |
93 /// In bytes from `self.full_path.start` |
93 /// In bytes from `self.full_path.start` |
123 const ALL_IGNORED_RECORDED = 1 <<15; |
123 const ALL_IGNORED_RECORDED = 1 <<15; |
124 } |
124 } |
125 } |
125 } |
126 |
126 |
127 /// Duration since the Unix epoch |
127 /// Duration since the Unix epoch |
128 #[derive(BytesCast, Copy, Clone)] |
128 #[derive(BytesCast, Copy, Clone, Debug)] |
129 #[repr(C)] |
129 #[repr(C)] |
130 struct PackedTruncatedTimestamp { |
130 struct PackedTruncatedTimestamp { |
131 truncated_seconds: U32Be, |
131 truncated_seconds: U32Be, |
132 nanoseconds: U32Be, |
132 nanoseconds: U32Be, |
133 } |
133 } |
151 /// of either some other node or of the repository root. |
151 /// of either some other node or of the repository root. |
152 /// |
152 /// |
153 /// Always sorted by ascending `full_path`, to allow binary search. |
153 /// Always sorted by ascending `full_path`, to allow binary search. |
154 /// Since nodes with the same parent nodes also have the same parent path, |
154 /// Since nodes with the same parent nodes also have the same parent path, |
155 /// only the `base_name`s need to be compared during binary search. |
155 /// only the `base_name`s need to be compared during binary search. |
156 #[derive(BytesCast, Copy, Clone)] |
156 #[derive(BytesCast, Copy, Clone, Debug)] |
157 #[repr(C)] |
157 #[repr(C)] |
158 struct ChildNodes { |
158 struct ChildNodes { |
159 start: Offset, |
159 start: Offset, |
160 len: Size, |
160 len: Size, |
161 } |
161 } |
162 |
162 |
163 /// A `HgPath` of `len` bytes |
163 /// A `HgPath` of `len` bytes |
164 #[derive(BytesCast, Copy, Clone)] |
164 #[derive(BytesCast, Copy, Clone, Debug)] |
165 #[repr(C)] |
165 #[repr(C)] |
166 struct PathSlice { |
166 struct PathSlice { |
167 start: Offset, |
167 start: Offset, |
168 len: PathSize, |
168 len: PathSize, |
169 } |
169 } |
415 Ok(m) |
415 Ok(m) |
416 } |
416 } |
417 |
417 |
418 fn assume_entry(&self) -> Result<DirstateEntry, DirstateV2ParseError> { |
418 fn assume_entry(&self) -> Result<DirstateEntry, DirstateV2ParseError> { |
419 // TODO: convert through raw bits instead? |
419 // TODO: convert through raw bits instead? |
420 let wdir_tracked = self.flags().contains(Flags::WDIR_TRACKED); |
420 let wc_tracked = self.flags().contains(Flags::WDIR_TRACKED); |
421 let p1_tracked = self.flags().contains(Flags::P1_TRACKED); |
421 let p1_tracked = self.flags().contains(Flags::P1_TRACKED); |
422 let p2_info = self.flags().contains(Flags::P2_INFO); |
422 let p2_info = self.flags().contains(Flags::P2_INFO); |
423 let mode_size = if self.flags().contains(Flags::HAS_MODE_AND_SIZE) |
423 let mode_size = if self.flags().contains(Flags::HAS_MODE_AND_SIZE) |
424 && !self.flags().contains(Flags::EXPECTED_STATE_IS_MODIFIED) |
424 && !self.flags().contains(Flags::EXPECTED_STATE_IS_MODIFIED) |
425 { |
425 { |
493 } |
493 } |
494 |
494 |
495 fn from_dirstate_entry( |
495 fn from_dirstate_entry( |
496 entry: &DirstateEntry, |
496 entry: &DirstateEntry, |
497 ) -> (Flags, U32Be, PackedTruncatedTimestamp) { |
497 ) -> (Flags, U32Be, PackedTruncatedTimestamp) { |
498 let ( |
498 let DirstateV2Data { |
499 wdir_tracked, |
499 wc_tracked, |
500 p1_tracked, |
500 p1_tracked, |
501 p2_info, |
501 p2_info, |
502 mode_size_opt, |
502 mode_size: mode_size_opt, |
503 mtime_opt, |
503 mtime: mtime_opt, |
504 fallback_exec, |
504 fallback_exec, |
505 fallback_symlink, |
505 fallback_symlink, |
506 ) = entry.v2_data(); |
506 } = entry.v2_data(); |
507 // TODO: convert throug raw flag bits instead? |
507 // TODO: convert through raw flag bits instead? |
508 let mut flags = Flags::empty(); |
508 let mut flags = Flags::empty(); |
509 flags.set(Flags::WDIR_TRACKED, wdir_tracked); |
509 flags.set(Flags::WDIR_TRACKED, wc_tracked); |
510 flags.set(Flags::P1_TRACKED, p1_tracked); |
510 flags.set(Flags::P1_TRACKED, p1_tracked); |
511 flags.set(Flags::P2_INFO, p2_info); |
511 flags.set(Flags::P2_INFO, p2_info); |
512 let size = if let Some((m, s)) = mode_size_opt { |
512 let size = if let Some((m, s)) = mode_size_opt { |
513 let exec_perm = m & (libc::S_IXUSR as u32) != 0; |
513 let exec_perm = m & (libc::S_IXUSR as u32) != 0; |
514 let is_symlink = m & (libc::S_IFMT as u32) == libc::S_IFLNK as u32; |
514 let is_symlink = m & (libc::S_IFMT as u32) == libc::S_IFLNK as u32; |
590 nodes: ChildNodes, |
590 nodes: ChildNodes, |
591 f: &mut impl FnMut(&'on_disk HgPath), |
591 f: &mut impl FnMut(&'on_disk HgPath), |
592 ) -> Result<(), DirstateV2ParseError> { |
592 ) -> Result<(), DirstateV2ParseError> { |
593 for node in read_nodes(on_disk, nodes)? { |
593 for node in read_nodes(on_disk, nodes)? { |
594 if let Some(entry) = node.entry()? { |
594 if let Some(entry) = node.entry()? { |
595 if entry.state().is_tracked() { |
595 if entry.tracked() { |
596 f(node.full_path(on_disk)?) |
596 f(node.full_path(on_disk)?) |
597 } |
597 } |
598 } |
598 } |
599 recur(on_disk, node.children, f)? |
599 recur(on_disk, node.children, f)? |
600 } |
600 } |