Skip to content

Commit 8a7c2af

Browse files
committed
Merge branch 'improvements'
2 parents 9ad9c5b + 9df4929 commit 8a7c2af

File tree

15 files changed

+180
-80
lines changed

15 files changed

+180
-80
lines changed

gitoxide-core/src/hours/core.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::{
66
},
77
};
88

9-
use gix::{bstr::BStr, odb::FindExt};
9+
use gix::bstr::BStr;
1010
use itertools::Itertools;
1111
use smallvec::SmallVec;
1212

@@ -182,11 +182,7 @@ pub fn spawn_tree_delta_threads<'scope>(
182182
(true, true) => {
183183
files.modified += 1;
184184
if let Some((attrs, matches)) = attributes.as_mut() {
185-
let entry = attrs.at_entry(
186-
change.location,
187-
Some(false),
188-
|id, buf| repo.objects.find_blob(id, buf),
189-
)?;
185+
let entry = attrs.at_entry(change.location, Some(false))?;
190186
let is_text_file = if entry.matching_attributes(matches) {
191187
let attrs: SmallVec<[_; 2]> =
192188
matches.iter_selected().collect();

gitoxide-core/src/repository/attributes/query.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub(crate) mod function {
1111
use std::{io, path::Path};
1212

1313
use anyhow::{anyhow, bail};
14-
use gix::{bstr::BStr, prelude::FindExt};
14+
use gix::bstr::BStr;
1515

1616
use crate::{
1717
repository::{
@@ -40,7 +40,7 @@ pub(crate) mod function {
4040
for path in paths {
4141
let is_dir = gix::path::from_bstr(path.as_ref()).metadata().ok().map(|m| m.is_dir());
4242

43-
let entry = cache.at_entry(path.as_slice(), is_dir, |oid, buf| repo.objects.find_blob(oid, buf))?;
43+
let entry = cache.at_entry(path.as_slice(), is_dir)?;
4444
if !entry.matching_attributes(&mut matches) {
4545
continue;
4646
}
@@ -59,7 +59,7 @@ pub(crate) mod function {
5959
.index_entries_with_paths(&index)
6060
.ok_or_else(|| anyhow!("Pathspec didn't match a single path in the index"))?
6161
{
62-
let entry = cache.at_entry(path, Some(false), |oid, buf| repo.objects.find_blob(oid, buf))?;
62+
let entry = cache.at_entry(path, Some(false))?;
6363
if !entry.matching_attributes(&mut matches) {
6464
continue;
6565
}
@@ -97,7 +97,7 @@ pub(crate) mod function {
9797

9898
pub(crate) fn attributes_cache(
9999
repo: &gix::Repository,
100-
) -> anyhow::Result<(gix::worktree::Stack, IndexPersistedOrInMemory)> {
100+
) -> anyhow::Result<(gix::AttributeStack<'_>, IndexPersistedOrInMemory)> {
101101
let index = repo.index_or_load_from_head()?;
102102
let cache = repo.attributes(
103103
&index,

gitoxide-core/src/repository/attributes/validate_baseline.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub(crate) mod function {
1818
};
1919

2020
use anyhow::{anyhow, bail};
21-
use gix::{attrs::Assignment, bstr::BString, odb::FindExt, Progress};
21+
use gix::{attrs::Assignment, bstr::BString, Progress};
2222

2323
use crate::{
2424
repository::attributes::{query::attributes_cache, validate_baseline::Options},
@@ -192,9 +192,7 @@ pub(crate) mod function {
192192
);
193193

194194
for (rela_path, baseline) in rx_base {
195-
let entry = cache.at_entry(rela_path.as_str(), Some(false), |oid, buf| {
196-
repo.objects.find_blob(oid, buf)
197-
})?;
195+
let entry = cache.at_entry(rela_path.as_str(), Some(false))?;
198196
match baseline {
199197
Baseline::Attribute { assignments: expected } => {
200198
entry.matching_attributes(&mut matches);

gitoxide-core/src/repository/index/entries.rs

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ pub(crate) mod function {
2525

2626
use gix::{
2727
bstr::{BStr, BString},
28-
odb::FindExt,
2928
repository::IndexPersistedOrInMemory,
3029
Repository,
3130
};
@@ -133,27 +132,25 @@ pub(crate) mod function {
133132
.and_then(|(attrs, cache)| {
134133
// If the user wants to see assigned attributes, we always have to match.
135134
attributes.is_some().then(|| {
136-
cache
137-
.at_entry(entry.path(&index), None, |id, buf| repo.objects.find_blob(id, buf))
138-
.map(|entry| {
139-
let is_excluded = entry.is_excluded();
140-
stats.excluded += usize::from(is_excluded);
141-
let attributes: Vec<_> = {
142-
last_match = Some(entry.matching_attributes(attrs));
143-
attrs.iter().map(|m| m.assignment.to_owned()).collect()
144-
};
145-
stats.with_attributes += usize::from(!attributes.is_empty());
146-
stats.max_attributes_per_path = stats.max_attributes_per_path.max(attributes.len());
147-
if let Some(attrs) = repo_attrs.as_mut() {
148-
attributes.iter().for_each(|attr| {
149-
attrs.insert(attr.clone());
150-
});
151-
}
152-
Attrs {
153-
is_excluded,
154-
attributes,
155-
}
156-
})
135+
cache.at_entry(entry.path(&index), None).map(|entry| {
136+
let is_excluded = entry.is_excluded();
137+
stats.excluded += usize::from(is_excluded);
138+
let attributes: Vec<_> = {
139+
last_match = Some(entry.matching_attributes(attrs));
140+
attrs.iter().map(|m| m.assignment.to_owned()).collect()
141+
};
142+
stats.with_attributes += usize::from(!attributes.is_empty());
143+
stats.max_attributes_per_path = stats.max_attributes_per_path.max(attributes.len());
144+
if let Some(attrs) = repo_attrs.as_mut() {
145+
attributes.iter().for_each(|attr| {
146+
attrs.insert(attr.clone());
147+
});
148+
}
149+
Attrs {
150+
is_excluded,
151+
attributes,
152+
}
153+
})
157154
})
158155
})
159156
.transpose()?;
@@ -173,7 +170,7 @@ pub(crate) mod function {
173170
}
174171
// The user doesn't want attributes, so we set the cache position on demand only
175172
None => cache
176-
.at_entry(rela_path, Some(is_dir), |id, buf| repo.objects.find_blob(id, buf))
173+
.at_entry(rela_path, Some(is_dir))
177174
.ok()
178175
.map(|platform| platform.matching_attributes(out))
179176
.unwrap_or_default(),
@@ -251,7 +248,7 @@ pub(crate) mod function {
251248
) -> anyhow::Result<(
252249
gix::pathspec::Search,
253250
IndexPersistedOrInMemory,
254-
Option<(gix::attrs::search::Outcome, gix::worktree::Stack)>,
251+
Option<(gix::attrs::search::Outcome, gix::AttributeStack<'_>)>,
255252
)> {
256253
let index = repo.index_or_load_from_head()?;
257254
let pathspec = repo.pathspec(

gix-attributes/src/state.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ pub struct Value(KString);
1313
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1414
pub struct ValueRef<'a>(#[cfg_attr(feature = "serde", serde(borrow))] KStringRef<'a>);
1515

16-
/// Conversions
16+
/// Lifecycle
1717
impl<'a> ValueRef<'a> {
1818
/// Keep `input` as our value.
1919
pub fn from_bytes(input: &'a [u8]) -> Self {
@@ -25,7 +25,10 @@ impl<'a> ValueRef<'a> {
2525
},
2626
))
2727
}
28+
}
2829

30+
/// Access and conversions
31+
impl ValueRef<'_> {
2932
/// Access this value as byte string.
3033
pub fn as_bstr(&self) -> &BStr {
3134
self.0.as_bytes().as_bstr()
@@ -79,6 +82,14 @@ impl StateRef<'_> {
7982
pub fn is_unset(&self) -> bool {
8083
matches!(self, StateRef::Unset)
8184
}
85+
86+
/// Attempt to obtain the string value of this state, or return `None` if there is no such value.
87+
pub fn as_bstr(&self) -> Option<&BStr> {
88+
match self {
89+
StateRef::Value(v) => Some(v.as_bstr()),
90+
_ => None,
91+
}
92+
}
8293
}
8394

8495
/// Initialization

gix/src/attribute_stack.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use crate::bstr::BStr;
2+
use crate::types::AttributeStack;
3+
use crate::Repository;
4+
use gix_odb::FindExt;
5+
use std::ops::{Deref, DerefMut};
6+
7+
/// Lifecycle
8+
impl<'repo> AttributeStack<'repo> {
9+
/// Create a new instance from a `repo` and the underlying pre-configured `stack`.
10+
///
11+
/// Note that this type is typically created by [`Repository::attributes()`] or [`Repository::attributes_only()`].
12+
pub fn new(stack: gix_worktree::Stack, repo: &'repo Repository) -> Self {
13+
AttributeStack { repo, inner: stack }
14+
}
15+
16+
/// Detach the repository and return the underlying plumbing datatype.
17+
pub fn detach(self) -> gix_worktree::Stack {
18+
self.inner
19+
}
20+
}
21+
22+
impl Deref for AttributeStack<'_> {
23+
type Target = gix_worktree::Stack;
24+
25+
fn deref(&self) -> &Self::Target {
26+
&self.inner
27+
}
28+
}
29+
30+
impl DerefMut for AttributeStack<'_> {
31+
fn deref_mut(&mut self) -> &mut Self::Target {
32+
&mut self.inner
33+
}
34+
}
35+
36+
/// Platform retrieval
37+
impl<'repo> AttributeStack<'repo> {
38+
/// Append the `relative` path to the root directory of the cache and efficiently create leading directories, while assuring that no
39+
/// symlinks are in that path.
40+
/// Unless `is_dir` is known with `Some(…)`, then `relative` points to a directory itself in which case the entire resulting
41+
/// path is created as directory. If it's not known it is assumed to be a file.
42+
///
43+
/// Provide access to cached information for that `relative` path via the returned platform.
44+
pub fn at_path(
45+
&mut self,
46+
relative: impl AsRef<std::path::Path>,
47+
is_dir: Option<bool>,
48+
) -> std::io::Result<gix_worktree::stack::Platform<'_>> {
49+
self.inner
50+
.at_path(relative, is_dir, |id, buf| self.repo.objects.find_blob(id, buf))
51+
}
52+
53+
/// Obtain a platform for lookups from a repo-`relative` path, typically obtained from an index entry. `is_dir` should reflect
54+
/// whether it's a directory or not, or left at `None` if unknown.
55+
///
56+
/// If `relative` ends with `/` and `is_dir` is `None`, it is automatically assumed to be a directory.
57+
///
58+
/// ### Panics
59+
///
60+
/// - on illformed UTF8 in `relative`
61+
pub fn at_entry<'r>(
62+
&mut self,
63+
relative: impl Into<&'r BStr>,
64+
is_dir: Option<bool>,
65+
) -> std::io::Result<gix_worktree::stack::Platform<'_>> {
66+
self.inner
67+
.at_entry(relative, is_dir, |id, buf| self.repo.objects.find_blob(id, buf))
68+
}
69+
}

gix/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ mod ext;
117117
///
118118
pub mod prelude;
119119

120+
mod attribute_stack;
121+
120122
///
121123
pub mod path;
122124

@@ -129,7 +131,7 @@ pub(crate) type Config = OwnShared<gix_config::File<'static>>;
129131

130132
mod types;
131133
pub use types::{
132-
Commit, Head, Id, Object, ObjectDetached, Pathspec, Reference, Remote, Repository, Submodule, Tag,
134+
AttributeStack, Commit, Head, Id, Object, ObjectDetached, Pathspec, Reference, Remote, Repository, Submodule, Tag,
133135
ThreadSafeRepository, Tree, Worktree,
134136
};
135137

gix/src/pathspec.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
use gix_odb::FindExt;
33
pub use gix_pathspec::*;
44

5-
use crate::{bstr::BStr, Pathspec, Repository};
5+
use crate::{bstr::BStr, AttributeStack, Pathspec, Repository};
66

77
///
88
pub mod init {
@@ -59,21 +59,28 @@ impl<'repo> Pathspec<'repo> {
5959
)?,
6060
)?;
6161
let cache = needs_cache.then(make_attributes).transpose()?;
62-
Ok(Self { repo, search, cache })
62+
Ok(Self {
63+
repo,
64+
search,
65+
stack: cache,
66+
})
6367
}
6468
/// Turn ourselves into the functional parts for direct usage.
65-
/// Note that the [`cache`](gix_worktree::Stack) is only set if one of the [`search` patterns](Search)
69+
/// Note that the [`cache`](AttributeStack) is only set if one of the [`search` patterns](Search)
6670
/// is specifying attributes to match for.
67-
pub fn into_parts(self) -> (Search, Option<gix_worktree::Stack>) {
68-
(self.search, self.cache)
71+
pub fn into_parts(self) -> (Search, Option<AttributeStack<'repo>>) {
72+
(
73+
self.search,
74+
self.stack.map(|stack| AttributeStack::new(stack, self.repo)),
75+
)
6976
}
7077
}
7178

7279
/// Access
7380
impl<'repo> Pathspec<'repo> {
7481
/// Return the attributes cache which is used when matching attributes in pathspecs, or `None` if none of the pathspecs require that.
7582
pub fn attributes(&self) -> Option<&gix_worktree::Stack> {
76-
self.cache.as_ref()
83+
self.stack.as_ref()
7784
}
7885

7986
/// Return the search itself which can be used for matching paths or accessing the actual patterns that will be used.
@@ -99,8 +106,8 @@ impl<'repo> Pathspec<'repo> {
99106
) -> Option<gix_pathspec::search::Match<'_>> {
100107
self.search
101108
.pattern_matching_relative_path(relative_path, is_dir, |relative_path, case, is_dir, out| {
102-
let cache = self.cache.as_mut().expect("initialized in advance");
103-
cache
109+
let stack = self.stack.as_mut().expect("initialized in advance");
110+
stack
104111
.set_case(case)
105112
.at_entry(relative_path, Some(is_dir), |id, buf| {
106113
self.repo.objects.find_blob(id, buf)

0 commit comments

Comments
 (0)