Skip to content

Commit a60bdd6

Browse files
[fix] Store bytes in EntryMode so Tree roundtrips
Before this change, EntryMode wrapped a `u16` which didn't store enough information to match the git representation as a `&[u8]` of len 5 or 6. In particular, the mode that represents a `Tree` could be represented as `"40000"` or `"040000"`, and the difference would get lost once it was represented as `0o40000`. We now have two ways to represent the `EntryMode`: * `EntryMode` is backed by an owned `BString`. * `EntryModeRef` is backed by a `&bstr` and bound to the lifetime of the owner of these bytes. This allows call-sites to pick a trade-off between convenience (`EntryMode` allows to not worry about lifetimes) vs performance (`EntryModeRef` doesn't allocate) and fits the general paradigm used in the wider gitoxide project. Fixes [issue 1887](#1887)
1 parent 513af24 commit a60bdd6

File tree

39 files changed

+530
-340
lines changed

39 files changed

+530
-340
lines changed

examples/ls-tree.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,17 @@ fn run(args: Args) -> anyhow::Result<()> {
4646
tree.iter()
4747
.filter_map(|res| res.ok().map(|entry| entry.inner)) // dropping errors silently
4848
.filter(|entry| !args.tree_only || (entry.mode.is_tree()))
49-
.map(|entry| Entry::new(entry.mode, entry.oid.to_owned(), entry.filename.to_owned()))
49+
.map(|entry| Entry::new(entry.mode.to_owned(), entry.oid.to_owned(), entry.filename.to_owned()))
5050
.collect::<Vec<_>>()
5151
};
5252

5353
let mut out = stdout().lock();
5454
for entry in entries {
5555
writeln!(
5656
out,
57-
"{:06o} {:4} {} {}",
58-
*entry.mode,
59-
entry.mode.as_str(),
57+
"{:>6o} {:4} {} {}",
58+
entry.mode,
59+
entry.mode.kind().as_descriptive_str(),
6060
entry.hash,
6161
entry.path
6262
)?;

gitoxide-core/src/repository/cat.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ pub fn display_object(
2424
match cache.expect("is some") {
2525
(BlobFormat::Git, _) => unreachable!("no need for a cache when querying object db"),
2626
(BlobFormat::Worktree, cache) => {
27-
let platform = cache.attr_stack.at_entry(path, Some(mode.into()), &repo.objects)?;
27+
let platform = cache
28+
.attr_stack
29+
.at_entry(path, Some(mode.clone().into()), &repo.objects)?;
2830
let object = id.object()?;
2931
let mut converted = cache.filter.worktree_filter.convert_to_worktree(
3032
&object.data,

gitoxide-core/src/repository/diff.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,19 @@ fn write_changes(
4747
entry_mode,
4848
..
4949
} => {
50-
writeln!(out, "A: {}", typed_location(location, entry_mode))?;
50+
writeln!(out, "A: {}", typed_location(location, &entry_mode))?;
5151
writeln!(out, " {}", id.attach(repo).shorten_or_id())?;
52-
writeln!(out, " -> {:o}", entry_mode)?;
52+
writeln!(out, " -> {entry_mode:o}")?;
5353
}
5454
gix::diff::tree_with_rewrites::Change::Deletion {
5555
location,
5656
id,
5757
entry_mode,
5858
..
5959
} => {
60-
writeln!(out, "D: {}", typed_location(location, entry_mode))?;
60+
writeln!(out, "D: {}", typed_location(location, &entry_mode))?;
6161
writeln!(out, " {}", id.attach(repo).shorten_or_id())?;
62-
writeln!(out, " {:o} ->", entry_mode)?;
62+
writeln!(out, " {entry_mode:o} ->")?;
6363
}
6464
gix::diff::tree_with_rewrites::Change::Modification {
6565
location,
@@ -68,15 +68,15 @@ fn write_changes(
6868
previous_entry_mode,
6969
entry_mode,
7070
} => {
71-
writeln!(out, "M: {}", typed_location(location, entry_mode))?;
71+
writeln!(out, "M: {}", typed_location(location, &entry_mode))?;
7272
writeln!(
7373
out,
7474
" {previous_id} -> {id}",
7575
previous_id = previous_id.attach(repo).shorten_or_id(),
7676
id = id.attach(repo).shorten_or_id()
7777
)?;
7878
if previous_entry_mode != entry_mode {
79-
writeln!(out, " {:o} -> {:o}", previous_entry_mode, entry_mode)?;
79+
writeln!(out, " {previous_entry_mode:o} -> {entry_mode:o}")?;
8080
}
8181
}
8282
gix::diff::tree_with_rewrites::Change::Rewrite {
@@ -91,8 +91,8 @@ fn write_changes(
9191
writeln!(
9292
out,
9393
"R: {source} -> {dest}",
94-
source = typed_location(source_location, source_entry_mode),
95-
dest = typed_location(location, entry_mode)
94+
source = typed_location(source_location, &source_entry_mode),
95+
dest = typed_location(location, &entry_mode)
9696
)?;
9797
writeln!(
9898
out,
@@ -101,7 +101,7 @@ fn write_changes(
101101
id = id.attach(repo).shorten_or_id()
102102
)?;
103103
if source_entry_mode != entry_mode {
104-
writeln!(out, " {:o} -> {:o}", source_entry_mode, entry_mode)?;
104+
writeln!(out, " {source_entry_mode:o} -> {entry_mode:o}")?;
105105
}
106106
}
107107
}
@@ -110,7 +110,7 @@ fn write_changes(
110110
Ok(())
111111
}
112112

113-
fn typed_location(mut location: BString, mode: EntryMode) -> BString {
113+
fn typed_location(mut location: BString, mode: &EntryMode) -> BString {
114114
if mode.is_tree() {
115115
location.push(b'/');
116116
}

gix-archive/src/write.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ fn append_tar_entry<W: std::io::Write>(
213213
) -> Result<(), Error> {
214214
let mut header = tar::Header::new_gnu();
215215
header.set_mtime(mtime_seconds_since_epoch as u64);
216-
header.set_entry_type(tar_entry_type(entry.mode));
216+
header.set_entry_type(tar_entry_type(entry.mode.clone()));
217217
header.set_mode(if entry.mode.is_executable() { 0o755 } else { 0o644 });
218218
buf.clear();
219219
std::io::copy(&mut entry, buf)?;

gix-archive/tests/archive.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ mod from_tree {
213213
noop_pipeline(),
214214
move |rela_path, mode, attrs| {
215215
cache
216-
.at_entry(rela_path, Some(mode.into()), &odb)
216+
.at_entry(rela_path, Some(mode.to_owned().into()), &odb)
217217
.map(|entry| entry.matching_attributes(attrs))
218218
.map(|_| ())
219219
},

gix-diff/src/rewrites/tracker.rs

+8-9
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use std::ops::Range;
99

1010
use bstr::{BStr, ByteSlice};
11-
use gix_object::tree::{EntryKind, EntryMode};
11+
use gix_object::tree::{EntryKind, EntryMode, EntryModeRef};
1212

1313
use crate::rewrites::tracker::visit::SourceKind;
1414
use crate::tree::visit::{Action, ChangeId, Relation};
@@ -69,15 +69,15 @@ impl<T: Change> Item<T> {
6969
fn location<'a>(&self, backing: &'a [u8]) -> &'a BStr {
7070
backing[self.path.clone()].as_ref()
7171
}
72-
fn entry_mode_compatible(&self, other: EntryMode) -> bool {
72+
fn entry_mode_compatible(&self, other: EntryModeRef<'_>) -> bool {
7373
use EntryKind::*;
7474
matches!(
7575
(other.kind(), self.change.entry_mode().kind()),
7676
(Blob | BlobExecutable, Blob | BlobExecutable) | (Link, Link) | (Tree, Tree)
7777
)
7878
}
7979

80-
fn is_source_for_destination_of(&self, kind: visit::SourceKind, dest_item_mode: EntryMode) -> bool {
80+
fn is_source_for_destination_of(&self, kind: visit::SourceKind, dest_item_mode: EntryModeRef<'_>) -> bool {
8181
self.entry_mode_compatible(dest_item_mode)
8282
&& match kind {
8383
visit::SourceKind::Rename => !self.emitted && matches!(self.change.kind(), ChangeKind::Deletion),
@@ -687,7 +687,8 @@ fn find_match<'a, T: Change>(
687687
let res = items[range.clone()].iter().enumerate().find_map(|(mut src_idx, src)| {
688688
src_idx += range.start;
689689
*num_checks += 1;
690-
(src_idx != item_idx && src.is_source_for_destination_of(kind, item_mode)).then_some((src_idx, src, None))
690+
(src_idx != item_idx && src.is_source_for_destination_of(kind, (&item_mode).into()))
691+
.then_some((src_idx, src, None))
691692
});
692693
if let Some(src) = res {
693694
return Ok(Some(src));
@@ -696,11 +697,9 @@ fn find_match<'a, T: Change>(
696697
let mut has_new = false;
697698
let percentage = percentage.expect("it's set to something below 1.0 and we assured this");
698699

699-
for (can_idx, src) in items
700-
.iter()
701-
.enumerate()
702-
.filter(|(src_idx, src)| *src_idx != item_idx && src.is_source_for_destination_of(kind, item_mode))
703-
{
700+
for (can_idx, src) in items.iter().enumerate().filter(|(src_idx, src)| {
701+
*src_idx != item_idx && src.is_source_for_destination_of(kind, (&item_mode).into())
702+
}) {
704703
if !has_new {
705704
diff_cache.set_resource(
706705
item_id.to_owned(),

gix-diff/src/tree/function.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ fn delete_entry_schedule_recursion(
142142
});
143143
let is_cancelled = delegate
144144
.visit(Change::Deletion {
145-
entry_mode: entry.mode,
145+
entry_mode: entry.mode.to_owned(),
146146
oid: entry.oid.to_owned(),
147147
relation,
148148
})
@@ -174,7 +174,7 @@ fn add_entry_schedule_recursion(
174174
});
175175
if delegate
176176
.visit(Change::Addition {
177-
entry_mode: entry.mode,
177+
entry_mode: entry.mode.to_owned(),
178178
oid: entry.oid.to_owned(),
179179
relation,
180180
})
@@ -302,9 +302,9 @@ fn handle_lhs_and_rhs_with_equal_filenames(
302302
if lhs.oid != rhs.oid
303303
&& delegate
304304
.visit(Change::Modification {
305-
previous_entry_mode: lhs.mode,
305+
previous_entry_mode: lhs.mode.to_owned(),
306306
previous_oid: lhs.oid.to_owned(),
307-
entry_mode: rhs.mode,
307+
entry_mode: rhs.mode.to_owned(),
308308
oid: rhs.oid.to_owned(),
309309
})
310310
.cancelled()
@@ -321,7 +321,7 @@ fn handle_lhs_and_rhs_with_equal_filenames(
321321
delegate.push_back_tracked_path_component(lhs.filename);
322322
if delegate
323323
.visit(Change::Deletion {
324-
entry_mode: lhs.mode,
324+
entry_mode: lhs.mode.to_owned(),
325325
oid: lhs.oid.to_owned(),
326326
relation: None,
327327
})
@@ -336,7 +336,7 @@ fn handle_lhs_and_rhs_with_equal_filenames(
336336
});
337337
if delegate
338338
.visit(Change::Addition {
339-
entry_mode: rhs.mode,
339+
entry_mode: rhs.mode.to_owned(),
340340
oid: rhs.oid.to_owned(),
341341
relation,
342342
})
@@ -354,7 +354,7 @@ fn handle_lhs_and_rhs_with_equal_filenames(
354354
});
355355
if delegate
356356
.visit(Change::Deletion {
357-
entry_mode: lhs.mode,
357+
entry_mode: lhs.mode.to_owned(),
358358
oid: lhs.oid.to_owned(),
359359
relation,
360360
})
@@ -364,7 +364,7 @@ fn handle_lhs_and_rhs_with_equal_filenames(
364364
}
365365
if delegate
366366
.visit(Change::Addition {
367-
entry_mode: rhs.mode,
367+
entry_mode: rhs.mode.to_owned(),
368368
oid: rhs.oid.to_owned(),
369369
relation: None,
370370
})
@@ -380,9 +380,9 @@ fn handle_lhs_and_rhs_with_equal_filenames(
380380
if (lhs.oid != rhs.oid || lhs.mode != rhs.mode)
381381
&& delegate
382382
.visit(Change::Modification {
383-
previous_entry_mode: lhs.mode,
383+
previous_entry_mode: lhs.mode.to_owned(),
384384
previous_oid: lhs.oid.to_owned(),
385-
entry_mode: rhs.mode,
385+
entry_mode: rhs.mode.to_owned(),
386386
oid: rhs.oid.to_owned(),
387387
})
388388
.cancelled()

gix-diff/src/tree/visit.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use gix_hash::ObjectId;
2-
use gix_object::{tree, tree::EntryMode};
2+
use gix_object::{tree, tree::EntryModeRef};
33

44
/// A way to recognize and associate different [`Change`] instances.
55
///
@@ -63,15 +63,15 @@ impl Change {
6363
}
6464
}
6565
/// Return the current tree entry mode.
66-
pub fn entry_mode(&self) -> EntryMode {
66+
pub fn entry_mode(&self) -> EntryModeRef<'_> {
6767
match self {
6868
Change::Addition { entry_mode, .. }
6969
| Change::Deletion { entry_mode, .. }
70-
| Change::Modification { entry_mode, .. } => *entry_mode,
70+
| Change::Modification { entry_mode, .. } => entry_mode.into(),
7171
}
7272
}
7373
/// Return the current object id and tree entry mode of a change.
74-
pub fn oid_and_entry_mode(&self) -> (&gix_hash::oid, EntryMode) {
74+
pub fn oid_and_entry_mode(&self) -> (&gix_hash::oid, EntryModeRef<'_>) {
7575
match self {
7676
Change::Addition {
7777
oid,
@@ -83,7 +83,7 @@ impl Change {
8383
entry_mode,
8484
relation: _,
8585
}
86-
| Change::Modification { oid, entry_mode, .. } => (oid, *entry_mode),
86+
| Change::Modification { oid, entry_mode, .. } => (oid, entry_mode.into()),
8787
}
8888
}
8989
}
@@ -139,15 +139,15 @@ mod change_impls {
139139
match self {
140140
Change::Addition { entry_mode, .. }
141141
| Change::Deletion { entry_mode, .. }
142-
| Change::Modification { entry_mode, .. } => *entry_mode,
142+
| Change::Modification { entry_mode, .. } => entry_mode.clone(),
143143
}
144144
}
145145

146146
fn id_and_entry_mode(&self) -> (&oid, EntryMode) {
147147
match self {
148148
Change::Addition { entry_mode, oid, .. }
149149
| Change::Deletion { entry_mode, oid, .. }
150-
| Change::Modification { entry_mode, oid, .. } => (oid, *entry_mode),
150+
| Change::Modification { entry_mode, oid, .. } => (oid, entry_mode.clone()),
151151
}
152152
}
153153
}
@@ -161,8 +161,8 @@ mod tests {
161161
fn size_of_change() {
162162
let actual = std::mem::size_of::<Change>();
163163
assert!(
164-
actual <= 48,
165-
"{actual} <= 48: this type shouldn't grow without us knowing"
164+
actual <= 104,
165+
"{actual} <= 104: this type shouldn't grow without us knowing"
166166
);
167167
}
168168
}

0 commit comments

Comments
 (0)