Skip to content

Commit a6cc781

Browse files
committed
feat!: turn entry::Stage into an enum.
This way, it's much clearer what possible values are. For performance, it also adds `Entry::stage_raw()` to avoid having to match on a number.
1 parent 16dc027 commit a6cc781

File tree

5 files changed

+69
-25
lines changed

5 files changed

+69
-25
lines changed

gix-index/src/access/mod.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::{cmp::Ordering, ops::Range};
33
use bstr::{BStr, ByteSlice, ByteVec};
44
use filetime::FileTime;
55

6+
use crate::entry::{Stage, StageRaw};
67
use crate::{entry, extension, AccelerateLookup, Entry, PathStorage, PathStorageRef, State, Version};
78

89
// TODO: integrate this somehow, somewhere, depending on later usage.
@@ -81,7 +82,7 @@ impl State {
8182
res
8283
})
8384
.ok()?;
84-
self.entry_index_by_idx_and_stage(path, idx, stage, stage_cmp)
85+
self.entry_index_by_idx_and_stage(path, idx, stage as StageRaw, stage_cmp)
8586
}
8687

8788
/// Walk as far in `direction` as possible, with [`Ordering::Greater`] towards higher stages, and [`Ordering::Less`]
@@ -112,7 +113,7 @@ impl State {
112113
&self,
113114
path: &BStr,
114115
idx: usize,
115-
wanted_stage: entry::Stage,
116+
wanted_stage: entry::StageRaw,
116117
stage_cmp: Ordering,
117118
) -> Option<usize> {
118119
match stage_cmp {
@@ -121,15 +122,15 @@ impl State {
121122
.enumerate()
122123
.rev()
123124
.take_while(|(_, e)| e.path(self) == path)
124-
.find_map(|(idx, e)| (e.stage() == wanted_stage).then_some(idx)),
125+
.find_map(|(idx, e)| (e.stage_raw() == wanted_stage).then_some(idx)),
125126
Ordering::Equal => Some(idx),
126127
Ordering::Less => self
127128
.entries
128129
.get(idx + 1..)?
129130
.iter()
130131
.enumerate()
131132
.take_while(|(_, e)| e.path(self) == path)
132-
.find_map(|(ofs, e)| (e.stage() == wanted_stage).then_some(idx + ofs + 1)),
133+
.find_map(|(ofs, e)| (e.stage_raw() == wanted_stage).then_some(idx + ofs + 1)),
133134
}
134135
}
135136

@@ -291,15 +292,15 @@ impl State {
291292
.binary_search_by(|e| {
292293
let res = e.path(self).cmp(path);
293294
if res.is_eq() {
294-
stage_at_index = e.stage();
295+
stage_at_index = e.stage_raw();
295296
}
296297
res
297298
})
298299
.ok()?;
299300
let idx = if stage_at_index == 0 || stage_at_index == 2 {
300301
idx
301302
} else {
302-
self.entry_index_by_idx_and_stage(path, idx, 2, stage_at_index.cmp(&2))?
303+
self.entry_index_by_idx_and_stage(path, idx, Stage::Ours as StageRaw, stage_at_index.cmp(&2))?
303304
};
304305
Some(&self.entries[idx])
305306
}
@@ -334,13 +335,13 @@ impl State {
334335
+ self.entries[low..].partition_point(|e| e.path(self).get(..prefix_len).map_or(false, |p| p <= prefix));
335336

336337
let low_entry = &self.entries.get(low)?;
337-
if low_entry.stage() != 0 {
338+
if low_entry.stage_raw() != 0 {
338339
low = self
339340
.walk_entry_stages(low_entry.path(self), low, Ordering::Less)
340341
.unwrap_or(low);
341342
}
342343
if let Some(high_entry) = self.entries.get(high) {
343-
if high_entry.stage() != 0 {
344+
if high_entry.stage_raw() != 0 {
344345
high = self
345346
.walk_entry_stages(high_entry.path(self), high, Ordering::Less)
346347
.unwrap_or(high);
@@ -374,7 +375,7 @@ impl State {
374375
.binary_search_by(|e| {
375376
let res = e.path(self).cmp(path);
376377
if res.is_eq() {
377-
stage_at_index = e.stage();
378+
stage_at_index = e.stage_raw();
378379
}
379380
res
380381
})

gix-index/src/entry/flags.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,23 @@ bitflags! {
6262
impl Flags {
6363
/// Return the stage as extracted from the bits of this instance.
6464
pub fn stage(&self) -> Stage {
65+
match self.stage_raw() {
66+
0 => Stage::Unconflicted,
67+
1 => Stage::Base,
68+
2 => Stage::Ours,
69+
3 => Stage::Theirs,
70+
_ => unreachable!("BUG: Flags::STAGE_MASK is two bits, whose 4 possible values we have covered"),
71+
}
72+
}
73+
74+
/// Return an entry's stage as raw number between 0 and 4.
75+
/// Possible values are:
76+
///
77+
/// * 0 = no conflict,
78+
/// * 1 = base,
79+
/// * 2 = ours,
80+
/// * 3 = theirs
81+
pub fn stage_raw(&self) -> u32 {
6582
(*self & Flags::STAGE_MASK).bits() >> 12
6683
}
6784

gix-index/src/entry/mod.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
1-
/// The stage of an entry, one of…
1+
/// The stage of an entry.
2+
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
3+
pub enum Stage {
4+
/// This is the default, and most entries are in this stage.
5+
#[default]
6+
Unconflicted = 0,
7+
/// The entry is the common base between 'our' change and 'their' change, for comparison.
8+
Base = 1,
9+
/// The entry represents our change.
10+
Ours = 2,
11+
/// The entry represents their change.
12+
Theirs = 3,
13+
}
14+
15+
// The stage of an entry, one of…
216
/// * 0 = no conflict,
317
/// * 1 = base,
418
/// * 2 = ours,
519
/// * 3 = theirs
6-
pub type Stage = u32;
20+
pub type StageRaw = u32;
721

822
///
923
#[allow(clippy::empty_docs)]
@@ -78,6 +92,17 @@ mod access {
7892
pub fn stage(&self) -> entry::Stage {
7993
self.flags.stage()
8094
}
95+
96+
/// Return an entry's stage as raw number between 0 and 4.
97+
/// Possible values are:
98+
///
99+
/// * 0 = no conflict,
100+
/// * 1 = base,
101+
/// * 2 = ours,
102+
/// * 3 = theirs
103+
pub fn stage_raw(&self) -> u32 {
104+
self.flags.stage_raw()
105+
}
81106
}
82107
}
83108

gix-index/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ pub struct State {
144144
}
145145

146146
mod impls {
147+
use crate::entry::Stage;
147148
use std::fmt::{Debug, Formatter};
148149

149150
use crate::State;
@@ -155,11 +156,10 @@ mod impls {
155156
f,
156157
"{} {}{:?} {} {}",
157158
match entry.flags.stage() {
158-
0 => " ",
159-
1 => "BASE ",
160-
2 => "OURS ",
161-
3 => "THEIRS ",
162-
_ => "UNKNOWN",
159+
Stage::Unconflicted => " ",
160+
Stage::Base => "BASE ",
161+
Stage::Ours => "OURS ",
162+
Stage::Theirs => "THEIRS ",
163163
},
164164
if entry.flags.is_empty() {
165165
"".to_string()

gix-index/tests/index/access.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::index::Fixture;
22
use bstr::{BString, ByteSlice};
3+
use gix_index::entry::Stage;
34

45
fn icase_fixture() -> gix_index::File {
56
Fixture::Generated("v2_icase_name_clashes").open()
@@ -11,7 +12,7 @@ fn entry_by_path() {
1112
for entry in file.entries() {
1213
let path = entry.path(&file);
1314
assert_eq!(file.entry_by_path(path), Some(entry));
14-
assert_eq!(file.entry_by_path_and_stage(path, 0), Some(entry));
15+
assert_eq!(file.entry_by_path_and_stage(path, Stage::Unconflicted), Some(entry));
1516
}
1617
}
1718

@@ -140,27 +141,27 @@ fn entry_by_path_and_stage() {
140141
for entry in file.entries() {
141142
let path = entry.path(&file);
142143
assert_eq!(
143-
file.entry_index_by_path_and_stage(path, 0)
144+
file.entry_index_by_path_and_stage(path, Stage::Unconflicted)
144145
.map(|idx| &file.entries()[idx]),
145146
Some(entry)
146147
);
147-
assert_eq!(file.entry_by_path_and_stage(path, 0), Some(entry));
148+
assert_eq!(file.entry_by_path_and_stage(path, Stage::Unconflicted), Some(entry));
148149
}
149150
}
150151

151152
#[test]
152153
fn entry_by_path_with_conflicting_file() {
153154
let file = Fixture::Loose("conflicting-file").open();
154-
for expected_stage in [1 /* common ancestor */, 2 /* ours */, 3 /* theirs */] {
155+
for expected_stage in [Stage::Base,Stage::Ours, Stage::Theirs] {
155156
assert!(
156157
file.entry_by_path_and_stage("file".into(), expected_stage).is_some(),
157-
"we have no stage 0 during a conflict, but all other ones. Missed {expected_stage}"
158+
"we have no stage 0 during a conflict, but all other ones. Missed {expected_stage:?}"
158159
);
159160
}
160161

161162
assert_eq!(
162163
file.entry_by_path("file".into()).expect("found").stage(),
163-
2,
164+
Stage::Ours,
164165
"we always find our stage while in a merge"
165166
);
166167
}
@@ -226,13 +227,13 @@ fn sort_entries() {
226227

227228
for (idx, entry) in file.entries()[..valid_entries].iter().enumerate() {
228229
assert_eq!(
229-
file.entry_index_by_path_and_stage_bounded(entry.path(&file), 0, valid_entries),
230+
file.entry_index_by_path_and_stage_bounded(entry.path(&file), Stage::Unconflicted, valid_entries),
230231
Some(idx),
231232
"we can still find entries in the correctly sorted region"
232233
);
233234
}
234235
assert_eq!(
235-
file.entry_by_path_and_stage(new_entry_path, 0),
236+
file.entry_by_path_and_stage(new_entry_path, Stage::Unconflicted),
236237
None,
237238
"new entry can't be found due to incorrect order"
238239
);
@@ -241,7 +242,7 @@ fn sort_entries() {
241242
assert!(file.verify_entries().is_ok(), "sorting of entries restores invariants");
242243

243244
assert_eq!(
244-
file.entry_by_path_and_stage(new_entry_path, 0)
245+
file.entry_by_path_and_stage(new_entry_path, Stage::Unconflicted)
245246
.expect("can be found")
246247
.path(&file),
247248
new_entry_path,

0 commit comments

Comments
 (0)