##// END OF EJS Templates
dirstate: narrow gathering of parent data...
dirstate: narrow gathering of parent data The parent data are only going to be useful is the file might be clean. And it might only be clean if it is tracked in both p1 and the working copy. Differential Revision: https://phab.mercurial-scm.org/D11584

File last commit:

r48951:ab5a7fdb default
r48953:42ab0bcb default
Show More
entry.rs
429 lines | 12.5 KiB | application/rls-services+xml | RustLexer
Simon Sapin
rust: Move DirstateEntry to its own module...
r48830 use crate::errors::HgError;
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 use bitflags::bitflags;
Simon Sapin
rust: Move DirstateEntry to its own module...
r48830 use std::convert::TryFrom;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum EntryState {
Normal,
Added,
Removed,
Merged,
}
/// The C implementation uses all signed types. This will be an issue
/// either when 4GB+ source files are commonplace or in 2038, whichever
/// comes first.
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct DirstateEntry {
Simon Sapin
dirstate-v2: Store a bitfield on disk instead of v1-like state...
r48951 pub(crate) flags: Flags,
dirstate-item: change the internal storage and constructor value...
r48950 mode_size: Option<(i32, i32)>,
mtime: Option<i32>,
Simon Sapin
rust: Move DirstateEntry to its own module...
r48830 }
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 bitflags! {
Simon Sapin
dirstate-v2: Store a bitfield on disk instead of v1-like state...
r48951 pub(crate) struct Flags: u8 {
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 const WDIR_TRACKED = 1 << 0;
const P1_TRACKED = 1 << 1;
dirstate-item: change the internal storage and constructor value...
r48950 const P2_INFO = 1 << 2;
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 }
}
Simon Sapin
rust: Move DirstateEntry to its own module...
r48830 pub const V1_RANGEMASK: i32 = 0x7FFFFFFF;
pub const MTIME_UNSET: i32 = -1;
/// A `DirstateEntry` with a size of `-2` means that it was merged from the
/// other parent. This allows revert to pick the right status back during a
/// merge.
pub const SIZE_FROM_OTHER_PARENT: i32 = -2;
/// A special value used for internal representation of special case in
/// dirstate v1 format.
pub const SIZE_NON_NORMAL: i32 = -1;
impl DirstateEntry {
Simon Sapin
dirstate-v2: Store a bitfield on disk instead of v1-like state...
r48951 pub fn from_v2_data(
dirstate-item: change the internal storage and constructor value...
r48950 wdir_tracked: bool,
p1_tracked: bool,
p2_info: bool,
mode_size: Option<(i32, i32)>,
mtime: Option<i32>,
Simon Sapin
rust: Add Python bindings for DirstateEntry as rustext.dirstate.DirstateItem...
r48857 ) -> Self {
dirstate-item: change the internal storage and constructor value...
r48950 let mut flags = Flags::empty();
flags.set(Flags::WDIR_TRACKED, wdir_tracked);
flags.set(Flags::P1_TRACKED, p1_tracked);
flags.set(Flags::P2_INFO, p2_info);
Simon Sapin
rust: Add Python bindings for DirstateEntry as rustext.dirstate.DirstateItem...
r48857 Self {
flags,
dirstate-item: change the internal storage and constructor value...
r48950 mode_size,
Simon Sapin
rust: Add Python bindings for DirstateEntry as rustext.dirstate.DirstateItem...
r48857 mtime,
}
}
Simon Sapin
rust: Make the fields of DirstateEntry private...
r48834 pub fn from_v1_data(
state: EntryState,
mode: i32,
size: i32,
mtime: i32,
) -> Self {
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 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 {
dirstate-item: change the internal storage and constructor value...
r48950 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
mode_size: Some((mode, size)),
mtime: None,
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 }
} else {
Simon Sapin
dirstate: Pass the final DirstateItem to _rustmap.addfile()...
r48865 Self::new_normal(mode, size, mtime)
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 }
}
EntryState::Added => Self::new_added(),
EntryState::Removed => Self {
flags: if size == SIZE_NON_NORMAL {
dirstate-item: change the internal storage and constructor value...
r48950 Flags::P1_TRACKED | Flags::P2_INFO
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 } else if size == SIZE_FROM_OTHER_PARENT {
// We don’t know if P1_TRACKED should be set (file history)
dirstate-item: change the internal storage and constructor value...
r48950 Flags::P2_INFO
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 } else {
Flags::P1_TRACKED
},
dirstate-item: change the internal storage and constructor value...
r48950 mode_size: None,
mtime: None,
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 },
EntryState::Merged => Self::new_merged(),
}
}
Simon Sapin
dirstate: Pass the final DirstateItem to _rustmap.addfile()...
r48865 pub fn new_from_p2() -> Self {
Simon Sapin
rust: Make the fields of DirstateEntry private...
r48834 Self {
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 // might be missing P1_TRACKED
dirstate-item: change the internal storage and constructor value...
r48950 flags: Flags::WDIR_TRACKED | Flags::P2_INFO,
mode_size: None,
mtime: None,
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 }
}
Simon Sapin
dirstate: Pass the final DirstateItem to _rustmap.addfile()...
r48865 pub fn new_possibly_dirty() -> Self {
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 Self {
dirstate-item: change the internal storage and constructor value...
r48950 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
mode_size: None,
mtime: None,
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 }
}
Simon Sapin
dirstate: Pass the final DirstateItem to _rustmap.addfile()...
r48865 pub fn new_added() -> Self {
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 Self {
flags: Flags::WDIR_TRACKED,
dirstate-item: change the internal storage and constructor value...
r48950 mode_size: None,
mtime: None,
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 }
}
Simon Sapin
dirstate: Pass the final DirstateItem to _rustmap.addfile()...
r48865 pub fn new_merged() -> Self {
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 Self {
flags: Flags::WDIR_TRACKED
| Flags::P1_TRACKED // might not be true because of rename ?
dirstate-item: change the internal storage and constructor value...
r48950 | Flags::P2_INFO, // might not be true because of rename ?
mode_size: None,
mtime: None,
Simon Sapin
rust: Make the fields of DirstateEntry private...
r48834 }
}
Simon Sapin
dirstate: Pass the final DirstateItem to _rustmap.addfile()...
r48865 pub fn new_normal(mode: i32, size: i32, mtime: i32) -> Self {
Self {
flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
dirstate-item: change the internal storage and constructor value...
r48950 mode_size: Some((mode, size)),
mtime: Some(mtime),
Simon Sapin
dirstate: Pass the final DirstateItem to _rustmap.addfile()...
r48865 }
}
Simon Sapin
rust: Make the fields of DirstateEntry private...
r48834 /// Creates a new entry in "removed" state.
///
/// `size` is expected to be zero, `SIZE_NON_NORMAL`, or
/// `SIZE_FROM_OTHER_PARENT`
pub fn new_removed(size: i32) -> Self {
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 Self::from_v1_data(EntryState::Removed, 0, size, 0)
Simon Sapin
rust: Make the fields of DirstateEntry private...
r48834 }
Simon Sapin
rust: Add Python bindings for DirstateEntry as rustext.dirstate.DirstateItem...
r48857 pub fn tracked(&self) -> bool {
self.flags.contains(Flags::WDIR_TRACKED)
}
dirstate-item: change the internal storage and constructor value...
r48950 fn in_either_parent(&self) -> bool {
self.flags.intersects(Flags::P1_TRACKED | Flags::P2_INFO)
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 }
Simon Sapin
rust: Add Python bindings for DirstateEntry as rustext.dirstate.DirstateItem...
r48857 pub fn removed(&self) -> bool {
dirstate-item: change the internal storage and constructor value...
r48950 self.in_either_parent() && !self.flags.contains(Flags::WDIR_TRACKED)
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 }
Simon Sapin
rust: Add Python bindings for DirstateEntry as rustext.dirstate.DirstateItem...
r48857 pub fn merged(&self) -> bool {
dirstate-item: change the internal storage and constructor value...
r48950 self.flags
.contains(Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO)
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 }
Simon Sapin
rust: Add Python bindings for DirstateEntry as rustext.dirstate.DirstateItem...
r48857 pub fn added(&self) -> bool {
dirstate-item: change the internal storage and constructor value...
r48950 self.flags.contains(Flags::WDIR_TRACKED) && !self.in_either_parent()
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 }
Simon Sapin
rust: Add Python bindings for DirstateEntry as rustext.dirstate.DirstateItem...
r48857 pub fn from_p2(&self) -> bool {
dirstate-item: change the internal storage and constructor value...
r48950 self.flags.contains(Flags::WDIR_TRACKED | Flags::P2_INFO)
&& !self.flags.contains(Flags::P1_TRACKED)
Simon Sapin
rust: Make the fields of DirstateEntry private...
r48834 }
dirstate-item: introduce a `maybe_clean` property...
r48898 pub fn maybe_clean(&self) -> bool {
if !self.flags.contains(Flags::WDIR_TRACKED) {
false
dirstate-item: change the internal storage and constructor value...
r48950 } else if !self.flags.contains(Flags::P1_TRACKED) {
dirstate-item: introduce a `maybe_clean` property...
r48898 false
dirstate-item: change the internal storage and constructor value...
r48950 } else if self.flags.contains(Flags::P2_INFO) {
dirstate-item: introduce a `maybe_clean` property...
r48898 false
} else {
true
}
}
dirstate-item: introduce a `any_tracked` property...
r48899 pub fn any_tracked(&self) -> bool {
self.flags.intersects(
dirstate-item: change the internal storage and constructor value...
r48950 Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO,
dirstate-item: introduce a `any_tracked` property...
r48899 )
}
Simon Sapin
dirstate-v2: Store a bitfield on disk instead of v1-like state...
r48951 /// Returns `(wdir_tracked, p1_tracked, p2_info, mode_size, mtime)`
pub(crate) fn v2_data(
&self,
) -> (bool, bool, bool, Option<(i32, i32)>, Option<i32>) {
if !self.any_tracked() {
// TODO: return an Option instead?
panic!("Accessing v1_state of an untracked DirstateEntry")
}
let wdir_tracked = self.flags.contains(Flags::WDIR_TRACKED);
let p1_tracked = self.flags.contains(Flags::P1_TRACKED);
let p2_info = self.flags.contains(Flags::P2_INFO);
let mode_size = self.mode_size;
let mtime = self.mtime;
(wdir_tracked, p1_tracked, p2_info, mode_size, mtime)
}
dirstate-item: change the internal storage and constructor value...
r48950 fn v1_state(&self) -> EntryState {
if !self.any_tracked() {
// TODO: return an Option instead?
panic!("Accessing v1_state of an untracked DirstateEntry")
}
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 if self.removed() {
EntryState::Removed
} else if self.merged() {
EntryState::Merged
} else if self.added() {
EntryState::Added
} else {
EntryState::Normal
}
Simon Sapin
rust: Make the fields of DirstateEntry private...
r48834 }
dirstate-item: change the internal storage and constructor value...
r48950 fn v1_mode(&self) -> i32 {
if let Some((mode, _size)) = self.mode_size {
mode
} else {
0
}
Simon Sapin
rust: Make the fields of DirstateEntry private...
r48834 }
dirstate-item: change the internal storage and constructor value...
r48950 fn v1_size(&self) -> i32 {
if !self.any_tracked() {
// TODO: return an Option instead?
panic!("Accessing v1_size of an untracked DirstateEntry")
}
if self.removed()
&& self.flags.contains(Flags::P1_TRACKED | Flags::P2_INFO)
{
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 SIZE_NON_NORMAL
dirstate-item: change the internal storage and constructor value...
r48950 } else if self.removed() && self.flags.contains(Flags::P2_INFO) {
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 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
dirstate-item: change the internal storage and constructor value...
r48950 } else if let Some((_mode, size)) = self.mode_size {
size
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 } else {
dirstate-item: change the internal storage and constructor value...
r48950 SIZE_NON_NORMAL
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 }
Simon Sapin
rust: Make the fields of DirstateEntry private...
r48834 }
dirstate-item: change the internal storage and constructor value...
r48950 fn v1_mtime(&self) -> i32 {
if !self.any_tracked() {
// TODO: return an Option instead?
panic!("Accessing v1_mtime of an untracked DirstateEntry")
}
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 if self.removed() {
0
dirstate-item: change the internal storage and constructor value...
r48950 } else if self.flags.contains(Flags::P2_INFO) {
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 MTIME_UNSET
dirstate-item: change the internal storage and constructor value...
r48950 } else if !self.flags.contains(Flags::P1_TRACKED) {
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 MTIME_UNSET
} else {
dirstate-item: change the internal storage and constructor value...
r48950 self.mtime.unwrap_or(MTIME_UNSET)
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 }
Simon Sapin
rust: Make the fields of DirstateEntry private...
r48834 }
dirstate-item: change the internal storage and constructor value...
r48950 // TODO: return `Option<EntryState>`? None when `!self.any_tracked`
pub fn state(&self) -> EntryState {
self.v1_state()
}
// TODO: return Option?
pub fn mode(&self) -> i32 {
self.v1_mode()
}
// TODO: return Option?
pub fn size(&self) -> i32 {
self.v1_size()
}
// TODO: return Option?
pub fn mtime(&self) -> i32 {
self.v1_mtime()
}
dirstate-item: implement `drop_merge_data` on the Rust DirstateItem...
r48946 pub fn drop_merge_data(&mut self) {
dirstate-item: change the internal storage and constructor value...
r48950 if self.flags.contains(Flags::P2_INFO) {
self.flags.remove(Flags::P2_INFO);
self.mode_size = None;
self.mtime = None;
dirstate-item: implement `drop_merge_data` on the Rust DirstateItem...
r48946 }
}
Simon Sapin
rust: Add Python bindings for DirstateEntry as rustext.dirstate.DirstateItem...
r48857 pub fn set_possibly_dirty(&mut self) {
dirstate-item: change the internal storage and constructor value...
r48950 self.mtime = None
Simon Sapin
rust: Add Python bindings for DirstateEntry as rustext.dirstate.DirstateItem...
r48857 }
pub fn set_clean(&mut self, mode: i32, size: i32, mtime: i32) {
self.flags.insert(Flags::WDIR_TRACKED | Flags::P1_TRACKED);
dirstate-item: change the internal storage and constructor value...
r48950 self.mode_size = Some((mode, size));
self.mtime = Some(mtime);
Simon Sapin
rust: Add Python bindings for DirstateEntry as rustext.dirstate.DirstateItem...
r48857 }
pub fn set_tracked(&mut self) {
dirstate-item: change the internal storage and constructor value...
r48950 self.flags.insert(Flags::WDIR_TRACKED);
// `set_tracked` is replacing various `normallookup` call. So we mark
// the files as needing lookup
//
// Consider dropping this in the future in favor of something less
// broad.
self.mtime = None;
Simon Sapin
rust: Add Python bindings for DirstateEntry as rustext.dirstate.DirstateItem...
r48857 }
pub fn set_untracked(&mut self) {
self.flags.remove(Flags::WDIR_TRACKED);
dirstate-item: change the internal storage and constructor value...
r48950 self.mode_size = None;
self.mtime = None;
Simon Sapin
rust: Add Python bindings for DirstateEntry as rustext.dirstate.DirstateItem...
r48857 }
Simon Sapin
rust: Make the fields of DirstateEntry private...
r48834 /// Returns `(state, mode, size, mtime)` for the puprose of serialization
/// in the dirstate-v1 format.
///
/// This includes marker values such as `mtime == -1`. In the future we may
/// 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) {
dirstate-item: change the internal storage and constructor value...
r48950 (
self.v1_state().into(),
self.v1_mode(),
self.v1_size(),
self.v1_mtime(),
)
Simon Sapin
rust: Make the fields of DirstateEntry private...
r48834 }
dirstate: drop all logic around the "non-normal" sets...
r48875 pub(crate) fn is_from_other_parent(&self) -> bool {
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 self.state() == EntryState::Normal
&& self.size() == SIZE_FROM_OTHER_PARENT
Simon Sapin
rust: Move DirstateEntry to its own module...
r48830 }
// TODO: other platforms
#[cfg(unix)]
pub fn mode_changed(
&self,
filesystem_metadata: &std::fs::Metadata,
) -> bool {
use std::os::unix::fs::MetadataExt;
const EXEC_BIT_MASK: u32 = 0o100;
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 let dirstate_exec_bit = (self.mode() as u32) & EXEC_BIT_MASK;
Simon Sapin
rust: Move DirstateEntry to its own module...
r48830 let fs_exec_bit = filesystem_metadata.mode() & EXEC_BIT_MASK;
dirstate_exec_bit != fs_exec_bit
}
/// Returns a `(state, mode, size, mtime)` tuple as for
/// `DirstateMapMethods::debug_iter`.
pub fn debug_tuple(&self) -> (u8, i32, i32, i32) {
dirstate-item: change the internal storage and constructor value...
r48950 (self.state().into(), self.mode(), self.size(), self.mtime())
Simon Sapin
rust: Move DirstateEntry to its own module...
r48830 }
pub fn mtime_is_ambiguous(&self, now: i32) -> bool {
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 self.state() == EntryState::Normal && self.mtime() == now
Simon Sapin
rust: Move DirstateEntry to its own module...
r48830 }
pub fn clear_ambiguous_mtime(&mut self, now: i32) -> bool {
let ambiguous = self.mtime_is_ambiguous(now);
if ambiguous {
// The file was last modified "simultaneously" with the current
// write to dirstate (i.e. within the same second for file-
// systems with a granularity of 1 sec). This commonly happens
// for at least a couple of files on 'update'.
// The user could change the file without changing its size
// within the same second. Invalidate the file's mtime in
// dirstate, forcing future 'status' calls to compare the
// contents of the file if the size is the same. This prevents
// mistakenly treating such files as clean.
dirstate-item: change the internal storage and constructor value...
r48950 self.set_possibly_dirty()
Simon Sapin
rust: Move DirstateEntry to its own module...
r48830 }
ambiguous
}
}
impl EntryState {
pub fn is_tracked(self) -> bool {
use EntryState::*;
match self {
Normal | Added | Merged => true,
Simon Sapin
rust: Remove EntryState::Unknown...
r48838 Removed => false,
Simon Sapin
rust: Move DirstateEntry to its own module...
r48830 }
}
}
impl TryFrom<u8> for EntryState {
type Error = HgError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
b'n' => Ok(EntryState::Normal),
b'a' => Ok(EntryState::Added),
b'r' => Ok(EntryState::Removed),
b'm' => Ok(EntryState::Merged),
_ => Err(HgError::CorruptedRepository(format!(
"Incorrect dirstate entry state {}",
value
))),
}
}
}
impl Into<u8> for EntryState {
fn into(self) -> u8 {
match self {
EntryState::Normal => b'n',
EntryState::Added => b'a',
EntryState::Removed => b'r',
EntryState::Merged => b'm',
}
}
}