##// END OF EJS Templates
dirstate-v2: Store a bitfield on disk instead of v1-like state...
Simon Sapin -
r48951:ab5a7fdb default
parent child Browse files
Show More
@@ -15,13 +15,13 b' pub enum EntryState {'
15 15 /// comes first.
16 16 #[derive(Debug, PartialEq, Copy, Clone)]
17 17 pub struct DirstateEntry {
18 flags: Flags,
18 pub(crate) flags: Flags,
19 19 mode_size: Option<(i32, i32)>,
20 20 mtime: Option<i32>,
21 21 }
22 22
23 23 bitflags! {
24 struct Flags: u8 {
24 pub(crate) struct Flags: u8 {
25 25 const WDIR_TRACKED = 1 << 0;
26 26 const P1_TRACKED = 1 << 1;
27 27 const P2_INFO = 1 << 2;
@@ -41,7 +41,7 b' pub const SIZE_FROM_OTHER_PARENT: i32 = '
41 41 pub const SIZE_NON_NORMAL: i32 = -1;
42 42
43 43 impl DirstateEntry {
44 pub fn new(
44 pub fn from_v2_data(
45 45 wdir_tracked: bool,
46 46 p1_tracked: bool,
47 47 p2_info: bool,
@@ -193,6 +193,22 b' impl DirstateEntry {'
193 193 )
194 194 }
195 195
196 /// Returns `(wdir_tracked, p1_tracked, p2_info, mode_size, mtime)`
197 pub(crate) fn v2_data(
198 &self,
199 ) -> (bool, bool, bool, Option<(i32, i32)>, Option<i32>) {
200 if !self.any_tracked() {
201 // TODO: return an Option instead?
202 panic!("Accessing v1_state of an untracked DirstateEntry")
203 }
204 let wdir_tracked = self.flags.contains(Flags::WDIR_TRACKED);
205 let p1_tracked = self.flags.contains(Flags::P1_TRACKED);
206 let p2_info = self.flags.contains(Flags::P2_INFO);
207 let mode_size = self.mode_size;
208 let mtime = self.mtime;
209 (wdir_tracked, p1_tracked, p2_info, mode_size, mtime)
210 }
211
196 212 fn v1_state(&self) -> EntryState {
197 213 if !self.any_tracked() {
198 214 // TODO: return an Option instead?
@@ -325,12 +325,7 b" impl<'tree, 'on_disk> NodeRef<'tree, 'on"
325 325 pub(super) fn state(
326 326 &self,
327 327 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
328 match self {
329 NodeRef::InMemory(_path, node) => {
330 Ok(node.data.as_entry().map(|entry| entry.state()))
331 }
332 NodeRef::OnDisk(node) => node.state(),
333 }
328 Ok(self.entry()?.map(|e| e.state()))
334 329 }
335 330
336 331 pub(super) fn cached_directory_mtime(
@@ -25,7 +25,7 b' use crate::utils::hg_path::HgPath;'
25 25 use crate::DirstateEntry;
26 26 use crate::DirstateError;
27 27 use crate::DirstateParents;
28 use crate::EntryState;
28 use bitflags::bitflags;
29 29 use bytes_cast::unaligned::{I32Be, I64Be, U16Be, U32Be};
30 30 use bytes_cast::BytesCast;
31 31 use format_bytes::format_bytes;
@@ -131,18 +131,26 b' pub(super) struct Node {'
131 131 pub(super) descendants_with_entry_count: Size,
132 132 pub(super) tracked_descendants_count: Size,
133 133
134 /// Depending on the value of `state`:
134 /// Depending on the bits in `flags`:
135 ///
136 /// * If any of `WDIR_TRACKED`, `P1_TRACKED`, or `P2_INFO` are set, the
137 /// node has an entry.
135 138 ///
136 /// * A null byte: `data` is not used.
139 /// - If `HAS_MODE_AND_SIZE` is set, `data.mode` and `data.size` are
140 /// meaningful. Otherwise they are set to zero
141 /// - If `HAS_MTIME` is set, `data.mtime` is meaningful. Otherwise it is
142 /// set to zero.
137 143 ///
138 /// * A `n`, `a`, `r`, or `m` ASCII byte: `state` and `data` together
139 /// represent a dirstate entry like in the v1 format.
144 /// * If none of `WDIR_TRACKED`, `P1_TRACKED`, `P2_INFO`, or `HAS_MTIME`
145 /// are set, the node does not have an entry and `data` is set to all
146 /// zeros.
140 147 ///
141 /// * A `d` ASCII byte: the bytes of `data` should instead be interpreted
142 /// as the `Timestamp` for the mtime of a cached directory.
148 /// * If none of `WDIR_TRACKED`, `P1_TRACKED`, `P2_INFO` are set, but
149 /// `HAS_MTIME` is set, the bytes of `data` should instead be
150 /// interpreted as the `Timestamp` for the mtime of a cached directory.
143 151 ///
144 /// The presence of this state means that at some point, this path in
145 /// the working directory was observed:
152 /// The presence of this combination of flags means that at some point,
153 /// this path in the working directory was observed:
146 154 ///
147 155 /// - To be a directory
148 156 /// - With the modification time as given by `Timestamp`
@@ -161,11 +169,23 b' pub(super) struct Node {'
161 169 /// of status that is not listing ignored files can skip calling
162 170 /// `std::fs::read_dir` again for this directory, iterate child
163 171 /// dirstate nodes instead.
164 state: u8,
172 flags: Flags,
165 173 data: Entry,
166 174 }
167 175
168 #[derive(BytesCast, Copy, Clone)]
176 bitflags! {
177 #[derive(BytesCast)]
178 #[repr(C)]
179 struct Flags: u8 {
180 const WDIR_TRACKED = 1 << 0;
181 const P1_TRACKED = 1 << 1;
182 const P2_INFO = 1 << 2;
183 const HAS_MODE_AND_SIZE = 1 << 3;
184 const HAS_MTIME = 1 << 4;
185 }
186 }
187
188 #[derive(BytesCast, Copy, Clone, Debug)]
169 189 #[repr(C)]
170 190 struct Entry {
171 191 mode: I32Be,
@@ -361,65 +381,64 b' impl Node {'
361 381 })
362 382 }
363 383
384 fn has_entry(&self) -> bool {
385 self.flags.intersects(
386 Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO,
387 )
388 }
389
364 390 pub(super) fn node_data(
365 391 &self,
366 392 ) -> Result<dirstate_map::NodeData, DirstateV2ParseError> {
367 let entry = |state| {
368 dirstate_map::NodeData::Entry(self.entry_with_given_state(state))
369 };
370
371 match self.state {
372 b'\0' => Ok(dirstate_map::NodeData::None),
373 b'd' => Ok(dirstate_map::NodeData::CachedDirectory {
374 mtime: *self.data.as_timestamp(),
375 }),
376 b'n' => Ok(entry(EntryState::Normal)),
377 b'a' => Ok(entry(EntryState::Added)),
378 b'r' => Ok(entry(EntryState::Removed)),
379 b'm' => Ok(entry(EntryState::Merged)),
380 _ => Err(DirstateV2ParseError),
393 if self.has_entry() {
394 Ok(dirstate_map::NodeData::Entry(self.assume_entry()))
395 } else if let Some(&mtime) = self.cached_directory_mtime() {
396 Ok(dirstate_map::NodeData::CachedDirectory { mtime })
397 } else {
398 Ok(dirstate_map::NodeData::None)
381 399 }
382 400 }
383 401
384 402 pub(super) fn cached_directory_mtime(&self) -> Option<&Timestamp> {
385 if self.state == b'd' {
403 if self.flags.contains(Flags::HAS_MTIME) && !self.has_entry() {
386 404 Some(self.data.as_timestamp())
387 405 } else {
388 406 None
389 407 }
390 408 }
391 409
392 pub(super) fn state(
393 &self,
394 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
395 match self.state {
396 b'\0' | b'd' => Ok(None),
397 b'n' => Ok(Some(EntryState::Normal)),
398 b'a' => Ok(Some(EntryState::Added)),
399 b'r' => Ok(Some(EntryState::Removed)),
400 b'm' => Ok(Some(EntryState::Merged)),
401 _ => Err(DirstateV2ParseError),
402 }
403 }
404
405 fn entry_with_given_state(&self, state: EntryState) -> DirstateEntry {
406 // For now, the on-disk representation of DirstateEntry in dirstate-v2
407 // format is equivalent to that of dirstate-v1. When that changes, add
408 // a new constructor.
409 DirstateEntry::from_v1_data(
410 state,
411 self.data.mode.get(),
412 self.data.size.get(),
413 self.data.mtime.get(),
410 fn assume_entry(&self) -> DirstateEntry {
411 // TODO: convert through raw bits instead?
412 let wdir_tracked = self.flags.contains(Flags::WDIR_TRACKED);
413 let p1_tracked = self.flags.contains(Flags::P1_TRACKED);
414 let p2_info = self.flags.contains(Flags::P2_INFO);
415 let mode_size = if self.flags.contains(Flags::HAS_MODE_AND_SIZE) {
416 Some((self.data.mode.into(), self.data.size.into()))
417 } else {
418 None
419 };
420 let mtime = if self.flags.contains(Flags::HAS_MTIME) {
421 Some(self.data.mtime.into())
422 } else {
423 None
424 };
425 DirstateEntry::from_v2_data(
426 wdir_tracked,
427 p1_tracked,
428 p2_info,
429 mode_size,
430 mtime,
414 431 )
415 432 }
416 433
417 434 pub(super) fn entry(
418 435 &self,
419 436 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
420 Ok(self
421 .state()?
422 .map(|state| self.entry_with_given_state(state)))
437 if self.has_entry() {
438 Ok(Some(self.assume_entry()))
439 } else {
440 Ok(None)
441 }
423 442 }
424 443
425 444 pub(super) fn children<'on_disk>(
@@ -448,6 +467,37 b' impl Node {'
448 467 }
449 468
450 469 impl Entry {
470 fn from_dirstate_entry(entry: &DirstateEntry) -> (Flags, Self) {
471 let (wdir_tracked, p1_tracked, p2_info, mode_size_opt, mtime_opt) =
472 entry.v2_data();
473 // TODO: convert throug raw flag bits instead?
474 let mut flags = Flags::empty();
475 flags.set(Flags::WDIR_TRACKED, wdir_tracked);
476 flags.set(Flags::P1_TRACKED, p1_tracked);
477 flags.set(Flags::P2_INFO, p2_info);
478 let (mode, size, mtime);
479 if let Some((m, s)) = mode_size_opt {
480 mode = m;
481 size = s;
482 flags.insert(Flags::HAS_MODE_AND_SIZE)
483 } else {
484 mode = 0;
485 size = 0;
486 }
487 if let Some(m) = mtime_opt {
488 mtime = m;
489 flags.insert(Flags::HAS_MTIME);
490 } else {
491 mtime = 0;
492 }
493 let raw_entry = Entry {
494 mode: mode.into(),
495 size: size.into(),
496 mtime: mtime.into(),
497 };
498 (flags, raw_entry)
499 }
500
451 501 fn from_timestamp(timestamp: Timestamp) -> Self {
452 502 // Safety: both types implement the `ByteCast` trait, so we could
453 503 // safely use `as_bytes` and `from_bytes` to do this conversion. Using
@@ -546,8 +596,8 b" pub(crate) fn for_each_tracked_path<'on_"
546 596 f: &mut impl FnMut(&'on_disk HgPath),
547 597 ) -> Result<(), DirstateV2ParseError> {
548 598 for node in read_nodes(on_disk, nodes)? {
549 if let Some(state) = node.state()? {
550 if state.is_tracked() {
599 if let Some(entry) = node.entry()? {
600 if entry.state().is_tracked() {
551 601 f(node.full_path(on_disk)?)
552 602 }
553 603 }
@@ -641,24 +691,19 b" impl Writer<'_, '_> {"
641 691 };
642 692 on_disk_nodes.push(match node {
643 693 NodeRef::InMemory(path, node) => {
644 let (state, data) = match &node.data {
645 dirstate_map::NodeData::Entry(entry) => (
646 entry.state().into(),
647 Entry {
648 mode: entry.mode().into(),
649 mtime: entry.mtime().into(),
650 size: entry.size().into(),
651 },
652 ),
694 let (flags, data) = match &node.data {
695 dirstate_map::NodeData::Entry(entry) => {
696 Entry::from_dirstate_entry(entry)
697 }
653 698 dirstate_map::NodeData::CachedDirectory { mtime } => {
654 (b'd', Entry::from_timestamp(*mtime))
699 (Flags::HAS_MTIME, Entry::from_timestamp(*mtime))
655 700 }
656 701 dirstate_map::NodeData::None => (
657 b'\0',
702 Flags::empty(),
658 703 Entry {
659 704 mode: 0.into(),
705 size: 0.into(),
660 706 mtime: 0.into(),
661 size: 0.into(),
662 707 },
663 708 ),
664 709 };
@@ -676,7 +721,7 b" impl Writer<'_, '_> {"
676 721 tracked_descendants_count: node
677 722 .tracked_descendants_count
678 723 .into(),
679 state,
724 flags,
680 725 data,
681 726 }
682 727 }
@@ -34,7 +34,7 b' py_class!(pub class DirstateItem |py| {'
34 34 mtime_opt = Some(mtime)
35 35 }
36 36 }
37 let entry = DirstateEntry::new(
37 let entry = DirstateEntry::from_v2_data(
38 38 wc_tracked, p1_tracked, p2_info, mode_size_opt, mtime_opt,
39 39 );
40 40 DirstateItem::create_instance(py, Cell::new(entry))
General Comments 0
You need to be logged in to leave comments. Login now