1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
use bitflags::bitflags;
use crate::entry::Stage;
bitflags! {
/// In-memory flags.
///
/// Notably, not all of these will be persisted but can be used to aid all kinds of operations.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Flags: u32 {
// TODO: could we use the pathlen ourselves to save 8 bytes? And how to handle longer paths than that? 0 as sentinel maybe?
/// The mask to obtain the length of the path associated with this entry, up to 4095 characters without extension.
const PATH_LEN = 0x0fff;
/// The mask to apply to obtain the stage number of an entry, encoding three value: 0 = base, 1 = ours, 2 = theirs.
const STAGE_MASK = 1<<12 | 1<<13;
/// If set, additional bits need to be written to storage.
const EXTENDED = 1<<14;
/// If set, the entry be assumed to match with the version on the working tree, as a way to avoid `lstat()` checks.
const ASSUME_VALID = 1 << 15;
/// Indicates that an entry needs to be updated as it's in-memory representation doesn't match what's on disk.
const UPDATE = 1 << 16;
/// Indicates an entry should be removed - this typically happens during writing, by simply skipping over them.
const REMOVE = 1 << 17;
/// Indicates that an entry is known to be up-to-date.
const UPTODATE = 1 << 18;
/// Only temporarily used by unpack_trees() (in C).
const ADDED = 1 << 19;
/// Whether an up-to-date object hash exists for the entry.
const HASHED = 1 << 20;
/// Set if the filesystem monitor is valid.
const FSMONITOR_VALID = 1 << 21;
/// Remove in work directory
const WORKTREE_REMOVE = 1 << 22;
/// Set to indicate the entry exists in multiple stages at once due to conflicts.
const CONFLICTED = 1 << 23;
/// Indicates that the entry was already turned into a tree.
const UNPACKED = 1 << 24;
/// Only temporarily used by unpack_trees() (in C)
const NEW_SKIP_WORKTREE = 1 << 25;
/// temporarily mark paths matched by a path spec
const PATHSPEC_MATCHED = 1 << 26;
/// When the index is split, this indicates the entry is up-to-date in the shared portion of the index.
const UPDATE_IN_BASE = 1 << 27;
/// Indicates the entry name is present in the base/shared index, and thus doesn't have to be stored in this one.
const STRIP_NAME = 1 << 28;
/// Created with `git add --intent-to-add` to mark empty entries that have their counter-part in the worktree, but not
/// yet in the object database.
const INTENT_TO_ADD = 1 << 29;
/// Stored at rest
const SKIP_WORKTREE = 1 << 30;
/// For future extension
const EXTENDED_2 = 1 << 31;
}
}
impl Flags {
/// Return the stage as extracted from the bits of this instance.
pub fn stage(&self) -> Stage {
match self.stage_raw() {
0 => Stage::Unconflicted,
1 => Stage::Base,
2 => Stage::Ours,
3 => Stage::Theirs,
_ => unreachable!("BUG: Flags::STAGE_MASK is two bits, whose 4 possible values we have covered"),
}
}
/// Return an entry's stage as raw number between 0 and 4.
/// Possible values are:
///
/// * 0 = no conflict,
/// * 1 = base,
/// * 2 = ours,
/// * 3 = theirs
pub fn stage_raw(&self) -> u32 {
(*self & Flags::STAGE_MASK).bits() >> 12
}
/// Transform ourselves to a storage representation to keep all flags which are to be persisted,
/// skipping all extended flags. Note that the caller has to check for the `EXTENDED` bit to be present
/// and write extended flags as well if so.
pub fn to_storage(mut self) -> at_rest::Flags {
at_rest::Flags::from_bits_retain(
{
self.remove(Self::PATH_LEN);
self
}
.bits() as u16,
)
}
}
pub(crate) mod at_rest {
use bitflags::bitflags;
bitflags! {
/// Flags how they are serialized to a storage location
#[derive(Copy, Clone, Debug)]
pub struct Flags: u16 {
/// A portion of a the flags that encodes the length of the path that follows.
const PATH_LEN = 0x0fff;
const STAGE_MASK = 0x3000;
/// If set, there is more extended flags past this one
const EXTENDED = 0x4000;
/// If set, the entry be assumed to match with the version on the working tree, as a way to avoid `lstat()` checks.
const ASSUME_VALID = 0x8000;
}
}
impl Flags {
pub fn to_memory(self) -> super::Flags {
super::Flags::from_bits_retain(self.bits() as u32)
}
}
bitflags! {
/// Extended flags - add flags for serialization here and offset them down to u16.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct FlagsExtended: u16 {
const INTENT_TO_ADD = 1 << (29 - 16);
const SKIP_WORKTREE = 1 << (30 - 16);
}
}
impl FlagsExtended {
pub fn from_flags(flags: super::Flags) -> Self {
Self::from_bits_retain(
((flags & (super::Flags::INTENT_TO_ADD | super::Flags::SKIP_WORKTREE)).bits() >> 16) as u16,
)
}
pub fn to_flags(self) -> Option<super::Flags> {
super::Flags::from_bits((self.bits() as u32) << 16)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn flags_extended_conversion() {
assert_eq!(
FlagsExtended::all().to_flags(),
Some(super::super::Flags::INTENT_TO_ADD | super::super::Flags::SKIP_WORKTREE)
);
assert_eq!(
FlagsExtended::from_flags(super::super::Flags::all()),
FlagsExtended::all()
);
}
#[test]
fn flags_from_bits_with_conflict() {
let input = 0b1110_0010_1000_1011;
assert_eq!(Flags::from_bits_retain(input).bits(), input);
}
}
}