diff --git a/mercurial/helptext/internals/dirstate-v2.txt b/mercurial/helptext/internals/dirstate-v2.txt --- a/mercurial/helptext/internals/dirstate-v2.txt +++ b/mercurial/helptext/internals/dirstate-v2.txt @@ -386,6 +386,10 @@ EXPECTED_STATE_IS_MODIFIED = 1 << 8 ALL_UNKNOWN_RECORDED = 1 << 9 ALL_IGNORED_RECORDED = 1 << 10 + HAS_FALLBACK_EXEC = 1 << 11 + FALLBACK_EXEC = 1 << 12 + HAS_FALLBACK_SYMLINK = 1 << 13 + FALLBACK_SYMLINK = 1 << 14 The meaning of each bit is described below. @@ -558,3 +562,33 @@ Also note that having this flag unset does not imply that no "ignored" children have been recorded. Some might be present, but there is no garantee that is will be all of them. + +`HAS_FALLBACK_EXEC` + If this flag is set, the entry carries as "fallback" information for the + executable bit in the `FALLBACK_EXEC` flag. + + Fallback information can be stored in the dirstate to keep track of + filesystem attribute tracked by Mercurial when the underlying file + system or operating system does not support that property, (e.g. + Windows). + +`FALLBACK_EXEC` + Should be ignored if `HAS_FALLBACK_EXEC` is unset. If set the file for this + entry should be considered executable if that information cannot be + extracted from the file system. If unset it should be considered + non-executable instead. + +`HAS_FALLBACK_SYMLINK` + If this flag is set, the entry carries as "fallback" information for symbolic + link status in the `FALLBACK_SYMLINK` flag. + + Fallback information can be stored in the dirstate to keep track of + filesystem attribute tracked by Mercurial when the underlying file + system or operating system does not support that property, (e.g. + Windows). + +`FALLBACK_SYMLINK` + Should be ignored if `HAS_FALLBACK_SYMLINK` is unset. If set the file for + this entry should be considered a symlink if that information cannot be + extracted from the file system. If unset it should be considered a normal + file instead. diff --git a/mercurial/pure/parsers.py b/mercurial/pure/parsers.py --- a/mercurial/pure/parsers.py +++ b/mercurial/pure/parsers.py @@ -56,6 +56,10 @@ DIRSTATE_V2_EXPECTED_STATE_IS_MODIFIED = 1 << 8 DIRSTATE_V2_ALL_UNKNOWN_RECORDED = 1 << 9 DIRSTATE_V2_ALL_IGNORED_RECORDED = 1 << 10 +DIRSTATE_V2_HAS_FALLBACK_EXEC = 1 << 11 +DIRSTATE_V2_FALLBACK_EXEC = 1 << 12 +DIRSTATE_V2_HAS_FALLBACK_SYMLINK = 1 << 13 +DIRSTATE_V2_FALLBACK_SYMLINK = 1 << 14 @attr.s(slots=True, init=False) @@ -142,6 +146,14 @@ has_mode_size = False has_meaningful_mtime = False + fallback_exec = None + if flags & DIRSTATE_V2_HAS_FALLBACK_EXEC: + fallback_exec = flags & DIRSTATE_V2_FALLBACK_EXEC + + fallback_symlink = None + if flags & DIRSTATE_V2_HAS_FALLBACK_SYMLINK: + fallback_symlink = flags & DIRSTATE_V2_FALLBACK_SYMLINK + if has_mode_size: assert stat.S_IXUSR == 0o100 if flags & DIRSTATE_V2_MODE_EXEC_PERM: @@ -159,6 +171,8 @@ has_meaningful_data=has_mode_size, has_meaningful_mtime=has_meaningful_mtime, parentfiledata=(mode, size, mtime), + fallback_exec=fallback_exec, + fallback_symlink=fallback_symlink, ) @classmethod @@ -428,6 +442,17 @@ flags |= DIRSTATE_V2_MODE_IS_SYMLINK if self._mtime is not None: flags |= DIRSTATE_V2_HAS_FILE_MTIME + + if self._fallback_exec is not None: + flags |= DIRSTATE_V2_HAS_FALLBACK_EXEC + if self._fallback_exec: + flags |= DIRSTATE_V2_FALLBACK_EXEC + + if self._fallback_symlink is not None: + flags |= DIRSTATE_V2_HAS_FALLBACK_SYMLINK + if self._fallback_symlink: + flags |= DIRSTATE_V2_FALLBACK_SYMLINK + # Note: we do not need to do anything regarding # DIRSTATE_V2_ALL_UNKNOWN_RECORDED and DIRSTATE_V2_ALL_IGNORED_RECORDED # since we never set _DIRSTATE_V2_HAS_DIRCTORY_MTIME diff --git a/rust/hg-core/src/dirstate/entry.rs b/rust/hg-core/src/dirstate/entry.rs --- a/rust/hg-core/src/dirstate/entry.rs +++ b/rust/hg-core/src/dirstate/entry.rs @@ -339,7 +339,15 @@ /// Returns `(wdir_tracked, p1_tracked, p2_info, mode_size, mtime)` pub(crate) fn v2_data( &self, - ) -> (bool, bool, bool, Option<(u32, u32)>, Option) { + ) -> ( + bool, + bool, + bool, + Option<(u32, u32)>, + Option, + Option, + Option, + ) { if !self.any_tracked() { // TODO: return an Option instead? panic!("Accessing v1_state of an untracked DirstateEntry") @@ -349,7 +357,15 @@ 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) + ( + wdir_tracked, + p1_tracked, + p2_info, + mode_size, + mtime, + self.get_fallback_exec(), + self.get_fallback_symlink(), + ) } fn v1_state(&self) -> EntryState { diff --git a/rust/hg-core/src/dirstate_tree/on_disk.rs b/rust/hg-core/src/dirstate_tree/on_disk.rs --- a/rust/hg-core/src/dirstate_tree/on_disk.rs +++ b/rust/hg-core/src/dirstate_tree/on_disk.rs @@ -113,6 +113,10 @@ const EXPECTED_STATE_IS_MODIFIED = 1 << 8; const ALL_UNKNOWN_RECORDED = 1 << 9; const ALL_IGNORED_RECORDED = 1 << 10; + const HAS_FALLBACK_EXEC = 1 << 11; + const FALLBACK_EXEC = 1 << 12; + const HAS_FALLBACK_SYMLINK = 1 << 13; + const FALLBACK_SYMLINK = 1 << 14; } } @@ -420,8 +424,15 @@ fn from_dirstate_entry( entry: &DirstateEntry, ) -> (Flags, U32Be, PackedTruncatedTimestamp) { - let (wdir_tracked, p1_tracked, p2_info, mode_size_opt, mtime_opt) = - entry.v2_data(); + let ( + wdir_tracked, + p1_tracked, + p2_info, + mode_size_opt, + mtime_opt, + fallback_exec, + fallback_symlink, + ) = entry.v2_data(); // TODO: convert throug raw flag bits instead? let mut flags = Flags::empty(); flags.set(Flags::WDIR_TRACKED, wdir_tracked); @@ -446,6 +457,18 @@ } else { PackedTruncatedTimestamp::null() }; + if let Some(f_exec) = fallback_exec { + flags.insert(Flags::HAS_FALLBACK_EXEC); + if f_exec { + flags.insert(Flags::FALLBACK_EXEC); + } + } + if let Some(f_symlink) = fallback_symlink { + flags.insert(Flags::HAS_FALLBACK_SYMLINK); + if f_symlink { + flags.insert(Flags::FALLBACK_SYMLINK); + } + } (flags, size, mtime) } }