Skip to content

Commit

Permalink
Merge branch 'gix-attribute-query'
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed May 9, 2023
2 parents 450212e + 450b232 commit 1d70213
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 8 deletions.
7 changes: 0 additions & 7 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,7 +0,0 @@
----

Ok for [Byron](https://github.com/Byron) review the PR on video?

- [ ] I give my permission to record review and upload on YouTube publicly

If I think the review will be helpful for the community, then I might record and publish a video.
66 changes: 66 additions & 0 deletions gitoxide-core/src/repository/attributes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::io;

use anyhow::bail;
use gix::prelude::FindExt;

use crate::OutputFormat;

pub mod query {
use crate::OutputFormat;

pub struct Options {
pub format: OutputFormat,
pub statistics: bool,
}
}

pub fn query(
repo: gix::Repository,
pathspecs: impl Iterator<Item = gix::path::Spec>,
mut out: impl io::Write,
mut err: impl io::Write,
query::Options { format, statistics }: query::Options,
) -> anyhow::Result<()> {
if format != OutputFormat::Human {
bail!("JSON output isn't implemented yet");
}

let index = repo.index()?;
let mut cache = repo.attributes(
&index,
gix::worktree::cache::state::attributes::Source::WorktreeThenIdMapping,
gix::worktree::cache::state::ignore::Source::IdMapping,
None,
)?;

let prefix = repo.prefix().expect("worktree - we have an index by now")?;
let mut matches = cache.attribute_matches();

for mut spec in pathspecs {
for path in spec.apply_prefix(&prefix).items() {
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))?;

if !entry.matching_attributes(&mut matches) {
continue;
}
for m in matches.iter() {
writeln!(
out,
"{}:{}:{}\t{}\t{}",
m.location.source.map(|p| p.to_string_lossy()).unwrap_or_default(),
m.location.sequence_number,
m.pattern,
path,
m.assignment
)?;
}
}
}

if let Some(stats) = statistics.then(|| cache.take_statistics()) {
out.flush()?;
writeln!(err, "{:#?}", stats).ok();
}
Ok(())
}
8 changes: 8 additions & 0 deletions gitoxide-core/src/repository/exclude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,20 @@ pub mod query {
pub format: OutputFormat,
pub overrides: Vec<OsString>,
pub show_ignore_patterns: bool,
pub statistics: bool,
}
}

pub fn query(
repo: gix::Repository,
pathspecs: impl Iterator<Item = gix::path::Spec>,
mut out: impl io::Write,
mut err: impl io::Write,
query::Options {
overrides,
format,
show_ignore_patterns,
statistics,
}: query::Options,
) -> anyhow::Result<()> {
if format != OutputFormat::Human {
Expand Down Expand Up @@ -62,5 +65,10 @@ pub fn query(
}
}
}

if let Some(stats) = statistics.then(|| cache.take_statistics()) {
out.flush()?;
writeln!(err, "{:#?}", stats).ok();
}
Ok(())
}
1 change: 1 addition & 0 deletions gitoxide-core/src/repository/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub mod commit;
pub mod config;
mod credential;
pub use credential::function as credential;
pub mod attributes;
#[cfg(feature = "blocking-client")]
pub mod clone;
pub mod exclude;
Expand Down
34 changes: 33 additions & 1 deletion src/plumbing/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use gitoxide_core as core;
use gitoxide_core::pack::verify;
use gix::bstr::io::BufReadExt;

use crate::plumbing::options::attributes;
use crate::{
plumbing::{
options::{commit, config, credential, exclude, free, index, mailmap, odb, revision, tree, Args, Subcommands},
Expand Down Expand Up @@ -831,8 +832,37 @@ pub fn main() -> Result<()> {
},
),
},
Subcommands::Attributes(cmd) => match cmd {
attributes::Subcommands::Query { statistics, pathspecs } => prepare_and_run(
"attributes-query",
verbose,
progress,
progress_keep_open,
None,
move |_progress, out, err| {
use gix::bstr::ByteSlice;
core::repository::attributes::query(
repository(Mode::Strict)?,
if pathspecs.is_empty() {
Box::new(
stdin_or_bail()?
.byte_lines()
.filter_map(Result::ok)
.filter_map(|line| gix::path::Spec::from_bytes(line.as_bstr())),
) as Box<dyn Iterator<Item = gix::path::Spec>>
} else {
Box::new(pathspecs.into_iter())
},
out,
err,
core::repository::attributes::query::Options { format, statistics },
)
},
),
},
Subcommands::Exclude(cmd) => match cmd {
exclude::Subcommands::Query {
statistics,
patterns,
pathspecs,
show_ignore_patterns,
Expand All @@ -842,7 +872,7 @@ pub fn main() -> Result<()> {
progress,
progress_keep_open,
None,
move |_progress, out, _err| {
move |_progress, out, err| {
use gix::bstr::ByteSlice;
core::repository::exclude::query(
repository(Mode::Strict)?,
Expand All @@ -857,10 +887,12 @@ pub fn main() -> Result<()> {
Box::new(pathspecs.into_iter())
},
out,
err,
core::repository::exclude::query::Options {
format,
show_ignore_patterns,
overrides: patterns,
statistics,
},
)
},
Expand Down
23 changes: 23 additions & 0 deletions src/plumbing/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ pub enum Subcommands {
/// Interact with the remote hosts.
#[cfg(any(feature = "gitoxide-core-async-client", feature = "gitoxide-core-blocking-client"))]
Remote(remote::Platform),
/// Interact with the attribute files like .gitattributes.
#[clap(subcommand, visible_alias = "attrs")]
Attributes(attributes::Subcommands),
/// Interact with the exclude files like .gitignore.
#[clap(subcommand)]
Exclude(exclude::Subcommands),
Expand Down Expand Up @@ -440,6 +443,23 @@ pub mod revision {
}
}

pub mod attributes {
use crate::shared::AsPathSpec;

#[derive(Debug, clap::Subcommand)]
pub enum Subcommands {
/// List all attributes of the given path-specs and display the result similar to `git check-attr`.
Query {
/// Print various statistics to stderr
#[clap(long, short = 's')]
statistics: bool,
/// The git path specifications to list attributes for, or unset to read from stdin one per line.
#[clap(value_parser = AsPathSpec)]
pathspecs: Vec<gix::path::Spec>,
},
}
}

pub mod exclude {
use std::ffi::OsString;

Expand All @@ -449,6 +469,9 @@ pub mod exclude {
pub enum Subcommands {
/// Check if path-specs are excluded and print the result similar to `git check-ignore`.
Query {
/// Print various statistics to stderr
#[clap(long, short = 's')]
statistics: bool,
/// Show actual ignore patterns instead of un-excluding an entry.
///
/// That way one can understand why an entry might not be excluded.
Expand Down

0 comments on commit 1d70213

Please sign in to comment.