Skip to content

Commit

Permalink
adapt to changes in gix
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Aug 17, 2023
1 parent a7d0e44 commit ca972a2
Show file tree
Hide file tree
Showing 10 changed files with 184 additions and 106 deletions.
22 changes: 15 additions & 7 deletions gitoxide-core/src/query/engine/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,22 @@ impl query::Engine {
mut progress: impl gix::Progress,
) -> anyhow::Result<()> {
match cmd {
Command::TracePath { mut spec } => {
Command::TracePath { spec } => {
let is_excluded = spec.is_excluded();
let relpath = spec
.normalize(
self.repo.prefix()?.unwrap_or_default().as_ref(),
self.repo.work_dir().unwrap_or_else(|| self.repo.git_dir()),
// Just to get the normalized version of the path with everything auto-configured.
let relpath = self
.repo
.pathspec(
Some(spec.to_bstring()),
false,
&gix::index::State::new(self.repo.object_hash()),
)?
.path();
.search()
.patterns()
.next()
.expect("exactly one")
.path()
.to_owned();
if relpath.is_empty() || is_excluded {
bail!("Invalid pathspec {spec} - path must not be empty, not be excluded, and wildcards are taken literally")
}
Expand All @@ -36,7 +44,7 @@ impl query::Engine {
|r| r.get(0),
)
.optional()?
.context("Path not found anywhere in recorded history")?;
.with_context(|| format!("Path '{relpath}' not found anywhere in recorded history"))?;

let mut by_file_id = self
.con
Expand Down
1 change: 1 addition & 0 deletions gitoxide-core/src/query/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub enum Command {
}

pub(crate) mod update;

pub use update::update;

mod command;
80 changes: 53 additions & 27 deletions gitoxide-core/src/repository/attributes/query.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::OutputFormat;
use gix::repository::IndexPersistedOrInMemory;

pub struct Options {
pub format: OutputFormat,
Expand All @@ -8,17 +9,19 @@ pub struct Options {
pub(crate) mod function {
use std::{io, path::Path};

use anyhow::bail;
use anyhow::{anyhow, bail};
use gix::bstr::BStr;
use gix::prelude::FindExt;

use crate::repository::PathsOrPatterns;
use crate::{
repository::attributes::query::{attributes_cache, Options},
OutputFormat,
};

pub fn query(
repo: gix::Repository,
pathspecs: impl Iterator<Item = gix::pathspec::Pattern>,
input: PathsOrPatterns,
mut out: impl io::Write,
mut err: impl io::Write,
Options { format, statistics }: Options,
Expand All @@ -27,32 +30,33 @@ pub(crate) mod function {
bail!("JSON output isn't implemented yet");
}

let mut cache = attributes_cache(&repo)?;
let (mut cache, index) = attributes_cache(&repo)?;
let mut matches = cache.attribute_matches();
// TODO(pathspec): The search is just used as a shortcut to normalization, but one day should be used for an actual search.
let search = gix::pathspec::Search::from_specs(
pathspecs,
repo.prefix()?.as_deref(),
repo.work_dir().unwrap_or_else(|| repo.git_dir()),
)?;

for spec in search.into_patterns() {
let is_dir = gix::path::from_bstr(spec.path()).metadata().ok().map(|m| m.is_dir());
let entry = cache.at_entry(spec.path(), is_dir, |oid, buf| repo.objects.find_blob(oid, buf))?;
match input {
PathsOrPatterns::Paths(paths) => {
for path in paths {
let is_dir = gix::path::from_bstr(path.as_ref()).metadata().ok().map(|m| m.is_dir());

if !entry.matching_attributes(&mut matches) {
continue;
let entry = cache.at_entry(path.as_slice(), is_dir, |oid, buf| repo.objects.find_blob(oid, buf))?;
if !entry.matching_attributes(&mut matches) {
continue;
}
print_match(&matches, path.as_ref(), &mut out)?;
}
}
for m in matches.iter() {
writeln!(
out,
"{}:{}:{}\t{}\t{}",
m.location.source.map(Path::to_string_lossy).unwrap_or_default(),
m.location.sequence_number,
m.pattern,
spec.path(),
m.assignment
)?;
PathsOrPatterns::Patterns(patterns) => {
let mut pathspec = repo.pathspec(patterns, true, &index)?;
for (path, _entry) in pathspec
.index_entries_with_paths(&index)
.ok_or_else(|| anyhow!("Pathspec didn't match a single path in the index"))?
{
let entry = cache.at_entry(path, Some(false), |oid, buf| repo.objects.find_blob(oid, buf))?;
if !entry.matching_attributes(&mut matches) {
continue;
}
print_match(&matches, path, &mut out)?;
}
}
}

Expand All @@ -62,11 +66,32 @@ pub(crate) mod function {
}
Ok(())
}

fn print_match(
matches: &gix::attrs::search::Outcome,
path: &BStr,
mut out: impl std::io::Write,
) -> std::io::Result<()> {
for m in matches.iter() {
writeln!(
out,
"{}:{}:{}\t{}\t{}",
m.location.source.map(Path::to_string_lossy).unwrap_or_default(),
m.location.sequence_number,
m.pattern,
path,
m.assignment
)?;
}
Ok(())
}
}

pub(crate) fn attributes_cache(repo: &gix::Repository) -> anyhow::Result<gix::worktree::Cache> {
pub(crate) fn attributes_cache(
repo: &gix::Repository,
) -> anyhow::Result<(gix::worktree::Cache, IndexPersistedOrInMemory)> {
let index = repo.index_or_load_from_head()?;
Ok(repo.attributes(
let cache = repo.attributes(
&index,
if repo.is_bare() {
gix::worktree::cache::state::attributes::Source::IdMapping
Expand All @@ -75,5 +100,6 @@ pub(crate) fn attributes_cache(repo: &gix::Repository) -> anyhow::Result<gix::wo
},
gix::worktree::cache::state::ignore::Source::IdMapping,
None,
)?)
)?;
Ok((cache, index))
}
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ pub(crate) mod function {
}
});

let mut cache = attributes_cache(&repo)?;
let (mut cache, _index) = attributes_cache(&repo)?;
let mut matches = cache.attribute_matches();
let mut progress = progress.add_child("validate");
let mut mismatches = Vec::new();
Expand Down
71 changes: 45 additions & 26 deletions gitoxide-core/src/repository/exclude.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use std::io;

use anyhow::bail;
use anyhow::{anyhow, bail};
use gix::bstr::BStr;
use gix::prelude::FindExt;

use crate::repository::PathsOrPatterns;
use crate::OutputFormat;

pub mod query {
Expand All @@ -20,7 +22,7 @@ pub mod query {

pub fn query(
repo: gix::Repository,
pathspecs: impl Iterator<Item = gix::pathspec::Pattern>,
input: PathsOrPatterns,
mut out: impl io::Write,
mut err: impl io::Write,
query::Options {
Expand All @@ -41,30 +43,29 @@ pub fn query(
Default::default(),
)?;

// TODO(pathspec): actually use the search to find items. This looks like `gix` capabilities to put it all together.
let search = gix::pathspec::Search::from_specs(
pathspecs,
repo.prefix()?.as_deref(),
repo.work_dir().unwrap_or_else(|| repo.git_dir()),
)?;

for spec in search.into_patterns() {
let path = spec.path();
let is_dir = gix::path::from_bstr(path).metadata().ok().map(|m| m.is_dir());
let entry = cache.at_entry(path, is_dir, |oid, buf| repo.objects.find_blob(oid, buf))?;
let match_ = entry
.matching_exclude_pattern()
.and_then(|m| (show_ignore_patterns || !m.pattern.is_negative()).then_some(m));
match match_ {
Some(m) => writeln!(
out,
"{}:{}:{}\t{}",
m.source.map(std::path::Path::to_string_lossy).unwrap_or_default(),
m.sequence_number,
m.pattern,
path
)?,
None => writeln!(out, "::\t{path}")?,
match input {
PathsOrPatterns::Paths(paths) => {
for path in paths {
let is_dir = gix::path::from_bstr(path.as_ref()).metadata().ok().map(|m| m.is_dir());
let entry = cache.at_entry(path.as_slice(), is_dir, |oid, buf| repo.objects.find_blob(oid, buf))?;
let match_ = entry
.matching_exclude_pattern()
.and_then(|m| (show_ignore_patterns || !m.pattern.is_negative()).then_some(m));
print_match(match_, path.as_ref(), &mut out)?;
}
}
PathsOrPatterns::Patterns(patterns) => {
for (path, _entry) in repo
.pathspec(patterns.into_iter(), repo.work_dir().is_some(), &index)?
.index_entries_with_paths(&index)
.ok_or_else(|| anyhow!("Pathspec didn't yield any entry"))?
{
let entry = cache.at_entry(path, Some(false), |oid, buf| repo.objects.find_blob(oid, buf))?;
let match_ = entry
.matching_exclude_pattern()
.and_then(|m| (show_ignore_patterns || !m.pattern.is_negative()).then_some(m));
print_match(match_, path, &mut out)?;
}
}
}

Expand All @@ -74,3 +75,21 @@ pub fn query(
}
Ok(())
}

fn print_match(
m: Option<gix::ignore::search::Match<'_>>,
path: &BStr,
mut out: impl std::io::Write,
) -> std::io::Result<()> {
match m {
Some(m) => writeln!(
out,
"{}:{}:{}\t{}",
m.source.map(std::path::Path::to_string_lossy).unwrap_or_default(),
m.sequence_number,
m.pattern,
path
),
None => writeln!(out, "::\t{path}"),
}
}
15 changes: 7 additions & 8 deletions gitoxide-core/src/repository/index/entries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub enum Attributes {
}

pub(crate) mod function {
use gix::bstr::BString;
use std::collections::BTreeSet;
use std::{
borrow::Cow,
Expand All @@ -28,7 +29,7 @@ pub(crate) mod function {

pub fn entries(
repo: gix::Repository,
pathspecs: Vec<gix::pathspec::Pattern>,
pathspecs: Vec<BString>,
out: impl std::io::Write,
mut err: impl std::io::Write,
Options {
Expand All @@ -40,10 +41,12 @@ pub(crate) mod function {
) -> anyhow::Result<()> {
use crate::OutputFormat::*;
let index = repo.index_or_load_from_head()?;
let pathspec = repo.pathspec(pathspecs, false, &index)?;
let mut cache = attributes
.or_else(|| {
pathspecs
.iter()
pathspec
.search()
.patterns()
.any(|spec| !spec.attributes.is_empty())
.then_some(Attributes::Index)
})
Expand Down Expand Up @@ -85,11 +88,7 @@ pub(crate) mod function {
if let Json = format {
out.write_all(b"[\n")?;
}
let mut search = gix::pathspec::Search::from_specs(
pathspecs,
repo.prefix()?.as_deref(),
gix::path::realpath(repo.work_dir().unwrap_or_else(|| repo.git_dir()))?.as_ref(), // TODO(pathspec): this setup needs `gix`.
)?;
let (mut search, _cache) = pathspec.into_parts();
let mut all_attrs = statistics.then(BTreeSet::new);
if let Some(entries) = index.prefixed_entries(search.common_prefix()) {
stats.entries_after_prune = entries.len();
Expand Down
7 changes: 7 additions & 0 deletions gitoxide-core/src/repository/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use gix::bstr::BString;
use std::path::PathBuf;

use anyhow::{Context as AnyhowContext, Result};
Expand All @@ -11,6 +12,11 @@ pub fn init(directory: Option<PathBuf>) -> Result<gix::discover::repository::Pat
.with_context(|| "Repository initialization failed")
}

pub enum PathsOrPatterns {
Paths(Box<dyn std::iter::Iterator<Item = BString>>),
Patterns(Vec<BString>),
}

#[cfg(feature = "archive")]
pub mod archive;
pub mod commit;
Expand All @@ -27,6 +33,7 @@ pub mod fetch;
pub use clone::function::clone;
#[cfg(feature = "blocking-client")]
pub use fetch::function::fetch;

pub mod commitgraph;
pub mod index;
pub mod mailmap;
Expand Down
Loading

0 comments on commit ca972a2

Please sign in to comment.