Show More
@@ -15,13 +15,13 b' pub enum EntryState {' | |||||
15 | /// comes first. |
|
15 | /// comes first. | |
16 | #[derive(Debug, PartialEq, Copy, Clone)] |
|
16 | #[derive(Debug, PartialEq, Copy, Clone)] | |
17 | pub struct DirstateEntry { |
|
17 | pub struct DirstateEntry { | |
18 | flags: Flags, |
|
18 | pub(crate) flags: Flags, | |
19 | mode_size: Option<(i32, i32)>, |
|
19 | mode_size: Option<(i32, i32)>, | |
20 | mtime: Option<i32>, |
|
20 | mtime: Option<i32>, | |
21 | } |
|
21 | } | |
22 |
|
22 | |||
23 | bitflags! { |
|
23 | bitflags! { | |
24 | struct Flags: u8 { |
|
24 | pub(crate) struct Flags: u8 { | |
25 | const WDIR_TRACKED = 1 << 0; |
|
25 | const WDIR_TRACKED = 1 << 0; | |
26 | const P1_TRACKED = 1 << 1; |
|
26 | const P1_TRACKED = 1 << 1; | |
27 | const P2_INFO = 1 << 2; |
|
27 | const P2_INFO = 1 << 2; | |
@@ -41,7 +41,7 b' pub const SIZE_FROM_OTHER_PARENT: i32 = ' | |||||
41 | pub const SIZE_NON_NORMAL: i32 = -1; |
|
41 | pub const SIZE_NON_NORMAL: i32 = -1; | |
42 |
|
42 | |||
43 | impl DirstateEntry { |
|
43 | impl DirstateEntry { | |
44 |
pub fn |
|
44 | pub fn from_v2_data( | |
45 | wdir_tracked: bool, |
|
45 | wdir_tracked: bool, | |
46 | p1_tracked: bool, |
|
46 | p1_tracked: bool, | |
47 | p2_info: bool, |
|
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 | fn v1_state(&self) -> EntryState { |
|
212 | fn v1_state(&self) -> EntryState { | |
197 | if !self.any_tracked() { |
|
213 | if !self.any_tracked() { | |
198 | // TODO: return an Option instead? |
|
214 | // TODO: return an Option instead? |
@@ -325,12 +325,7 b" impl<'tree, 'on_disk> NodeRef<'tree, 'on" | |||||
325 | pub(super) fn state( |
|
325 | pub(super) fn state( | |
326 | &self, |
|
326 | &self, | |
327 | ) -> Result<Option<EntryState>, DirstateV2ParseError> { |
|
327 | ) -> Result<Option<EntryState>, DirstateV2ParseError> { | |
328 | match self { |
|
328 | Ok(self.entry()?.map(|e| e.state())) | |
329 | NodeRef::InMemory(_path, node) => { |
|
|||
330 | Ok(node.data.as_entry().map(|entry| entry.state())) |
|
|||
331 | } |
|
|||
332 | NodeRef::OnDisk(node) => node.state(), |
|
|||
333 | } |
|
|||
334 | } |
|
329 | } | |
335 |
|
330 | |||
336 | pub(super) fn cached_directory_mtime( |
|
331 | pub(super) fn cached_directory_mtime( |
@@ -25,7 +25,7 b' use crate::utils::hg_path::HgPath;' | |||||
25 | use crate::DirstateEntry; |
|
25 | use crate::DirstateEntry; | |
26 | use crate::DirstateError; |
|
26 | use crate::DirstateError; | |
27 | use crate::DirstateParents; |
|
27 | use crate::DirstateParents; | |
28 | use crate::EntryState; |
|
28 | use bitflags::bitflags; | |
29 | use bytes_cast::unaligned::{I32Be, I64Be, U16Be, U32Be}; |
|
29 | use bytes_cast::unaligned::{I32Be, I64Be, U16Be, U32Be}; | |
30 | use bytes_cast::BytesCast; |
|
30 | use bytes_cast::BytesCast; | |
31 | use format_bytes::format_bytes; |
|
31 | use format_bytes::format_bytes; | |
@@ -131,18 +131,26 b' pub(super) struct Node {' | |||||
131 | pub(super) descendants_with_entry_count: Size, |
|
131 | pub(super) descendants_with_entry_count: Size, | |
132 | pub(super) tracked_descendants_count: Size, |
|
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 |
|
144 | /// * If none of `WDIR_TRACKED`, `P1_TRACKED`, `P2_INFO`, or `HAS_MTIME` | |
139 | /// represent a dirstate entry like in the v1 format. |
|
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 |
|
148 | /// * If none of `WDIR_TRACKED`, `P1_TRACKED`, `P2_INFO` are set, but | |
142 | /// as the `Timestamp` for the mtime of a cached directory. |
|
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 |
|
152 | /// The presence of this combination of flags means that at some point, | |
145 | /// the working directory was observed: |
|
153 | /// this path in the working directory was observed: | |
146 | /// |
|
154 | /// | |
147 | /// - To be a directory |
|
155 | /// - To be a directory | |
148 | /// - With the modification time as given by `Timestamp` |
|
156 | /// - With the modification time as given by `Timestamp` | |
@@ -161,11 +169,23 b' pub(super) struct Node {' | |||||
161 | /// of status that is not listing ignored files can skip calling |
|
169 | /// of status that is not listing ignored files can skip calling | |
162 | /// `std::fs::read_dir` again for this directory, iterate child |
|
170 | /// `std::fs::read_dir` again for this directory, iterate child | |
163 | /// dirstate nodes instead. |
|
171 | /// dirstate nodes instead. | |
164 | state: u8, |
|
172 | flags: Flags, | |
165 | data: Entry, |
|
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 | #[repr(C)] |
|
189 | #[repr(C)] | |
170 | struct Entry { |
|
190 | struct Entry { | |
171 | mode: I32Be, |
|
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 | pub(super) fn node_data( |
|
390 | pub(super) fn node_data( | |
365 | &self, |
|
391 | &self, | |
366 | ) -> Result<dirstate_map::NodeData, DirstateV2ParseError> { |
|
392 | ) -> Result<dirstate_map::NodeData, DirstateV2ParseError> { | |
367 | let entry = |state| { |
|
393 | if self.has_entry() { | |
368 |
dirstate_map::NodeData::Entry(self. |
|
394 | Ok(dirstate_map::NodeData::Entry(self.assume_entry())) | |
369 | }; |
|
395 | } else if let Some(&mtime) = self.cached_directory_mtime() { | |
370 |
|
396 | Ok(dirstate_map::NodeData::CachedDirectory { mtime }) | ||
371 |
|
|
397 | } else { | |
372 |
|
|
398 | 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), |
|
|||
381 | } |
|
399 | } | |
382 | } |
|
400 | } | |
383 |
|
401 | |||
384 | pub(super) fn cached_directory_mtime(&self) -> Option<&Timestamp> { |
|
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 | Some(self.data.as_timestamp()) |
|
404 | Some(self.data.as_timestamp()) | |
387 | } else { |
|
405 | } else { | |
388 | None |
|
406 | None | |
389 | } |
|
407 | } | |
390 | } |
|
408 | } | |
391 |
|
409 | |||
392 | pub(super) fn state( |
|
410 | fn assume_entry(&self) -> DirstateEntry { | |
393 | &self, |
|
411 | // TODO: convert through raw bits instead? | |
394 | ) -> Result<Option<EntryState>, DirstateV2ParseError> { |
|
412 | let wdir_tracked = self.flags.contains(Flags::WDIR_TRACKED); | |
395 | match self.state { |
|
413 | let p1_tracked = self.flags.contains(Flags::P1_TRACKED); | |
396 | b'\0' | b'd' => Ok(None), |
|
414 | let p2_info = self.flags.contains(Flags::P2_INFO); | |
397 | b'n' => Ok(Some(EntryState::Normal)), |
|
415 | let mode_size = if self.flags.contains(Flags::HAS_MODE_AND_SIZE) { | |
398 | b'a' => Ok(Some(EntryState::Added)), |
|
416 | Some((self.data.mode.into(), self.data.size.into())) | |
399 | b'r' => Ok(Some(EntryState::Removed)), |
|
417 | } else { | |
400 | b'm' => Ok(Some(EntryState::Merged)), |
|
418 | None | |
401 | _ => Err(DirstateV2ParseError), |
|
419 | }; | |
402 | } |
|
420 | let mtime = if self.flags.contains(Flags::HAS_MTIME) { | |
403 | } |
|
421 | Some(self.data.mtime.into()) | |
404 |
|
422 | } else { | ||
405 | fn entry_with_given_state(&self, state: EntryState) -> DirstateEntry { |
|
423 | None | |
406 | // For now, the on-disk representation of DirstateEntry in dirstate-v2 |
|
424 | }; | |
407 | // format is equivalent to that of dirstate-v1. When that changes, add |
|
425 | DirstateEntry::from_v2_data( | |
408 | // a new constructor. |
|
426 | wdir_tracked, | |
409 | DirstateEntry::from_v1_data( |
|
427 | p1_tracked, | |
410 |
|
|
428 | p2_info, | |
411 |
|
|
429 | mode_size, | |
412 | self.data.size.get(), |
|
430 | mtime, | |
413 | self.data.mtime.get(), |
|
|||
414 | ) |
|
431 | ) | |
415 | } |
|
432 | } | |
416 |
|
433 | |||
417 | pub(super) fn entry( |
|
434 | pub(super) fn entry( | |
418 | &self, |
|
435 | &self, | |
419 | ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> { |
|
436 | ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> { | |
420 | Ok(self |
|
437 | if self.has_entry() { | |
421 | .state()? |
|
438 | Ok(Some(self.assume_entry())) | |
422 | .map(|state| self.entry_with_given_state(state))) |
|
439 | } else { | |
|
440 | Ok(None) | |||
|
441 | } | |||
423 | } |
|
442 | } | |
424 |
|
443 | |||
425 | pub(super) fn children<'on_disk>( |
|
444 | pub(super) fn children<'on_disk>( | |
@@ -448,6 +467,37 b' impl Node {' | |||||
448 | } |
|
467 | } | |
449 |
|
468 | |||
450 | impl Entry { |
|
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 | fn from_timestamp(timestamp: Timestamp) -> Self { |
|
501 | fn from_timestamp(timestamp: Timestamp) -> Self { | |
452 | // Safety: both types implement the `ByteCast` trait, so we could |
|
502 | // Safety: both types implement the `ByteCast` trait, so we could | |
453 | // safely use `as_bytes` and `from_bytes` to do this conversion. Using |
|
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 | f: &mut impl FnMut(&'on_disk HgPath), |
|
596 | f: &mut impl FnMut(&'on_disk HgPath), | |
547 | ) -> Result<(), DirstateV2ParseError> { |
|
597 | ) -> Result<(), DirstateV2ParseError> { | |
548 | for node in read_nodes(on_disk, nodes)? { |
|
598 | for node in read_nodes(on_disk, nodes)? { | |
549 |
if let Some( |
|
599 | if let Some(entry) = node.entry()? { | |
550 | if state.is_tracked() { |
|
600 | if entry.state().is_tracked() { | |
551 | f(node.full_path(on_disk)?) |
|
601 | f(node.full_path(on_disk)?) | |
552 | } |
|
602 | } | |
553 | } |
|
603 | } | |
@@ -641,24 +691,19 b" impl Writer<'_, '_> {" | |||||
641 | }; |
|
691 | }; | |
642 | on_disk_nodes.push(match node { |
|
692 | on_disk_nodes.push(match node { | |
643 | NodeRef::InMemory(path, node) => { |
|
693 | NodeRef::InMemory(path, node) => { | |
644 |
let ( |
|
694 | let (flags, data) = match &node.data { | |
645 |
dirstate_map::NodeData::Entry(entry) => |
|
695 | dirstate_map::NodeData::Entry(entry) => { | |
646 |
entry |
|
696 | Entry::from_dirstate_entry(entry) | |
647 |
|
|
697 | } | |
648 | mode: entry.mode().into(), |
|
|||
649 | mtime: entry.mtime().into(), |
|
|||
650 | size: entry.size().into(), |
|
|||
651 | }, |
|
|||
652 | ), |
|
|||
653 | dirstate_map::NodeData::CachedDirectory { mtime } => { |
|
698 | dirstate_map::NodeData::CachedDirectory { mtime } => { | |
654 |
( |
|
699 | (Flags::HAS_MTIME, Entry::from_timestamp(*mtime)) | |
655 | } |
|
700 | } | |
656 | dirstate_map::NodeData::None => ( |
|
701 | dirstate_map::NodeData::None => ( | |
657 |
|
|
702 | Flags::empty(), | |
658 | Entry { |
|
703 | Entry { | |
659 | mode: 0.into(), |
|
704 | mode: 0.into(), | |
|
705 | size: 0.into(), | |||
660 | mtime: 0.into(), |
|
706 | mtime: 0.into(), | |
661 | size: 0.into(), |
|
|||
662 | }, |
|
707 | }, | |
663 | ), |
|
708 | ), | |
664 | }; |
|
709 | }; | |
@@ -676,7 +721,7 b" impl Writer<'_, '_> {" | |||||
676 | tracked_descendants_count: node |
|
721 | tracked_descendants_count: node | |
677 | .tracked_descendants_count |
|
722 | .tracked_descendants_count | |
678 | .into(), |
|
723 | .into(), | |
679 |
|
|
724 | flags, | |
680 | data, |
|
725 | data, | |
681 | } |
|
726 | } | |
682 | } |
|
727 | } |
@@ -34,7 +34,7 b' py_class!(pub class DirstateItem |py| {' | |||||
34 | mtime_opt = Some(mtime) |
|
34 | mtime_opt = Some(mtime) | |
35 | } |
|
35 | } | |
36 | } |
|
36 | } | |
37 |
let entry = DirstateEntry:: |
|
37 | let entry = DirstateEntry::from_v2_data( | |
38 | wc_tracked, p1_tracked, p2_info, mode_size_opt, mtime_opt, |
|
38 | wc_tracked, p1_tracked, p2_info, mode_size_opt, mtime_opt, | |
39 | ); |
|
39 | ); | |
40 | DirstateItem::create_instance(py, Cell::new(entry)) |
|
40 | DirstateItem::create_instance(py, Cell::new(entry)) |
General Comments 0
You need to be logged in to leave comments.
Login now