use std::io; use anyhow::{bail, Context}; use git_repository as git; use git_repository::prelude::FindExt; use crate::OutputFormat; pub mod query { use std::ffi::OsString; use crate::OutputFormat; pub struct Options { pub format: OutputFormat, pub overrides: Vec, pub show_ignore_patterns: bool, } } pub fn query( repo: git::Repository, pathspecs: impl Iterator, mut out: impl io::Write, query::Options { overrides, format, show_ignore_patterns, }: query::Options, ) -> anyhow::Result<()> { if format != OutputFormat::Human { bail!("JSON output isn't implemented yet"); } let worktree = repo .worktree() .with_context(|| "Cannot check excludes without a current worktree")?; let index = worktree.index()?; let mut cache = worktree.excludes( &index.state, Some(git::attrs::MatchGroup::::from_overrides(overrides)), )?; let prefix = repo.prefix().expect("worktree - we have an index by now")?; for mut spec in pathspecs { for path in spec.apply_prefix(&prefix).items() { // TODO: what about paths that end in /? Pathspec might handle it, it's definitely something git considers // even if the directory doesn't exist. Seems to work as long as these are kept in the spec. let is_dir = git::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(|| m)); match match_ { Some(m) => writeln!( out, "{}:{}:{}\t{}", m.source.map(|p| p.to_string_lossy()).unwrap_or_default(), m.sequence_number, m.pattern, path )?, None => writeln!(out, "::\t{}", path)?, } } } Ok(()) }