History

feat: `gix attribute query` as something similar to `git check-attrs`.

Sebastian Thiel committed 3 years ago

Commit bfdcb141 parent 450212e4 files · +116 −0Browse files

Unified · Split

4 files
  1. gitoxide-core/src/repository/attributes.rs +66 −0
  2. gitoxide-core/src/repository/mod.rs +1 −0
  3. src/plumbing/main.rs +29 −0
  4. src/plumbing/options/mod.rs +20 −0
gitoxide-core/src/repository/attributes.rs+66−0
@@ -0,0 +1,66 @@
1use std::io;
2
3use anyhow::bail;
4use gix::prelude::FindExt;
5
6use crate::OutputFormat;
7
8pub mod query {
9 use crate::OutputFormat;
10
11 pub struct Options {
12 pub format: OutputFormat,
13 pub statistics: bool,
14 }
15}
16
17pub fn query(
18 repo: gix::Repository,
19 pathspecs: impl Iterator<Item = gix::path::Spec>,
20 mut out: impl io::Write,
21 mut err: impl io::Write,
22 query::Options { format, statistics }: query::Options,
23) -> anyhow::Result<()> {
24 if format != OutputFormat::Human {
25 bail!("JSON output isn't implemented yet");
26 }
27
28 let index = repo.index()?;
29 let mut cache = repo.attributes(
30 &index,
31 gix::worktree::cache::state::attributes::Source::WorktreeThenIdMapping,
32 gix::worktree::cache::state::ignore::Source::IdMapping,
33 None,
34 )?;
35
36 let prefix = repo.prefix().expect("worktree - we have an index by now")?;
37 let mut matches = cache.attribute_matches();
38
39 for mut spec in pathspecs {
40 for path in spec.apply_prefix(&prefix).items() {
41 let is_dir = gix::path::from_bstr(path).metadata().ok().map(|m| m.is_dir());
42 let entry = cache.at_entry(path, is_dir, |oid, buf| repo.objects.find_blob(oid, buf))?;
43
44 if !entry.matching_attributes(&mut matches) {
45 continue;
46 }
47 for m in matches.iter() {
48 writeln!(
49 out,
50 "{}:{}:{}\t{}\t{}",
51 m.location.source.map(|p| p.to_string_lossy()).unwrap_or_default(),
52 m.location.sequence_number,
53 m.pattern,
54 path,
55 m.assignment
56 )?;
57 }
58 }
59 }
60
61 if let Some(stats) = statistics.then(|| cache.take_statistics()) {
62 out.flush()?;
63 writeln!(err, "{:#?}", stats).ok();
64 }
65 Ok(())
66}
gitoxide-core/src/repository/mod.rs+1−0
@@ -15,6 +15,7 @@ pub mod commit; · 14 unmodified lines
15pub mod config;15pub mod config;
16mod credential;16mod credential;
17pub use credential::function as credential;17pub use credential::function as credential;
18pub mod attributes;
18#[cfg(feature = "blocking-client")]19#[cfg(feature = "blocking-client")]
19pub mod clone;20pub mod clone;
20pub mod exclude;21pub mod exclude;
src/plumbing/main.rs+29−0
@@ -13,6 +13,7 @@ use gitoxide_core as core; · 12 unmodified lines
13use gitoxide_core::pack::verify;13use gitoxide_core::pack::verify;
14use gix::bstr::io::BufReadExt;14use gix::bstr::io::BufReadExt;
15 15
16use crate::plumbing::options::attributes;
16use crate::{17use crate::{
17 plumbing::{18 plumbing::{
18 options::{commit, config, credential, exclude, free, index, mailmap, odb, revision, tree, Args, Subcommands},19 options::{commit, config, credential, exclude, free, index, mailmap, odb, revision, tree, Args, Subcommands},
@@ -831,6 +832,34 @@ pub fn main() -> Result<()> { · 812 unmodified lines
831 },832 },
832 ),833 ),
833 },834 },
835 Subcommands::Attributes(cmd) => match cmd {
836 attributes::Subcommands::Query { statistics, 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 err,
858 core::repository::attributes::query::Options { format, statistics },
859 )
860 },
861 ),
862 },
834 Subcommands::Exclude(cmd) => match cmd {863 Subcommands::Exclude(cmd) => match cmd {
835 exclude::Subcommands::Query {864 exclude::Subcommands::Query {
836 patterns,865 patterns,
src/plumbing/options/mod.rs+20−0
@@ -103,6 +103,9 @@ pub enum Subcommands { · 102 unmodified lines
103 /// Interact with the remote hosts.103 /// Interact with the remote hosts.
104 #[cfg(any(feature = "gitoxide-core-async-client", feature = "gitoxide-core-blocking-client"))]104 #[cfg(any(feature = "gitoxide-core-async-client", feature = "gitoxide-core-blocking-client"))]
105 Remote(remote::Platform),105 Remote(remote::Platform),
106 /// Interact with the attribute files like .gitattributes.
107 #[clap(subcommand, visible_alias = "attrs")]
108 Attributes(attributes::Subcommands),
106 /// Interact with the exclude files like .gitignore.109 /// Interact with the exclude files like .gitignore.
107 #[clap(subcommand)]110 #[clap(subcommand)]
108 Exclude(exclude::Subcommands),111 Exclude(exclude::Subcommands),
@@ -440,6 +443,23 @@ pub mod revision { · 331 unmodified lines
440 }443 }
441}444}
442 445
446pub 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 /// Print various statistics to stderr
454 #[clap(long, short = 's')]
455 statistics: bool,
456 /// The git path specifications to list attributes for, or unset to read from stdin one per line.
457 #[clap(value_parser = AsPathSpec)]
458 pathspecs: Vec<gix::path::Spec>,
459 },
460 }
461}
462
443pub mod exclude {463pub mod exclude {
444 use std::ffi::OsString;464 use std::ffi::OsString;
445 465