Skip to content

Commit 9375713

Browse files
committed
improve terminology and documentation
1 parent 8df154b commit 9375713

File tree

5 files changed

+120
-109
lines changed

5 files changed

+120
-109
lines changed

gix-worktree/src/index/status.rs

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
//!
1+
//! Compute changes that need to be applied to the ! index to obtain a different
2+
//state (either a worktree or a second index). ! Also computes other related
3+
//information like conflicts/added files and provides ! everything necessary
4+
//for `gix diff`/`git status` (when not both arguments are revisions) ! and
5+
//dirtiness checks that are run before commands like `git merge`/`git rebase`
26

37
use bstr::BStr;
48

@@ -8,53 +12,65 @@ pub mod worktree;
812
///
913
pub mod index;
1014

11-
///
12-
pub mod recorder;
15+
mod recorder;
16+
pub use recorder::Recorder;
1317

1418
///
15-
pub mod diff;
19+
pub mod content;
1620

1721
/// The status of an index entry in a worktree
18-
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)]
19-
pub enum Status<T = ()> {
20-
#[default]
21-
/// The file in the worktree is identical to the index entry
22-
Unchanged,
23-
/// An index entry has no corresponding file in the worktree.
22+
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
23+
pub enum Change<T = ()> {
24+
/// This index entry has been removed
2425
Removed,
25-
/// The type of file changed (symlink <=> file) performing a
26-
/// diff is usually not necessary/desired
26+
/// The type of file changed (for example symlink <=> file) performing
27+
/// a diff
2728
TypeChange,
28-
/// The worktree file that belongs to this index has a changed stat
29-
/// therefore could have been modified.
30-
///
31-
/// Note that this doesn't necessarily mean that the *content* of the file changed.
32-
/// A modified even is emitted whenever a change could have occurred. It is up
33-
/// to API consumers to check the file for content changes
29+
/// This index entry has been modified in some form (permission change
30+
/// or content change or both)
3431
Modified {
3532
/// Indicates that one of the stat changes was an executable bit change
3633
/// which is a significant change itself (for git status)
3734
/// these files don't need to be rechanged
3835
executable_bit_changed: bool,
39-
/// The output of the diff run on this entry.
36+
/// The output of the [`ContentComparison`] run on this entry.
4037
/// if the there is no content change and only the executable bit
4138
/// changed than this is `None`
42-
diff: Option<T>,
39+
content_change: Option<T>,
4340
},
4441
/// An index entry that correspond to an untracked worktree file marked with `git add`
4542
Added,
4643
}
4744

48-
///
45+
/// Collects the changes produced by comparing an index entry to
46+
/// the worktree (or another index)
4947
pub trait Collector<'index>: Send {
5048
/// Data generated by comparing two files/entries
51-
type Diff;
49+
type ContentChange;
5250
///
5351
fn visit_entry(
5452
&mut self,
5553
entry: &'index gix_index::Entry,
5654
path: &'index BStr,
57-
status: Status<Self::Diff>,
55+
status: Option<Change<Self::ContentChange>>,
5856
conflict: bool,
5957
);
6058
}
59+
60+
/// Compares the content of two blobs while performing a diff
61+
pub trait ContentComparison: Send + Sync {
62+
/// Output data produced by this
63+
type Output;
64+
/// Compares an index entry to a different blob. The size of the blob
65+
/// and a lazy handle to read its data (from disk or ODB) is provided. If
66+
/// this function returns `None` the entry and the blob are assumed to be
67+
/// identical if this assumption is broken by a faulty implementation then
68+
/// the index state can be messed up
69+
fn compare_blob<'a, E>(
70+
&self,
71+
entry: &'a gix_index::Entry,
72+
blob_size: usize,
73+
blob: impl content::LazyBlob<'a, E>,
74+
resolve_oid: impl FnMut(gix_hash::ObjectId) -> Result<&'a [u8], E>,
75+
) -> Result<Option<Self::Output>, E>;
76+
}

gix-worktree/src/index/status/diff.rs renamed to gix-worktree/src/index/status/content.rs

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,24 @@ use gix_index as index;
44
use gix_object::encode::loose_header;
55
use index::Entry;
66

7-
///
7+
use crate::index::status::ContentComparison;
8+
9+
/// Lazy borrwoed access to blob data from disk or ODB
810
pub trait LazyBlob<'a, E> {
9-
///
11+
/// Returns the contents of this blob, potentially performs IO
12+
/// and other expensive operations and should only be called
13+
/// when necessary.
1014
fn read(self) -> Result<&'a [u8], E>;
1115
}
1216

13-
///
14-
pub trait Diff: Send + Sync {
15-
///
16-
type Output;
17-
///
18-
fn content_changed<'a, E>(
19-
&self,
20-
entry: &'a Entry,
21-
blob_size: usize,
22-
blob: impl LazyBlob<'a, E>,
23-
resolve_oid: impl FnMut(gix_hash::ObjectId) -> Result<&'a [u8], E>,
24-
) -> Result<Option<Self::Output>, E>;
25-
}
26-
27-
/// compares to blobs by comparing their size and oid, only looks at the file if
17+
/// Compares to blobs by comparing their size and oid, only looks at the file if
2818
/// the size matches, therefore very fast
29-
pub struct Fast;
19+
pub struct FastEq;
3020

31-
impl Diff for Fast {
21+
impl ContentComparison for FastEq {
3222
type Output = ();
3323

34-
fn content_changed<'a, E>(
24+
fn compare_blob<'a, E>(
3525
&self,
3626
entry: &'a Entry,
3727
blob_size: usize,
@@ -58,12 +48,12 @@ impl Diff for Fast {
5848
/// Compares files to blobs by comparing their oids. Same as [`Fast`] but does
5949
/// not contain a fast path for files with mismatched files and therefore always
6050
/// returns an OID that can be reused later
61-
pub struct Hash;
51+
pub struct HashEq;
6252

63-
impl Diff for Hash {
53+
impl ContentComparison for HashEq {
6454
type Output = ObjectId;
6555

66-
fn content_changed<'a, E>(
56+
fn compare_blob<'a, E>(
6757
&self,
6858
entry: &'a Entry,
6959
_blob_size: usize,

gix-worktree/src/index/status/recorder.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,27 @@ use bstr::BStr;
22

33
use gix_index as index;
44

5-
use crate::index::status::{Collector, Status};
5+
use crate::index::status::{Change, Collector};
66

7-
///
7+
/// Convenience [`Collecotr`] implementation that collects all non-trivial
8+
/// changes into a `Vec`
89
#[derive(Debug, Default)]
910
pub struct Recorder<'index, T = ()> {
10-
/// collected records, unchanged fields are excluded
11-
pub records: Vec<(&'index BStr, Status<T>, bool)>,
11+
/// collected changes, index entries without conflicts or changes are excluded
12+
pub records: Vec<(&'index BStr, Option<Change<T>>, bool)>,
1213
}
1314

1415
impl<'index, T: Send> Collector<'index> for Recorder<'index, T> {
15-
type Diff = T;
16+
type ContentChange = T;
1617

1718
fn visit_entry(
1819
&mut self,
1920
_entry: &'index index::Entry,
2021
path: &'index BStr,
21-
status: Status<Self::Diff>,
22+
status: Option<Change<Self::ContentChange>>,
2223
conflict: bool,
2324
) {
24-
if !matches!(status, Status::Unchanged) {
25+
if conflict || status.is_some() {
2526
self.records.push((path, status, conflict))
2627
}
2728
}

gix-worktree/src/index/status/worktree.rs

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ use std::io;
22
use std::marker::PhantomData;
33
use std::path::Path;
44

5-
use crate::index::status::diff::{self, Diff};
6-
use crate::index::status::{Collector, Status};
5+
use crate::index::status::{content, Change, Collector, ContentComparison};
76
use crate::{fs, read};
87
use bstr::BStr;
98
use filetime::FileTime;
@@ -62,12 +61,18 @@ impl Default for Options {
6261
}
6362
}
6463

65-
/// Calculates the status of worktree
66-
pub fn status<'index, T: Send>(
64+
/// Calculates the changes that need to be applied to an index to obtain a
65+
/// worktree. Note that this isn't technically quite what this function does
66+
/// as this also provides some additional information (whether a file has
67+
/// conflicts) and files that were added with `git add` are shown as a special
68+
/// changes despite not technically requiring a change to the index (since `gid
69+
/// add` already added the file to the index) but the naming matches the intuition
70+
/// of a git user (and matches `git status`/git diff`)
71+
pub fn changes_to_obtain<'index, T: Send>(
6772
index: &'index mut index::State,
6873
worktree: &Path,
69-
collector: &mut impl Collector<'index, Diff = T>,
70-
diff: &impl Diff<Output = T>,
74+
collector: &mut impl Collector<'index, ContentChange = T>,
75+
diff: &impl ContentComparison<Output = T>,
7176
options: Options,
7277
) -> Result<(), Error> {
7378
// the order is absoluty critical here
@@ -116,13 +121,13 @@ struct State<'a, 'b> {
116121
options: &'a Options,
117122
}
118123

119-
type StatusResult<'index, T> = Result<(&'index index::Entry, &'index BStr, Status<T>, bool), Error>;
124+
type StatusResult<'index, T> = Result<(&'index index::Entry, &'index BStr, Option<Change<T>>, bool), Error>;
120125

121126
impl<'index> State<'_, 'index> {
122127
fn process<T>(
123128
&mut self,
124129
entry: &'index mut index::Entry,
125-
diff: &impl Diff<Output = T>,
130+
diff: &impl ContentComparison<Output = T>,
126131
) -> Option<StatusResult<'index, T>> {
127132
let conflict = match entry.stage() {
128133
0 => false,
@@ -146,8 +151,8 @@ impl<'index> State<'_, 'index> {
146151
&mut self,
147152
entry: &mut index::Entry,
148153
git_path: &BStr,
149-
diff: &impl Diff<Output = T>,
150-
) -> Result<Status<T>, Error> {
154+
diff: &impl ContentComparison<Output = T>,
155+
) -> Result<Option<Change<T>>, Error> {
151156
// TODO fs caache
152157
let worktree_path = path::try_from_bstr(git_path).map_err(|_| Error::IllformedUtf8)?;
153158
let worktree_path = self.worktree.join(worktree_path);
@@ -163,24 +168,24 @@ impl<'index> State<'_, 'index> {
163168
// TODO: submodules:
164169
// if entry.mode.contains(Mode::COMMIT) &&
165170
// resolve_gitlink_ref(ce->name, "HEAD", &sub))
166-
return Ok(Status::Removed);
171+
return Ok(Some(Change::Removed));
167172
}
168173
Ok(metadata) => metadata,
169-
Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(Status::Removed),
174+
Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(Some(Change::Removed)),
170175
Err(err) => {
171176
return Err(err.into());
172177
}
173178
};
174179
if self.options.check_added && entry.flags.contains(index::entry::Flags::INTENT_TO_ADD) {
175-
return Ok(Status::Added);
180+
return Ok(Some(Change::Added));
176181
}
177182
let new_stat = index::entry::Stat::from_fs(&metadata)?;
178183
let executable_bit_changed =
179184
match entry
180185
.mode
181186
.change_to_match_fs(&metadata, self.options.fs.symlink, self.options.fs.executable_bit)
182187
{
183-
Some(index::entry::mode::Change::Type { .. }) => return Ok(Status::TypeChange),
188+
Some(index::entry::mode::Change::Type { .. }) => return Ok(Some(Change::TypeChange)),
184189
Some(index::entry::mode::Change::ExecutableBit) => true,
185190
None => false,
186191
};
@@ -202,7 +207,7 @@ impl<'index> State<'_, 'index> {
202207
{
203208
racy_clean = new_stat.is_racy(self.timestamp, self.options.stat);
204209
if !racy_clean {
205-
return Ok(Status::Unchanged);
210+
return Ok(None);
206211
}
207212
}
208213

@@ -212,22 +217,22 @@ impl<'index> State<'_, 'index> {
212217
entry,
213218
options: self.options,
214219
};
215-
let diff = diff.content_changed::<Error>(entry, metadata.len() as usize, file, |_| Ok(&[]))?;
220+
let content_change = diff.compare_blob::<Error>(entry, metadata.len() as usize, file, |_| Ok(&[]))?;
216221
// this file is racy clean! Set the size to 0 so we keep detecting this
217222
// as the file is updated
218-
if diff.is_some() && racy_clean {
223+
if content_change.is_some() && racy_clean {
219224
entry.stat.size = 0;
220225
}
221-
if diff.is_some() || executable_bit_changed {
222-
Ok(Status::Modified {
226+
if content_change.is_some() || executable_bit_changed {
227+
Ok(Some(Change::Modified {
223228
executable_bit_changed,
224-
diff,
225-
})
229+
content_change,
230+
}))
226231
} else {
227232
// don't diff against this file next time since
228233
// we know the file is unchanged
229234
entry.stat = new_stat;
230-
Ok(Status::Unchanged)
235+
Ok(None)
231236
}
232237
}
233238
}
@@ -237,7 +242,7 @@ struct Reducer<'a, 'index, T: Collector<'index>> {
237242
phantom: PhantomData<fn(&'index ())>,
238243
}
239244

240-
impl<'index, T, C: Collector<'index, Diff = T>> Reduce for Reducer<'_, 'index, C> {
245+
impl<'index, T, C: Collector<'index, ContentChange = T>> Reduce for Reducer<'_, 'index, C> {
241246
type Input = Vec<StatusResult<'index, T>>;
242247

243248
type FeedProduce = ();
@@ -248,8 +253,8 @@ impl<'index, T, C: Collector<'index, Diff = T>> Reduce for Reducer<'_, 'index, C
248253

249254
fn feed(&mut self, items: Self::Input) -> Result<Self::FeedProduce, Self::Error> {
250255
for item in items {
251-
let (entry, path, status, conflict) = item?;
252-
self.collector.visit_entry(entry, path, status, conflict);
256+
let (entry, path, change, conflict) = item?;
257+
self.collector.visit_entry(entry, path, change, conflict);
253258
}
254259
Ok(())
255260
}
@@ -265,7 +270,7 @@ struct WorktreeFile<'a> {
265270
options: &'a Options,
266271
}
267272

268-
impl<'a> diff::LazyBlob<'a, Error> for WorktreeFile<'a> {
273+
impl<'a> content::LazyBlob<'a, Error> for WorktreeFile<'a> {
269274
fn read(self) -> Result<&'a [u8], Error> {
270275
let res = read::data_to_buf_with_meta(
271276
self.path,

0 commit comments

Comments
 (0)