Skip to content

Commit 4c5280c

Browse files
committed
feat: add Repository::attributes() and Worktree::attributes().
1 parent defa7c3 commit 4c5280c

File tree

6 files changed

+150
-36
lines changed

6 files changed

+150
-36
lines changed

gix/src/config/cache/access.rs

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,13 @@ impl Cache {
167167
symlink: boolean(self, "core.symlinks", &Core::SYMLINKS, true)?,
168168
};
169169
Ok(gix_worktree::checkout::Options {
170-
attributes: self.assemble_attribute_globals(
171-
git_dir,
172-
gix_worktree::cache::state::attributes::Source::IdMappingThenWorktree,
173-
self.attributes,
174-
)?,
170+
attributes: self
171+
.assemble_attribute_globals(
172+
git_dir,
173+
gix_worktree::cache::state::attributes::Source::IdMappingThenWorktree,
174+
self.attributes,
175+
)?
176+
.0,
175177
fs: capabilities,
176178
thread_limit,
177179
destination_is_initially_empty: false,
@@ -192,13 +194,29 @@ impl Cache {
192194
})
193195
}
194196

197+
pub(crate) fn assemble_exclude_globals(
198+
&self,
199+
git_dir: &std::path::Path,
200+
overrides: Option<gix_ignore::Search>,
201+
buf: &mut Vec<u8>,
202+
) -> Result<gix_worktree::cache::state::Ignore, config::exclude_stack::Error> {
203+
let excludes_file = match self.excludes_file().transpose()? {
204+
Some(user_path) => Some(user_path),
205+
None => self.xdg_config_path("ignore")?,
206+
};
207+
Ok(gix_worktree::cache::state::Ignore::new(
208+
overrides.unwrap_or_default(),
209+
gix_ignore::Search::from_git_dir(git_dir, excludes_file, buf)?,
210+
None,
211+
))
212+
}
195213
// TODO: at least one test, maybe related to core.attributesFile configuration.
196-
fn assemble_attribute_globals(
214+
pub(crate) fn assemble_attribute_globals(
197215
&self,
198216
git_dir: &std::path::Path,
199217
source: gix_worktree::cache::state::attributes::Source,
200218
attributes: crate::open::permissions::Attributes,
201-
) -> Result<gix_worktree::cache::state::Attributes, config::attribute_stack::Error> {
219+
) -> Result<(gix_worktree::cache::state::Attributes, Vec<u8>), config::attribute_stack::Error> {
202220
let configured_or_user_attributes = match self
203221
.trusted_file_path("core", None, Core::ATTRIBUTES_FILE.name)
204222
.transpose()?
@@ -224,12 +242,13 @@ impl Cache {
224242
let info_attributes_path = git_dir.join("info").join("attributes");
225243
let mut buf = Vec::new();
226244
let mut collection = gix_attributes::search::MetadataCollection::default();
227-
Ok(gix_worktree::cache::state::Attributes::new(
245+
let res = gix_worktree::cache::state::Attributes::new(
228246
gix_attributes::Search::new_globals(attribute_files, &mut buf, &mut collection)?,
229247
Some(info_attributes_path),
230248
source,
231249
collection,
232-
))
250+
);
251+
Ok((res, buf))
233252
}
234253

235254
pub(crate) fn xdg_config_path(

gix/src/config/mod.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,23 @@ pub mod checkout_options {
118118
}
119119
}
120120

121+
///
122+
pub mod exclude_stack {
123+
use std::path::PathBuf;
124+
125+
/// The error produced when setting up a stack to query `gitignore` information.
126+
#[derive(Debug, thiserror::Error)]
127+
#[allow(missing_docs)]
128+
pub enum Error {
129+
#[error("Could not read repository exclude")]
130+
Io(#[from] std::io::Error),
131+
#[error(transparent)]
132+
EnvironmentPermission(#[from] gix_sec::permission::Error<PathBuf>),
133+
#[error("The value for `core.excludesFile` could not be read from configuration")]
134+
ExcludesFilePathInterpolation(#[from] gix_config::path::interpolate::Error),
135+
}
136+
}
137+
121138
///
122139
pub mod attribute_stack {
123140
/// The error produced when setting up the attribute stack to query `gitattributes`.

gix/src/repository/attributes.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//! exclude information
2+
use crate::Repository;
3+
4+
/// The error returned by [`Repository::attributes()`].
5+
#[derive(Debug, thiserror::Error)]
6+
#[allow(missing_docs)]
7+
pub enum Error {
8+
#[error(transparent)]
9+
ConfigureAttributes(#[from] crate::config::attribute_stack::Error),
10+
#[error(transparent)]
11+
ConfigureExcludes(#[from] crate::config::exclude_stack::Error),
12+
}
13+
14+
impl Repository {
15+
/// Configure a file-system cache for accessing git attributes *and* excludes on a per-path basis.
16+
///
17+
/// Use `attribute_source` to specify where to read attributes from. Also note that exclude information will
18+
/// always try to read `.gitignore` files from disk before trying to read it from the `index`.
19+
///
20+
/// Note that no worktree is required for this to work, even though access to in-tree `.gitattributes` and `.gitignore` files
21+
/// would require a non-empty `index` that represents a git tree.
22+
///
23+
/// This takes into consideration all the usual repository configuration, namely:
24+
///
25+
/// * `$XDG_CONFIG_HOME/…/ignore|attributes` if `core.excludesFile|attributesFile` is *not* set, otherwise use the configured file.
26+
/// * `$GIT_DIR/info/exclude|attributes` if present.
27+
// TODO: test, provide higher-level custom Cache wrapper that is much easier to use and doesn't panic when accessing entries
28+
// by non-relative path.
29+
pub fn attributes(
30+
&self,
31+
index: &gix_index::State,
32+
source: gix_worktree::cache::state::attributes::Source,
33+
exclude_overrides: Option<gix_ignore::Search>,
34+
) -> Result<gix_worktree::Cache, Error> {
35+
let case = if self.config.ignore_case {
36+
gix_glob::pattern::Case::Fold
37+
} else {
38+
gix_glob::pattern::Case::Sensitive
39+
};
40+
let (attributes, mut buf) =
41+
self.config
42+
.assemble_attribute_globals(self.git_dir(), source, self.options.permissions.attributes)?;
43+
let ignore = self
44+
.config
45+
.assemble_exclude_globals(self.git_dir(), exclude_overrides, &mut buf)?;
46+
let state = gix_worktree::cache::State::AttributesAndIgnoreStack { attributes, ignore };
47+
let attribute_list = state.id_mappings_from_index(index, index.path_backing(), case);
48+
Ok(gix_worktree::Cache::new(
49+
// this is alright as we don't cause mutation of that directory, it's virtual.
50+
self.work_dir().unwrap_or(self.git_dir()),
51+
state,
52+
case,
53+
buf,
54+
attribute_list,
55+
))
56+
}
57+
}

gix/src/repository/excludes.rs

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,5 @@
11
//! exclude information
2-
use crate::Repository;
3-
use std::path::PathBuf;
4-
5-
/// The error returned by [`Repository::excludes()`].
6-
#[derive(Debug, thiserror::Error)]
7-
#[allow(missing_docs)]
8-
pub enum Error {
9-
#[error("Could not read repository exclude")]
10-
Io(#[from] std::io::Error),
11-
#[error(transparent)]
12-
EnvironmentPermission(#[from] gix_sec::permission::Error<PathBuf>),
13-
#[error("The value for `core.excludesFile` could not be read from configuration")]
14-
ExcludesFilePathInterpolation(#[from] gix_config::path::interpolate::Error),
15-
}
16-
2+
use crate::{config, Repository};
173
impl Repository {
184
/// Configure a file-system cache checking if files below the repository are excluded.
195
///
@@ -24,28 +10,26 @@ impl Repository {
2410
///
2511
/// * `$XDG_CONFIG_HOME/…/ignore` if `core.excludesFile` is *not* set, otherwise use the configured file.
2612
/// * `$GIT_DIR/info/exclude` if present.
13+
///
14+
/// When only excludes are desired, this is the most efficient way to obtain them. Otherwise use
15+
/// [`Repository::attributes()`] for accessing both attributes and excludes.
2716
// TODO: test, provide higher-level custom Cache wrapper that is much easier to use and doesn't panic when accessing entries
2817
// by non-relative path.
2918
pub fn excludes(
3019
&self,
3120
index: &gix_index::State,
3221
overrides: Option<gix_ignore::Search>,
33-
) -> Result<gix_worktree::Cache, Error> {
22+
) -> Result<gix_worktree::Cache, config::exclude_stack::Error> {
3423
let case = if self.config.ignore_case {
3524
gix_glob::pattern::Case::Fold
3625
} else {
3726
gix_glob::pattern::Case::Sensitive
3827
};
3928
let mut buf = Vec::with_capacity(512);
40-
let excludes_file = match self.config.excludes_file().transpose()? {
41-
Some(user_path) => Some(user_path),
42-
None => self.config.xdg_config_path("ignore")?,
43-
};
44-
let state = gix_worktree::cache::State::IgnoreStack(gix_worktree::cache::state::Ignore::new(
45-
overrides.unwrap_or_default(),
46-
gix_ignore::Search::from_git_dir(self.git_dir(), excludes_file, &mut buf)?,
47-
None,
48-
));
29+
let ignore = self
30+
.config
31+
.assemble_exclude_globals(self.git_dir(), overrides, &mut buf)?;
32+
let state = gix_worktree::cache::State::IgnoreStack(ignore);
4933
let attribute_list = state.id_mappings_from_index(index, index.path_backing(), case);
5034
Ok(gix_worktree::Cache::new(
5135
// this is alright as we don't cause mutation of that directory, it's virtual.

gix/src/repository/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ impl crate::Repository {
1919
}
2020
}
2121

22+
pub mod attributes;
2223
mod cache;
2324
mod config;
24-
pub mod excludes;
25+
mod excludes;
2526
pub(crate) mod identity;
2627
mod impls;
2728
mod init;

gix/src/worktree/mod.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ pub mod excludes {
103103
#[error(transparent)]
104104
OpenIndex(#[from] crate::worktree::open_index::Error),
105105
#[error(transparent)]
106-
CreateCache(#[from] crate::repository::excludes::Error),
106+
CreateCache(#[from] crate::config::exclude_stack::Error),
107107
}
108108

109109
impl<'repo> crate::Worktree<'repo> {
@@ -113,9 +113,45 @@ pub mod excludes {
113113
///
114114
/// * `$XDG_CONFIG_HOME/…/ignore` if `core.excludesFile` is *not* set, otherwise use the configured file.
115115
/// * `$GIT_DIR/info/exclude` if present.
116+
///
117+
/// When only excludes are desired, this is the most efficient way to obtain them. Otherwise use
118+
/// [`Worktree::attributes()`][crate::Worktree::attributes()] for accessing both attributes and excludes.
116119
pub fn excludes(&self, overrides: Option<gix_ignore::Search>) -> Result<gix_worktree::Cache, Error> {
117120
let index = self.index()?;
118121
Ok(self.parent.excludes(&index, overrides)?)
119122
}
120123
}
121124
}
125+
126+
///
127+
pub mod attributes {
128+
/// The error returned by [`Worktree::attributes()`][crate::Worktree::attributes()].
129+
#[derive(Debug, thiserror::Error)]
130+
#[allow(missing_docs)]
131+
pub enum Error {
132+
#[error(transparent)]
133+
OpenIndex(#[from] crate::worktree::open_index::Error),
134+
#[error(transparent)]
135+
CreateCache(#[from] crate::repository::attributes::Error),
136+
}
137+
138+
impl<'repo> crate::Worktree<'repo> {
139+
/// Configure a file-system cache checking if files below the repository are excluded or for querying their attributes.
140+
///
141+
/// Use `attribute_source` to specify where to read attributes from. Also note that exclude information will
142+
/// always try to read `.gitignore` files from disk before trying to read it from the `index`.
143+
///
144+
/// This takes into consideration all the usual repository configuration, namely:
145+
///
146+
/// * `$XDG_CONFIG_HOME/…/ignore|attributes` if `core.excludesFile|attributesFile` is *not* set, otherwise use the configured file.
147+
/// * `$GIT_DIR/info/exclude|attributes` if present.
148+
pub fn attributes(
149+
&self,
150+
source: gix_worktree::cache::state::attributes::Source,
151+
overrides: Option<gix_ignore::Search>,
152+
) -> Result<gix_worktree::Cache, Error> {
153+
let index = self.index()?;
154+
Ok(self.parent.attributes(&index, source, overrides)?)
155+
}
156+
}
157+
}

0 commit comments

Comments
 (0)