Skip to content

Commit ca50b60

Browse files
committed
feat: gix attribute query as something similar to git check-attrs.
1 parent 450212e commit ca50b60

File tree

4 files changed

+97
-0
lines changed

4 files changed

+97
-0
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use std::io;
2+
3+
use anyhow::bail;
4+
use gix::prelude::FindExt;
5+
6+
use crate::OutputFormat;
7+
8+
pub fn query(
9+
repo: gix::Repository,
10+
pathspecs: impl Iterator<Item = gix::path::Spec>,
11+
mut out: impl io::Write,
12+
format: OutputFormat,
13+
) -> anyhow::Result<()> {
14+
if format != OutputFormat::Human {
15+
bail!("JSON output isn't implemented yet");
16+
}
17+
18+
let index = repo.index()?;
19+
let mut cache = repo.attributes(
20+
&index,
21+
gix::worktree::cache::state::attributes::Source::WorktreeThenIdMapping,
22+
gix::worktree::cache::state::ignore::Source::IdMapping,
23+
None,
24+
)?;
25+
26+
let prefix = repo.prefix().expect("worktree - we have an index by now")?;
27+
let mut matches = cache.attribute_matches();
28+
29+
for mut spec in pathspecs {
30+
for path in spec.apply_prefix(&prefix).items() {
31+
let is_dir = gix::path::from_bstr(path).metadata().ok().map(|m| m.is_dir());
32+
let entry = cache.at_entry(path, is_dir, |oid, buf| repo.objects.find_blob(oid, buf))?;
33+
34+
if !entry.matching_attributes(&mut matches) {
35+
continue;
36+
}
37+
for m in matches.iter() {
38+
writeln!(
39+
out,
40+
"{}:{}:{}\t{}\t{}",
41+
m.location.source.map(|p| p.to_string_lossy()).unwrap_or_default(),
42+
m.location.sequence_number,
43+
m.pattern,
44+
path,
45+
m.assignment
46+
)?;
47+
}
48+
}
49+
}
50+
Ok(())
51+
}

gitoxide-core/src/repository/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub mod commit;
1515
pub mod config;
1616
mod credential;
1717
pub use credential::function as credential;
18+
pub mod attributes;
1819
#[cfg(feature = "blocking-client")]
1920
pub mod clone;
2021
pub mod exclude;

src/plumbing/main.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use gitoxide_core as core;
1313
use gitoxide_core::pack::verify;
1414
use gix::bstr::io::BufReadExt;
1515

16+
use crate::plumbing::options::attributes;
1617
use crate::{
1718
plumbing::{
1819
options::{commit, config, credential, exclude, free, index, mailmap, odb, revision, tree, Args, Subcommands},
@@ -831,6 +832,33 @@ pub fn main() -> Result<()> {
831832
},
832833
),
833834
},
835+
Subcommands::Attributes(cmd) => match cmd {
836+
attributes::Subcommands::Query { pathspecs } => prepare_and_run(
837+
"attributes-query",
838+
verbose,
839+
progress,
840+
progress_keep_open,
841+
None,
842+
move |_progress, out, _err| {
843+
use gix::bstr::ByteSlice;
844+
core::repository::attributes::query(
845+
repository(Mode::Strict)?,
846+
if pathspecs.is_empty() {
847+
Box::new(
848+
stdin_or_bail()?
849+
.byte_lines()
850+
.filter_map(Result::ok)
851+
.filter_map(|line| gix::path::Spec::from_bytes(line.as_bstr())),
852+
) as Box<dyn Iterator<Item = gix::path::Spec>>
853+
} else {
854+
Box::new(pathspecs.into_iter())
855+
},
856+
out,
857+
format,
858+
)
859+
},
860+
),
861+
},
834862
Subcommands::Exclude(cmd) => match cmd {
835863
exclude::Subcommands::Query {
836864
patterns,

src/plumbing/options/mod.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ pub enum Subcommands {
103103
/// Interact with the remote hosts.
104104
#[cfg(any(feature = "gitoxide-core-async-client", feature = "gitoxide-core-blocking-client"))]
105105
Remote(remote::Platform),
106+
/// Interact with the attribute files like .gitattributes.
107+
#[clap(subcommand, visible_alias = "attrs")]
108+
Attributes(attributes::Subcommands),
106109
/// Interact with the exclude files like .gitignore.
107110
#[clap(subcommand)]
108111
Exclude(exclude::Subcommands),
@@ -440,6 +443,20 @@ pub mod revision {
440443
}
441444
}
442445

446+
pub mod attributes {
447+
use crate::shared::AsPathSpec;
448+
449+
#[derive(Debug, clap::Subcommand)]
450+
pub enum Subcommands {
451+
/// List all attributes of the given path-specs and display the result similar to `git check-attr`.
452+
Query {
453+
/// The git path specifications to list attributes for, or unset to read from stdin one per line.
454+
#[clap(value_parser = AsPathSpec)]
455+
pathspecs: Vec<gix::path::Spec>,
456+
},
457+
}
458+
}
459+
443460
pub mod exclude {
444461
use std::ffi::OsString;
445462

0 commit comments

Comments
 (0)