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