##// END OF EJS Templates
rust: make `Revision` a newtype...
rust: make `Revision` a newtype This change is the one we've been building towards during this series. The aim is to make `Revision` mean more than a simple integer, holding the information that it is valid for a given revlog index. While this still allows for programmer error, since creating a revision directly and querying a different index with a "checked" revision are still possible, the friction created by the newtype will hopefully make us think twice about which type to use. Enough of the Rust ecosystem relies on the newtype pattern to be efficiently optimized away (even compiler in codegen tests¹), so I'm not worried about this being a fundamental problem. [1] https://github.com/rust-lang/rust/blob/7a70647f195f6b0a0f1ebd72b1542ba91a32f43a/tests/codegen/vec-in-place.rs#L47

File last commit:

r50825:e98fd81b default
r51872:4c5f6e95 default
Show More
entry.rs
722 lines | 23.1 KiB | application/rls-services+xml | RustLexer
Simon Sapin
dirstate-v2: Truncate directory mtimes to 31 bits of seconds...
r49007 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
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
status: Extract TruncatedTimestamp from fs::Metadata without SystemTime...
r49032 use std::fs;
use std::io;
Simon Sapin
dirstate-v2: Separate Rust structs for Timestamp and PackedTimestamp...
r49006 use std::time::{SystemTime, UNIX_EPOCH};
Simon Sapin
rust: Move DirstateEntry to its own module...
r48830
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum EntryState {
Normal,
Added,
Removed,
Merged,
}
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 /// `size` and `mtime.seconds` are truncated to 31 bits.
///
/// TODO: double-check status algorithm correctness for files
/// larger than 2 GiB or modified after 2038.
#[derive(Debug, Copy, Clone)]
Simon Sapin
rust: Move DirstateEntry to its own module...
r48830 pub struct DirstateEntry {
Simon Sapin
dirstate-v2: Store a bitfield on disk instead of v1-like state...
r48951 pub(crate) flags: Flags,
Simon Sapin
dirstate-v2: Store unsigned integers inside DirstateEntry...
r49008 mode_size: Option<(u32, u32)>,
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 mtime: Option<TruncatedTimestamp>,
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;
dirstate: add a concept of "fallback" flags to dirstate item...
r49068 const HAS_FALLBACK_EXEC = 1 << 3;
const FALLBACK_EXEC = 1 << 4;
const HAS_FALLBACK_SYMLINK = 1 << 5;
const FALLBACK_SYMLINK = 1 << 6;
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 }
}
Simon Sapin
dirstate-v2: Truncate directory mtimes to 31 bits of seconds...
r49007 /// A Unix timestamp with nanoseconds precision
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 #[derive(Debug, Copy, Clone)]
Simon Sapin
dirstate-v2: Truncate directory mtimes to 31 bits of seconds...
r49007 pub struct TruncatedTimestamp {
truncated_seconds: u32,
/// Always in the `0 .. 1_000_000_000` range.
Simon Sapin
dirstate-v2: Separate Rust structs for Timestamp and PackedTimestamp...
r49006 nanoseconds: u32,
dirstate-item: make sure we set the mtime-second-ambiguous on v2 write...
r49230 /// TODO this should be in DirstateEntry, but the current code needs
/// refactoring to use DirstateEntry instead of TruncatedTimestamp for
/// comparison.
pub second_ambiguous: bool,
Simon Sapin
dirstate-v2: Separate Rust structs for Timestamp and PackedTimestamp...
r49006 }
Simon Sapin
dirstate-v2: Truncate directory mtimes to 31 bits of seconds...
r49007 impl TruncatedTimestamp {
/// Constructs from a timestamp potentially outside of the supported range,
/// and truncate the seconds components to its lower 31 bits.
///
/// Panics if the nanoseconds components is not in the expected range.
dirstate-item: add a "second_ambiguous` flag in the mtime tuple...
r49227 pub fn new_truncate(
seconds: i64,
nanoseconds: u32,
second_ambiguous: bool,
) -> Self {
Simon Sapin
dirstate-v2: Truncate directory mtimes to 31 bits of seconds...
r49007 assert!(nanoseconds < NSEC_PER_SEC);
Simon Sapin
dirstate-v2: Separate Rust structs for Timestamp and PackedTimestamp...
r49006 Self {
Simon Sapin
dirstate-v2: Truncate directory mtimes to 31 bits of seconds...
r49007 truncated_seconds: seconds as u32 & RANGE_MASK_31BIT,
Simon Sapin
dirstate-v2: Separate Rust structs for Timestamp and PackedTimestamp...
r49006 nanoseconds,
dirstate-item: add a "second_ambiguous` flag in the mtime tuple...
r49227 second_ambiguous,
Simon Sapin
dirstate-v2: Separate Rust structs for Timestamp and PackedTimestamp...
r49006 }
}
Simon Sapin
dirstate-v2: Truncate directory mtimes to 31 bits of seconds...
r49007 /// Construct from components. Returns an error if they are not in the
/// expcted range.
pub fn from_already_truncated(
truncated_seconds: u32,
nanoseconds: u32,
dirstate-item: add a "second_ambiguous` flag in the mtime tuple...
r49227 second_ambiguous: bool,
Simon Sapin
dirstate-v2: Truncate directory mtimes to 31 bits of seconds...
r49007 ) -> Result<Self, DirstateV2ParseError> {
if truncated_seconds & !RANGE_MASK_31BIT == 0
&& nanoseconds < NSEC_PER_SEC
{
Ok(Self {
truncated_seconds,
nanoseconds,
dirstate-item: add a "second_ambiguous` flag in the mtime tuple...
r49227 second_ambiguous,
Simon Sapin
dirstate-v2: Truncate directory mtimes to 31 bits of seconds...
r49007 })
} else {
Raphaël Gomès
rust: add message to `DirstateV2ParseError` to give some context...
r50268 Err(DirstateV2ParseError::new("when reading datetime"))
Simon Sapin
dirstate-v2: Truncate directory mtimes to 31 bits of seconds...
r49007 }
Simon Sapin
dirstate-v2: Separate Rust structs for Timestamp and PackedTimestamp...
r49006 }
Simon Sapin
rhg: Set second_ambiguous as needed in post-status fixup...
r49272 /// Returns a `TruncatedTimestamp` for the modification time of `metadata`.
///
/// Propagates errors from `std` on platforms where modification time
/// is not available at all.
Simon Sapin
status: Extract TruncatedTimestamp from fs::Metadata without SystemTime...
r49032 pub fn for_mtime_of(metadata: &fs::Metadata) -> io::Result<Self> {
#[cfg(unix)]
{
use std::os::unix::fs::MetadataExt;
let seconds = metadata.mtime();
// i64 -> u32 with value always in the `0 .. NSEC_PER_SEC` range
let nanoseconds = metadata.mtime_nsec().try_into().unwrap();
dirstate-item: add a "second_ambiguous` flag in the mtime tuple...
r49227 Ok(Self::new_truncate(seconds, nanoseconds, false))
Simon Sapin
status: Extract TruncatedTimestamp from fs::Metadata without SystemTime...
r49032 }
#[cfg(not(unix))]
{
metadata.modified().map(Self::from)
}
}
Simon Sapin
rhg: Set second_ambiguous as needed in post-status fixup...
r49272 /// Like `for_mtime_of`, but may return `None` or a value with
/// `second_ambiguous` set if the mtime is not "reliable".
Simon Sapin
rhg: Update the dirstate on disk after status...
r49250 ///
/// A modification time is reliable if it is older than `boundary` (or
/// sufficiently in the future).
///
/// Otherwise a concurrent modification might happens with the same mtime.
Simon Sapin
rhg: Set second_ambiguous as needed in post-status fixup...
r49272 pub fn for_reliable_mtime_of(
metadata: &fs::Metadata,
boundary: &Self,
) -> io::Result<Option<Self>> {
let mut mtime = Self::for_mtime_of(metadata)?;
Simon Sapin
rhg: Update the dirstate on disk after status...
r49250 // If the mtime of the ambiguous file is younger (or equal) to the
// starting point of the `status` walk, we cannot garantee that
// another, racy, write will not happen right after with the same mtime
// and we cannot cache the information.
//
// However if the mtime is far away in the future, this is likely some
// mismatch between the current clock and previous file system
// operation. So mtime more than one days in the future are considered
// fine.
Simon Sapin
rhg: Set second_ambiguous as needed in post-status fixup...
r49272 let reliable = if mtime.truncated_seconds == boundary.truncated_seconds
{
mtime.second_ambiguous = true;
mtime.nanoseconds != 0
Simon Sapin
rhg: Update the dirstate on disk after status...
r49250 && boundary.nanoseconds != 0
Simon Sapin
rhg: Set second_ambiguous as needed in post-status fixup...
r49272 && mtime.nanoseconds < boundary.nanoseconds
Simon Sapin
rhg: Update the dirstate on disk after status...
r49250 } else {
// `truncated_seconds` is less than 2**31,
// so this does not overflow `u32`:
let one_day_later = boundary.truncated_seconds + 24 * 3600;
Simon Sapin
rhg: Set second_ambiguous as needed in post-status fixup...
r49272 mtime.truncated_seconds < boundary.truncated_seconds
|| mtime.truncated_seconds > one_day_later
};
if reliable {
Ok(Some(mtime))
} else {
Ok(None)
Simon Sapin
rhg: Update the dirstate on disk after status...
r49250 }
}
Simon Sapin
dirstate-v2: Truncate directory mtimes to 31 bits of seconds...
r49007 /// The lower 31 bits of the number of seconds since the epoch.
pub fn truncated_seconds(&self) -> u32 {
self.truncated_seconds
}
/// The sub-second component of this timestamp, in nanoseconds.
/// Always in the `0 .. 1_000_000_000` range.
///
/// This timestamp is after `(seconds, 0)` by this many nanoseconds.
Simon Sapin
dirstate-v2: Separate Rust structs for Timestamp and PackedTimestamp...
r49006 pub fn nanoseconds(&self) -> u32 {
self.nanoseconds
}
Simon Sapin
dirstate-v2: Truncate directory mtimes to 31 bits of seconds...
r49007
/// Returns whether two timestamps are equal modulo 2**31 seconds.
///
/// If this returns `true`, the original values converted from `SystemTime`
/// or given to `new_truncate` were very likely equal. A false positive is
/// possible if they were exactly a multiple of 2**31 seconds apart (around
/// 68 years). This is deemed very unlikely to happen by chance, especially
/// on filesystems that support sub-second precision.
///
/// If someone is manipulating the modification times of some files to
/// intentionally make `hg status` return incorrect results, not truncating
/// wouldn’t help much since they can set exactly the expected timestamp.
Simon Sapin
dirstate: ignore sub-second component when either is zero in mtime...
r49081 ///
/// Sub-second precision is ignored if it is zero in either value.
/// Some APIs simply return zero when more precision is not available.
/// When comparing values from different sources, if only one is truncated
/// in that way, doing a simple comparison would cause many false
/// negatives.
Simon Sapin
dirstate: rename a `very_likely_equal` method to `likely_equal`...
r49076 pub fn likely_equal(self, other: Self) -> bool {
dirstate-item: implement the comparison logic for mtime-second-ambiguous...
r49228 if self.truncated_seconds != other.truncated_seconds {
false
} else if self.nanoseconds == 0 || other.nanoseconds == 0 {
Raphaël Gomès
rust-clippy: fix most warnings in `hg-core`...
r50825 !self.second_ambiguous
dirstate-item: implement the comparison logic for mtime-second-ambiguous...
r49228 } else {
self.nanoseconds == other.nanoseconds
}
Simon Sapin
dirstate-v2: Truncate directory mtimes to 31 bits of seconds...
r49007 }
Simon Sapin
status: Extract TruncatedTimestamp from fs::Metadata without SystemTime...
r49032
Simon Sapin
dirstate: rename a `very_likely_equal` method to `likely_equal`...
r49076 pub fn likely_equal_to_mtime_of(
Simon Sapin
status: Extract TruncatedTimestamp from fs::Metadata without SystemTime...
r49032 self,
metadata: &fs::Metadata,
) -> io::Result<bool> {
Simon Sapin
dirstate: rename a `very_likely_equal` method to `likely_equal`...
r49076 Ok(self.likely_equal(Self::for_mtime_of(metadata)?))
Simon Sapin
status: Extract TruncatedTimestamp from fs::Metadata without SystemTime...
r49032 }
Simon Sapin
dirstate-v2: Separate Rust structs for Timestamp and PackedTimestamp...
r49006 }
Simon Sapin
dirstate-v2: Truncate directory mtimes to 31 bits of seconds...
r49007 impl From<SystemTime> for TruncatedTimestamp {
Simon Sapin
dirstate-v2: Separate Rust structs for Timestamp and PackedTimestamp...
r49006 fn from(system_time: SystemTime) -> Self {
// On Unix, `SystemTime` is a wrapper for the `timespec` C struct:
// https://www.gnu.org/software/libc/manual/html_node/Time-Types.html#index-struct-timespec
// We want to effectively access its fields, but the Rust standard
// library does not expose them. The best we can do is:
let seconds;
let nanoseconds;
match system_time.duration_since(UNIX_EPOCH) {
Ok(duration) => {
seconds = duration.as_secs() as i64;
nanoseconds = duration.subsec_nanos();
}
Err(error) => {
// `system_time` is before `UNIX_EPOCH`.
// We need to undo this algorithm:
// https://github.com/rust-lang/rust/blob/6bed1f0bc3cc50c10aab26d5f94b16a00776b8a5/library/std/src/sys/unix/time.rs#L40-L41
let negative = error.duration();
let negative_secs = negative.as_secs() as i64;
let negative_nanos = negative.subsec_nanos();
if negative_nanos == 0 {
seconds = -negative_secs;
nanoseconds = 0;
} else {
// For example if `system_time` was 4.3 seconds before
// the Unix epoch we get a Duration that represents
// `(-4, -0.3)` but we want `(-5, +0.7)`:
seconds = -1 - negative_secs;
nanoseconds = NSEC_PER_SEC - negative_nanos;
}
}
};
dirstate-item: add a "second_ambiguous` flag in the mtime tuple...
r49227 Self::new_truncate(seconds, nanoseconds, false)
Simon Sapin
dirstate-v2: Separate Rust structs for Timestamp and PackedTimestamp...
r49006 }
}
Simon Sapin
dirstate-v2: Truncate directory mtimes to 31 bits of seconds...
r49007 const NSEC_PER_SEC: u32 = 1_000_000_000;
Simon Sapin
rhg: Update the dirstate on disk after status...
r49250 pub const RANGE_MASK_31BIT: u32 = 0x7FFF_FFFF;
Simon Sapin
rust: Move DirstateEntry to its own module...
r48830
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;
Raphaël Gomès
rust-dirstate: introduce intermediate struct for dirstate-v2 data...
r49991 #[derive(Debug, Default, Copy, Clone)]
pub struct DirstateV2Data {
pub wc_tracked: bool,
pub p1_tracked: bool,
pub p2_info: bool,
pub mode_size: Option<(u32, u32)>,
pub mtime: Option<TruncatedTimestamp>,
pub fallback_exec: Option<bool>,
pub fallback_symlink: Option<bool>,
}
Raphaël Gomès
rust-dirstatemap: add Rust implementation of `reset_state`...
r49992 #[derive(Debug, Default, Copy, Clone)]
pub struct ParentFileData {
pub mode_size: Option<(u32, u32)>,
pub mtime: Option<TruncatedTimestamp>,
}
Simon Sapin
rust: Move DirstateEntry to its own module...
r48830 impl DirstateEntry {
Raphaël Gomès
rust-dirstate: introduce intermediate struct for dirstate-v2 data...
r49991 pub fn from_v2_data(v2_data: DirstateV2Data) -> Self {
let DirstateV2Data {
wc_tracked,
p1_tracked,
p2_info,
mode_size,
mtime,
fallback_exec,
fallback_symlink,
} = v2_data;
Simon Sapin
dirstate-v2: Store unsigned integers inside DirstateEntry...
r49008 if let Some((mode, size)) = mode_size {
// TODO: return an error for out of range values?
assert!(mode & !RANGE_MASK_31BIT == 0);
assert!(size & !RANGE_MASK_31BIT == 0);
}
dirstate-item: change the internal storage and constructor value...
r48950 let mut flags = Flags::empty();
Raphaël Gomès
rust-dirstate: introduce intermediate struct for dirstate-v2 data...
r49991 flags.set(Flags::WDIR_TRACKED, wc_tracked);
dirstate-item: change the internal storage and constructor value...
r48950 flags.set(Flags::P1_TRACKED, p1_tracked);
flags.set(Flags::P2_INFO, p2_info);
dirstate: make DirstateItem constructor accept fallback value...
r49069 if let Some(exec) = fallback_exec {
flags.insert(Flags::HAS_FALLBACK_EXEC);
if exec {
flags.insert(Flags::FALLBACK_EXEC);
}
}
if let Some(exec) = fallback_symlink {
flags.insert(Flags::HAS_FALLBACK_SYMLINK);
if exec {
flags.insert(Flags::FALLBACK_SYMLINK);
}
}
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 {
dirstate-item: replace call to new_from_p2...
r48970 Self {
// might be missing P1_TRACKED
flags: Flags::WDIR_TRACKED | Flags::P2_INFO,
mode_size: None,
mtime: None,
}
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 } else if size == SIZE_NON_NORMAL {
dirstate-item: replace call to new_possibly_dirty...
r48972 Self {
flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
mode_size: None,
mtime: None,
}
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 } else if mtime == MTIME_UNSET {
Simon Sapin
dirstate-v2: Store unsigned integers inside DirstateEntry...
r49008 // TODO: return an error for negative values?
let mode = u32::try_from(mode).unwrap();
let size = u32::try_from(size).unwrap();
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: Some((mode, size)),
mtime: None,
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 }
} else {
Simon Sapin
dirstate-v2: Store unsigned integers inside DirstateEntry...
r49008 // TODO: return an error for negative values?
let mode = u32::try_from(mode).unwrap();
let size = u32::try_from(size).unwrap();
let mtime = u32::try_from(mtime).unwrap();
dirstate-item: add a "second_ambiguous` flag in the mtime tuple...
r49227 let mtime = TruncatedTimestamp::from_already_truncated(
mtime, 0, false,
)
.unwrap();
dirstate-item: replace call to new_normal...
r48975 Self {
flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
mode_size: Some((mode, size)),
mtime: Some(mtime),
}
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 }
}
dirstate-item: replace call to new_added...
r48968 EntryState::Added => Self {
flags: Flags::WDIR_TRACKED,
mode_size: None,
mtime: None,
},
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 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 },
dirstate-item: replace call to new_merged...
r48966 EntryState::Merged => Self {
flags: Flags::WDIR_TRACKED
| Flags::P1_TRACKED // might not be true because of rename ?
| Flags::P2_INFO, // might not be true because of rename ?
mode_size: None,
mtime: None,
},
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 }
}
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 }
Raphaël Gomès
rust-dirstatemap: add `set_tracked` method...
r49988 pub fn new_tracked() -> Self {
Raphaël Gomès
rust-dirstate: introduce intermediate struct for dirstate-v2 data...
r49991 let data = DirstateV2Data {
wc_tracked: true,
..Default::default()
};
Self::from_v2_data(data)
Raphaël Gomès
rust-dirstatemap: add `set_tracked` method...
r49988 }
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: introduce a `p1_tracked` property...
r48955 pub fn p1_tracked(&self) -> bool {
self.flags.contains(Flags::P1_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 }
dirstate-item: introduce a `p2_info` property that combine two others...
r48954 pub fn p2_info(&self) -> bool {
self.flags.contains(Flags::WDIR_TRACKED | Flags::P2_INFO)
}
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 }
Raphaël Gomès
rust-dirstate-entry: add `modified` method...
r50029 pub fn modified(&self) -> bool {
self.flags
.contains(Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO)
}
dirstate-item: introduce a `maybe_clean` property...
r48898 pub fn maybe_clean(&self) -> bool {
Raphaël Gomès
rust-clippy: tell clippy we care about keeping those `if` clauses separate...
r50813 #[allow(clippy::if_same_then_else)]
Raphaël Gomès
rust-clippy: tell clippy we want to keep those clauses separate...
r50814 #[allow(clippy::needless_bool)]
dirstate-item: introduce a `maybe_clean` property...
r48898 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 )
}
Raphaël Gomès
rust-dirstate: introduce intermediate struct for dirstate-v2 data...
r49991 pub(crate) fn v2_data(&self) -> DirstateV2Data {
Simon Sapin
dirstate-v2: Store a bitfield on disk instead of v1-like state...
r48951 if !self.any_tracked() {
// TODO: return an Option instead?
Raphaël Gomès
rust-dirstate-entry: fix typo in panic message...
r49919 panic!("Accessing v2_data of an untracked DirstateEntry")
Simon Sapin
dirstate-v2: Store a bitfield on disk instead of v1-like state...
r48951 }
Raphaël Gomès
rust-dirstate: introduce intermediate struct for dirstate-v2 data...
r49991 let wc_tracked = self.flags.contains(Flags::WDIR_TRACKED);
Simon Sapin
dirstate-v2: Store a bitfield on disk instead of v1-like state...
r48951 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;
Raphaël Gomès
rust-dirstate: introduce intermediate struct for dirstate-v2 data...
r49991 DirstateV2Data {
wc_tracked,
dirstate-v2: preserve the fallback values on disk...
r49070 p1_tracked,
p2_info,
mode_size,
mtime,
Raphaël Gomès
rust-dirstate: introduce intermediate struct for dirstate-v2 data...
r49991 fallback_exec: self.get_fallback_exec(),
fallback_symlink: self.get_fallback_symlink(),
}
Simon Sapin
dirstate-v2: Store a bitfield on disk instead of v1-like state...
r48951 }
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
Raphaël Gomès
rust-dirstate-entry: add `modified` method...
r50029 } else if self.modified() {
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 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 {
Simon Sapin
dirstate-v2: Store unsigned integers inside DirstateEntry...
r49008 i32::try_from(mode).unwrap()
dirstate-item: change the internal storage and constructor value...
r48950 } 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: directly use `p2_info` in `v1_size`...
r48961 } else if 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.added() {
SIZE_NON_NORMAL
dirstate-item: change the internal storage and constructor value...
r48950 } else if let Some((_mode, size)) = self.mode_size {
Simon Sapin
dirstate-v2: Store unsigned integers inside DirstateEntry...
r49008 i32::try_from(size).unwrap()
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")
}
Raphaël Gomès
rust-clippy: tell clippy we care about keeping those `if` clauses separate...
r50813
#[allow(clippy::if_same_then_else)]
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
Simon Sapin
dirstate-v2: Store unsigned integers inside DirstateEntry...
r49008 } else if let Some(mtime) = self.mtime {
dirstate-item: ignore mtime to write v1 when `mtime-second-ambiguous` is set...
r49229 if mtime.second_ambiguous {
MTIME_UNSET
} else {
i32::try_from(mtime.truncated_seconds()).unwrap()
}
Simon Sapin
rust: Align DirstateEntry internals with Python/C DirstateItem...
r48856 } else {
Simon Sapin
dirstate-v2: Store unsigned integers inside DirstateEntry...
r49008 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: add a concept of "fallback" flags to dirstate item...
r49068 pub fn get_fallback_exec(&self) -> Option<bool> {
if self.flags.contains(Flags::HAS_FALLBACK_EXEC) {
Some(self.flags.contains(Flags::FALLBACK_EXEC))
} else {
None
}
}
pub fn set_fallback_exec(&mut self, value: Option<bool>) {
match value {
None => {
self.flags.remove(Flags::HAS_FALLBACK_EXEC);
self.flags.remove(Flags::FALLBACK_EXEC);
}
Some(exec) => {
self.flags.insert(Flags::HAS_FALLBACK_EXEC);
if exec {
self.flags.insert(Flags::FALLBACK_EXEC);
}
}
}
}
pub fn get_fallback_symlink(&self) -> Option<bool> {
if self.flags.contains(Flags::HAS_FALLBACK_SYMLINK) {
Some(self.flags.contains(Flags::FALLBACK_SYMLINK))
} else {
None
}
}
pub fn set_fallback_symlink(&mut self, value: Option<bool>) {
match value {
None => {
self.flags.remove(Flags::HAS_FALLBACK_SYMLINK);
self.flags.remove(Flags::FALLBACK_SYMLINK);
}
Some(symlink) => {
self.flags.insert(Flags::HAS_FALLBACK_SYMLINK);
if symlink {
self.flags.insert(Flags::FALLBACK_SYMLINK);
}
}
}
}
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 pub fn truncated_mtime(&self) -> Option<TruncatedTimestamp> {
self.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 }
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 pub fn set_clean(
&mut self,
mode: u32,
size: u32,
mtime: TruncatedTimestamp,
) {
Simon Sapin
dirstate-v2: Store unsigned integers inside DirstateEntry...
r49008 let size = size & RANGE_MASK_31BIT;
Simon Sapin
rust: Add Python bindings for DirstateEntry as rustext.dirstate.DirstateItem...
r48857 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 {
Raphaël Gomès
rust-dirstatemap: stop using `.state` in `is_from_other_parent`...
r50030 self.flags.contains(Flags::WDIR_TRACKED | Flags::P2_INFO)
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 {
Simon Sapin
rhg: Fix status desambiguation of symlinks and executable files...
r49168 let dirstate_exec_bit = (self.mode() as u32 & EXEC_BIT_MASK) != 0;
let fs_exec_bit = has_exec_bit(filesystem_metadata);
Simon Sapin
rust: Move DirstateEntry to its own module...
r48830 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 }
}
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
))),
}
}
}
Raphaël Gomès
rust-clippy: fix most warnings in `hg-core`...
r50825 impl From<EntryState> for u8 {
fn from(val: EntryState) -> Self {
match val {
Simon Sapin
rust: Move DirstateEntry to its own module...
r48830 EntryState::Normal => b'n',
EntryState::Added => b'a',
EntryState::Removed => b'r',
EntryState::Merged => b'm',
}
}
}
Simon Sapin
rhg: Fix status desambiguation of symlinks and executable files...
r49168
const EXEC_BIT_MASK: u32 = 0o100;
pub fn has_exec_bit(metadata: &std::fs::Metadata) -> bool {
// TODO: How to handle executable permissions on Windows?
use std::os::unix::fs::MetadataExt;
(metadata.mode() & EXEC_BIT_MASK) != 0
}