--- a/rust/hg-core/src/dirstate/dirstate_map.rs Tue Sep 28 09:40:57 2021 +0200
+++ b/rust/hg-core/src/dirstate/dirstate_map.rs Mon Sep 20 19:18:21 2021 +0200
@@ -452,7 +452,8 @@
.collect();
let mut non_normal = [
- b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
+ b"f1", b"f2", b"f4", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa",
+ b"fb",
]
.iter()
.map(|x| HgPathBuf::from_bytes(x.as_ref()))
--- a/rust/hg-core/src/dirstate/entry.rs Tue Sep 28 09:40:57 2021 +0200
+++ b/rust/hg-core/src/dirstate/entry.rs Mon Sep 20 19:18:21 2021 +0200
@@ -1,4 +1,5 @@
use crate::errors::HgError;
+use bitflags::bitflags;
use std::convert::TryFrom;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -14,12 +15,25 @@
/// comes first.
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct DirstateEntry {
- state: EntryState,
+ flags: Flags,
mode: i32,
size: i32,
mtime: i32,
}
+bitflags! {
+ struct Flags: u8 {
+ const WDIR_TRACKED = 1 << 0;
+ const P1_TRACKED = 1 << 1;
+ const P2_TRACKED = 1 << 2;
+ const POSSIBLY_DIRTY = 1 << 3;
+ const MERGED = 1 << 4;
+ const CLEAN_P1 = 1 << 5;
+ const CLEAN_P2 = 1 << 6;
+ const ENTRYLESS_TREE_NODE = 1 << 7;
+ }
+}
+
pub const V1_RANGEMASK: i32 = 0x7FFFFFFF;
pub const MTIME_UNSET: i32 = -1;
@@ -39,11 +53,89 @@
size: i32,
mtime: i32,
) -> Self {
+ match state {
+ EntryState::Normal => {
+ if size == SIZE_FROM_OTHER_PARENT {
+ Self::new_from_p2()
+ } else if size == SIZE_NON_NORMAL {
+ Self::new_possibly_dirty()
+ } else if mtime == MTIME_UNSET {
+ Self {
+ flags: Flags::WDIR_TRACKED
+ | Flags::P1_TRACKED
+ | Flags::POSSIBLY_DIRTY,
+ mode,
+ size,
+ mtime: 0,
+ }
+ } else {
+ Self {
+ flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
+ mode,
+ size,
+ mtime,
+ }
+ }
+ }
+ EntryState::Added => Self::new_added(),
+ EntryState::Removed => Self {
+ flags: if size == SIZE_NON_NORMAL {
+ Flags::P1_TRACKED // might not be true because of rename ?
+ | Flags::P2_TRACKED // might not be true because of rename ?
+ | Flags::MERGED
+ } else if size == SIZE_FROM_OTHER_PARENT {
+ // We don’t know if P1_TRACKED should be set (file history)
+ Flags::P2_TRACKED | Flags::CLEAN_P2
+ } else {
+ Flags::P1_TRACKED
+ },
+ mode: 0,
+ size: 0,
+ mtime: 0,
+ },
+ EntryState::Merged => Self::new_merged(),
+ }
+ }
+
+ fn new_from_p2() -> Self {
Self {
- state,
- mode,
- size,
- mtime,
+ // might be missing P1_TRACKED
+ flags: Flags::WDIR_TRACKED | Flags::P2_TRACKED | Flags::CLEAN_P2,
+ mode: 0,
+ size: SIZE_FROM_OTHER_PARENT,
+ mtime: MTIME_UNSET,
+ }
+ }
+
+ fn new_possibly_dirty() -> Self {
+ Self {
+ flags: Flags::WDIR_TRACKED
+ | Flags::P1_TRACKED
+ | Flags::POSSIBLY_DIRTY,
+ mode: 0,
+ size: SIZE_NON_NORMAL,
+ mtime: MTIME_UNSET,
+ }
+ }
+
+ fn new_added() -> Self {
+ Self {
+ flags: Flags::WDIR_TRACKED,
+ mode: 0,
+ size: SIZE_NON_NORMAL,
+ mtime: MTIME_UNSET,
+ }
+ }
+
+ fn new_merged() -> Self {
+ Self {
+ flags: Flags::WDIR_TRACKED
+ | Flags::P1_TRACKED // might not be true because of rename ?
+ | Flags::P2_TRACKED // might not be true because of rename ?
+ | Flags::MERGED,
+ mode: 0,
+ size: SIZE_NON_NORMAL,
+ mtime: MTIME_UNSET,
}
}
@@ -52,28 +144,57 @@
/// `size` is expected to be zero, `SIZE_NON_NORMAL`, or
/// `SIZE_FROM_OTHER_PARENT`
pub fn new_removed(size: i32) -> Self {
- Self {
- state: EntryState::Removed,
- mode: 0,
- size,
- mtime: 0,
- }
+ Self::from_v1_data(EntryState::Removed, 0, size, 0)
}
/// TODO: refactor `DirstateMap::add_file` to not take a `DirstateEntry`
/// parameter and remove this constructor
pub fn new_for_add_file(mode: i32, size: i32, mtime: i32) -> Self {
- Self {
- // XXX Arbitrary default value since the value is determined later
- state: EntryState::Normal,
- mode,
- size,
- mtime,
- }
+ // XXX Arbitrary default value since the value is determined later
+ let state = EntryState::Normal;
+ Self::from_v1_data(state, mode, size, mtime)
+ }
+
+ fn tracked_in_any_parent(&self) -> bool {
+ self.flags.intersects(Flags::P1_TRACKED | Flags::P2_TRACKED)
+ }
+
+ fn removed(&self) -> bool {
+ self.tracked_in_any_parent()
+ && !self.flags.contains(Flags::WDIR_TRACKED)
+ }
+
+ fn merged_removed(&self) -> bool {
+ self.removed() && self.flags.contains(Flags::MERGED)
+ }
+
+ fn from_p2_removed(&self) -> bool {
+ self.removed() && self.flags.contains(Flags::CLEAN_P2)
+ }
+
+ fn merged(&self) -> bool {
+ self.flags.contains(Flags::WDIR_TRACKED | Flags::MERGED)
+ }
+
+ fn added(&self) -> bool {
+ self.flags.contains(Flags::WDIR_TRACKED)
+ && !self.tracked_in_any_parent()
+ }
+
+ fn from_p2(&self) -> bool {
+ self.flags.contains(Flags::WDIR_TRACKED | Flags::CLEAN_P2)
}
pub fn state(&self) -> EntryState {
- self.state
+ if self.removed() {
+ EntryState::Removed
+ } else if self.merged() {
+ EntryState::Merged
+ } else if self.added() {
+ EntryState::Added
+ } else {
+ EntryState::Normal
+ }
}
pub fn mode(&self) -> i32 {
@@ -81,11 +202,39 @@
}
pub fn size(&self) -> i32 {
- self.size
+ if self.merged_removed() {
+ SIZE_NON_NORMAL
+ } else if self.from_p2_removed() {
+ SIZE_FROM_OTHER_PARENT
+ } else if self.removed() {
+ 0
+ } else if self.merged() {
+ SIZE_FROM_OTHER_PARENT
+ } else if self.added() {
+ SIZE_NON_NORMAL
+ } else if self.from_p2() {
+ SIZE_FROM_OTHER_PARENT
+ } else if self.flags.contains(Flags::POSSIBLY_DIRTY) {
+ self.size // TODO: SIZE_NON_NORMAL ?
+ } else {
+ self.size
+ }
}
pub fn mtime(&self) -> i32 {
- self.mtime
+ if self.removed() {
+ 0
+ } else if self.flags.contains(Flags::POSSIBLY_DIRTY) {
+ MTIME_UNSET
+ } else if self.merged() {
+ MTIME_UNSET
+ } else if self.added() {
+ MTIME_UNSET
+ } else if self.from_p2() {
+ MTIME_UNSET
+ } else {
+ self.mtime
+ }
}
/// Returns `(state, mode, size, mtime)` for the puprose of serialization
@@ -95,15 +244,16 @@
/// want to not represent these cases that way in memory, but serialization
/// will need to keep the same format.
pub fn v1_data(&self) -> (u8, i32, i32, i32) {
- (self.state.into(), self.mode, self.size, self.mtime)
+ (self.state().into(), self.mode(), self.size(), self.mtime())
}
pub fn is_non_normal(&self) -> bool {
- self.state != EntryState::Normal || self.mtime == MTIME_UNSET
+ self.state() != EntryState::Normal || self.mtime() == MTIME_UNSET
}
pub fn is_from_other_parent(&self) -> bool {
- self.state == EntryState::Normal && self.size == SIZE_FROM_OTHER_PARENT
+ self.state() == EntryState::Normal
+ && self.size() == SIZE_FROM_OTHER_PARENT
}
// TODO: other platforms
@@ -114,7 +264,7 @@
) -> bool {
use std::os::unix::fs::MetadataExt;
const EXEC_BIT_MASK: u32 = 0o100;
- let dirstate_exec_bit = (self.mode as u32) & EXEC_BIT_MASK;
+ let dirstate_exec_bit = (self.mode() as u32) & EXEC_BIT_MASK;
let fs_exec_bit = filesystem_metadata.mode() & EXEC_BIT_MASK;
dirstate_exec_bit != fs_exec_bit
}
@@ -122,11 +272,16 @@
/// Returns a `(state, mode, size, mtime)` tuple as for
/// `DirstateMapMethods::debug_iter`.
pub fn debug_tuple(&self) -> (u8, i32, i32, i32) {
- (self.state.into(), self.mode, self.size, self.mtime)
+ let state = if self.flags.contains(Flags::ENTRYLESS_TREE_NODE) {
+ b' '
+ } else {
+ self.state().into()
+ };
+ (state, self.mode(), self.size(), self.mtime())
}
pub fn mtime_is_ambiguous(&self, now: i32) -> bool {
- self.state == EntryState::Normal && self.mtime == now
+ self.state() == EntryState::Normal && self.mtime() == now
}
pub fn clear_ambiguous_mtime(&mut self, now: i32) -> bool {