Skip to content

Commit

Permalink
feat: pathspec_search([specs]) to instantiate a search using pathsp…
Browse files Browse the repository at this point in the history
…ecs.

It can be used to for filtering input paths.
This type also makes filtering index entries easy.
  • Loading branch information
Byron committed Aug 17, 2023
1 parent 430e58c commit a7d0e44
Show file tree
Hide file tree
Showing 19 changed files with 419 additions and 86 deletions.
77 changes: 55 additions & 22 deletions gix/src/config/cache/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::{borrow::Cow, path::PathBuf, time::Duration};
use gix_attributes::Source;
use gix_lock::acquire::Fail;

use crate::config::boolean;
use crate::{
bstr::BStr,
config,
Expand Down Expand Up @@ -144,6 +145,15 @@ impl Cache {
res.transpose().with_leniency(self.lenient_config)
}

pub(crate) fn fs_capabilities(&self) -> Result<gix_fs::Capabilities, boolean::Error> {
Ok(gix_fs::Capabilities {
precompose_unicode: boolean(self, "core.precomposeUnicode", &Core::PRECOMPOSE_UNICODE, false)?,
ignore_case: boolean(self, "core.ignoreCase", &Core::IGNORE_CASE, false)?,
executable_bit: boolean(self, "core.fileMode", &Core::FILE_MODE, true)?,
symlink: boolean(self, "core.symlinks", &Core::SYMLINKS, true)?,
})
}

/// Collect everything needed to checkout files into a worktree.
/// Note that some of the options being returned will be defaulted so safe settings, the caller might have to override them
/// depending on the use-case.
Expand All @@ -152,34 +162,13 @@ impl Cache {
repo: &Repository,
attributes_source: gix_worktree::cache::state::attributes::Source,
) -> Result<gix_worktree::checkout::Options, checkout_options::Error> {
fn boolean(
me: &Cache,
full_key: &str,
key: &'static config::tree::keys::Boolean,
default: bool,
) -> Result<bool, checkout_options::Error> {
debug_assert_eq!(
full_key,
key.logical_name(),
"BUG: key name and hardcoded name must match"
);
Ok(me
.apply_leniency(me.resolved.boolean_by_key(full_key).map(|v| key.enrich_error(v)))?
.unwrap_or(default))
}

let git_dir = repo.git_dir();
let thread_limit = self.apply_leniency(
self.resolved
.integer_filter_by_key("checkout.workers", &mut self.filter_config_section.clone())
.map(|value| Checkout::WORKERS.try_from_workers(value)),
)?;
let capabilities = gix_fs::Capabilities {
precompose_unicode: boolean(self, "core.precomposeUnicode", &Core::PRECOMPOSE_UNICODE, false)?,
ignore_case: boolean(self, "core.ignoreCase", &Core::IGNORE_CASE, false)?,
executable_bit: boolean(self, "core.fileMode", &Core::FILE_MODE, true)?,
symlink: boolean(self, "core.symlinks", &Core::SYMLINKS, true)?,
};
let capabilities = self.fs_capabilities()?;
let filters = {
let collection = Default::default();
let mut filters = gix_filter::Pipeline::new(&collection, filter::Pipeline::options(repo)?);
Expand Down Expand Up @@ -285,6 +274,34 @@ impl Cache {
Ok((state, buf))
}

/// // TODO: let caller control if base-case-sensitivity should be inherited from the file-system if it isn't overridden in globals.
pub(crate) fn pathspec_defaults(
&self,
) -> Result<gix_pathspec::Defaults, gix_pathspec::defaults::from_environment::Error> {
let res = gix_pathspec::Defaults::from_environment(|name| {
let key = [
&gitoxide::Pathspec::ICASE,
&gitoxide::Pathspec::GLOB,
&gitoxide::Pathspec::NOGLOB,
&gitoxide::Pathspec::LITERAL,
]
.iter()
.find_map(|key| (key.environment_override().expect("set") == name).then_some(key))
.expect("we must know all possible input variable names");

let val = self
.resolved
.string("gitoxide", Some("pathspec".into()), key.name())
.map(gix_path::from_bstr)?;
Some(val.into_owned().into())
});
if res.is_err() && self.lenient_config {
Ok(gix_pathspec::Defaults::default())
} else {
res
}
}

pub(crate) fn xdg_config_path(
&self,
resource_file_name: &str,
Expand Down Expand Up @@ -317,3 +334,19 @@ impl Cache {
gix_path::env::home_dir().and_then(|path| self.environment.home.check_opt(path))
}
}

fn boolean(
me: &Cache,
full_key: &str,
key: &'static config::tree::keys::Boolean,
default: bool,
) -> Result<bool, boolean::Error> {
debug_assert_eq!(
full_key,
key.logical_name(),
"BUG: key name and hardcoded name must match"
);
Ok(me
.apply_leniency(me.resolved.boolean_by_key(full_key).map(|v| key.enrich_error(v)))?
.unwrap_or(default))
}
23 changes: 23 additions & 0 deletions gix/src/config/cache/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,29 @@ fn apply_environment_overrides(
(env(key), key.name)
}],
),
(
"gitoxide",
Some(Cow::Borrowed("pathspec".into())),
git_prefix,
&[
{
let key = &gitoxide::Pathspec::LITERAL;
(env(key), key.name)
},
{
let key = &gitoxide::Pathspec::GLOB;
(env(key), key.name)
},
{
let key = &gitoxide::Pathspec::NOGLOB;
(env(key), key.name)
},
{
let key = &gitoxide::Pathspec::ICASE;
(env(key), key.name)
},
],
),
(
"ssh",
None,
Expand Down
42 changes: 41 additions & 1 deletion gix/src/config/tree/sections/gitoxide.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ impl Gitoxide {
pub const SSH: Ssh = Ssh;
/// The `gitoxide.user` section.
pub const USER: User = User;
/// The `gitoxide.pathspec` section.
pub const PATHSPEC: Pathspec = Pathspec;

/// The `gitoxide.userAgent` Key.
pub const USER_AGENT: keys::Any = keys::Any::new("userAgent", &config::Tree::GITOXIDE).with_note(
Expand Down Expand Up @@ -52,6 +54,7 @@ impl Section for Gitoxide {
&Self::OBJECTS,
&Self::SSH,
&Self::USER,
&Self::PATHSPEC,
]
}
}
Expand Down Expand Up @@ -313,6 +316,43 @@ mod subsections {
}
}

/// The `pathspec` sub-section.
#[derive(Copy, Clone, Default)]
pub struct Pathspec;

impl Pathspec {
/// The `gitoxide.pathspec.glob` key.
pub const GLOB: keys::Boolean = keys::Boolean::new_boolean("glob", &Gitoxide::PATHSPEC)
.with_environment_override("GIT_GLOB_PATHSPECS")
.with_note("pathspec wildcards don't match the slash character, then needing '**' to get past them");
/// The `gitoxide.pathspec.noglob` key.
pub const NOGLOB: keys::Boolean = keys::Boolean::new_boolean("noglob", &Gitoxide::PATHSPEC)
.with_environment_override("GIT_NOGLOB_PATHSPECS")
.with_note("Enable literal matching for glob patterns, effectively disabling globbing");
/// The `gitoxide.pathspec.literal` key.
pub const LITERAL: keys::Boolean = keys::Boolean::new_boolean("literal", &Gitoxide::PATHSPEC)
.with_environment_override("GIT_LITERAL_PATHSPECS")
.with_note("Make the entire spec used verbatim, the only way to get ':()name' verbatim for instance");
/// The `gitoxide.pathspec.icase` key.
pub const ICASE: keys::Boolean = keys::Boolean::new_boolean("icase", &Gitoxide::PATHSPEC)
.with_environment_override("GIT_ICASE_PATHSPECS")
.with_note("Compare string in a case-insensitive manner");
}

impl Section for Pathspec {
fn name(&self) -> &str {
"pathspec"
}

fn keys(&self) -> &[&dyn Key] {
&[&Self::GLOB, &Self::NOGLOB, &Self::LITERAL, &Self::ICASE]
}

fn parent(&self) -> Option<&dyn Section> {
Some(&Tree::GITOXIDE)
}
}

/// The `objects` sub-section.
#[derive(Copy, Clone, Default)]
pub struct Objects;
Expand Down Expand Up @@ -398,7 +438,7 @@ mod subsections {
}
}
}
pub use subsections::{Allow, Author, Commit, Committer, Core, Http, Https, Objects, Ssh, User};
pub use subsections::{Allow, Author, Commit, Committer, Core, Http, Https, Objects, Pathspec, Ssh, User};

pub mod validate {
use std::error::Error;
Expand Down
6 changes: 3 additions & 3 deletions gix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ pub use gix_negotiate as negotiate;
pub use gix_object as objs;
pub use gix_object::bstr;
pub use gix_odb as odb;
pub use gix_pathspec as pathspec;
pub use gix_prompt as prompt;
#[cfg(feature = "gix-protocol")]
pub use gix_protocol as protocol;
Expand Down Expand Up @@ -131,10 +130,10 @@ pub type OdbHandle = gix_odb::Handle;
/// A way to access git configuration
pub(crate) type Config = OwnShared<gix_config::File<'static>>;

///
mod types;
pub use types::{
Commit, Head, Id, Object, ObjectDetached, Reference, Remote, Repository, Tag, ThreadSafeRepository, Tree, Worktree,
Commit, Head, Id, Object, ObjectDetached, Pathspec, Reference, Remote, Repository, Tag, ThreadSafeRepository, Tree,
Worktree,
};

///
Expand All @@ -143,6 +142,7 @@ pub mod commit;
pub mod head;
pub mod id;
pub mod object;
pub mod pathspec;
pub mod reference;
pub mod repository;
pub mod tag;
Expand Down
11 changes: 9 additions & 2 deletions gix/src/open/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,13 @@ impl ThreadSafeRepository {
)?;

if bail_if_untrusted && git_dir_trust != gix_sec::Trust::Full {
check_safe_directories(&git_dir, git_install_dir.as_deref(), home.as_deref(), &config)?;
check_safe_directories(
&git_dir,
git_install_dir.as_deref(),
current_dir,
home.as_deref(),
&config,
)?;
}

// core.worktree might be used to overwrite the worktree directory
Expand Down Expand Up @@ -321,11 +327,12 @@ fn replacement_objects_refs_prefix(
fn check_safe_directories(
git_dir: &std::path::Path,
git_install_dir: Option<&std::path::Path>,
current_dir: &std::path::Path,
home: Option<&std::path::Path>,
config: &config::Cache,
) -> Result<(), Error> {
let mut is_safe = false;
let git_dir = match gix_path::realpath(git_dir) {
let git_dir = match gix_path::realpath_opts(git_dir, current_dir, gix_path::realpath::MAX_SYMLINKS) {
Ok(p) => p,
Err(_) => git_dir.to_owned(),
};
Expand Down
Loading

0 comments on commit a7d0e44

Please sign in to comment.