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 |
|
|
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 |
|
|
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 |
|
|
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. |
|
|
369 | }; | |
|
370 | ||
|
371 |
|
|
|
372 |
|
|
|
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 |
|
|
|
411 |
|
|
|
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( |
|
|
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 ( |
|
|
645 |
dirstate_map::NodeData::Entry(entry) => |
|
|
646 |
entry |
|
|
647 |
|
|
|
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 |
( |
|
|
699 | (Flags::HAS_MTIME, Entry::from_timestamp(*mtime)) | |
|
655 | 700 | } |
|
656 | 701 | dirstate_map::NodeData::None => ( |
|
657 |
|
|
|
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 |
|
|
|
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:: |
|
|
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