Skip to content

Commit

Permalink
Basic prefix support as well the first working version of `exclude qu…
Browse files Browse the repository at this point in the history
…ery` (#301)

Details are still to be fixed, and reading from stdin should be
implemented one day.

Issue right now is that the source path is normalized for some reason,
let's see where that happens.
  • Loading branch information
Byron committed Apr 29, 2022
1 parent cb1c80f commit 9cb8385
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 15 deletions.
14 changes: 4 additions & 10 deletions git-path/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,12 @@
//! ever get into a code-path which does panic though.

/// A dummy type to represent path specs and help finding all spots that take path specs once it is implemented.
#[derive(Clone, Debug)]
pub struct Spec(String);

impl FromStr for Spec {
type Err = std::convert::Infallible;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Spec(s.to_owned()))
}
}
/// A preliminary version of a path-spec based on glances of the code.
#[derive(Clone, Debug)]
pub struct Spec(bstr::BString);

mod convert;
mod spec;

pub use convert::*;
use std::str::FromStr;
49 changes: 49 additions & 0 deletions git-path/src/spec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use crate::Spec;
use bstr::{BStr, ByteSlice, ByteVec};
use std::ffi::OsStr;
use std::str::FromStr;

impl std::convert::TryFrom<&OsStr> for Spec {
type Error = crate::Utf8Error;

fn try_from(value: &OsStr) -> Result<Self, Self::Error> {
crate::os_str_into_bstr(value).map(|value| Spec(value.into()))
}
}

impl FromStr for Spec {
type Err = std::convert::Infallible;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Spec(s.into()))
}
}

impl Spec {
/// Return all paths described by this path spec, using slashes on all platforms.
pub fn items(&self) -> impl Iterator<Item = &BStr> {
std::iter::once(self.0.as_bstr())
}
/// Adjust this path specification according to the given `prefix`, which may be empty to indicate we are the at work-tree root.
// TODO: this is a hack, needs test and time to do according to spec. This is just a minimum version to have -something-.
pub fn apply_prefix(&mut self, prefix: &std::path::Path) -> &Self {
assert!(!self.0.contains_str(b"/../"));
assert!(!self.0.contains_str(b"/./"));
assert!(!self.0.starts_with_str(b"../"));
assert!(!self.0.starts_with_str(b"./"));
assert!(!self.0.starts_with_str(b"/"));
// many more things we can't handle. `Path` never ends with trailing path separator.

let prefix = crate::into_bstr(prefix);
if !prefix.is_empty() {
let mut prefix = crate::to_unix_separators_on_windows(prefix);
{
let path = prefix.to_mut();
path.push_byte(b'/');
path.extend_from_slice(&self.0);
}
self.0 = prefix.into_owned();
}
self
}
}
25 changes: 25 additions & 0 deletions git-repository/src/repository/location.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,31 @@ impl crate::Repository {
crate::path::install_dir()
}

/// Returns the relative path which is the components between the working tree and the current working dir (CWD).
/// Note that there may be `None` if there is no work tree, even though the `PathBuf` will be empty
/// if the CWD is at the root of the work tree.
// TODO: tests, details - there is a lot about environment variables to change things around.
pub fn prefix(&self) -> Option<std::io::Result<std::path::PathBuf>> {
self.work_tree.as_ref().map(|root| {
root.canonicalize().and_then(|root| {
std::env::current_dir().and_then(|cwd| {
cwd.strip_prefix(&root)
.map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"CWD '{}' isn't within the work tree '{}'",
cwd.display(),
root.display()
),
)
})
.map(ToOwned::to_owned)
})
})
})
}

/// Return the kind of repository, either bare or one with a work tree.
pub fn kind(&self) -> crate::Kind {
match self.work_tree {
Expand Down
1 change: 1 addition & 0 deletions git-worktree/src/fs/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ impl<'paths> Cache<'paths> {
}
}

#[must_use]
pub struct Platform<'a, 'paths> {
parent: &'a Cache<'paths>,
is_dir: Option<bool>,
Expand Down
33 changes: 29 additions & 4 deletions gitoxide-core/src/repository/exclude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::io;

use crate::OutputFormat;
use git_repository as git;
use git_repository::prelude::FindExt;

pub mod query {
use crate::OutputFormat;
Expand All @@ -18,11 +19,11 @@ pub mod query {

pub fn query(
repo: git::Repository,
_out: impl io::Write,
mut out: impl io::Write,
query::Options {
overrides,
format,
pathspecs: _,
pathspecs,
}: query::Options,
) -> anyhow::Result<()> {
if format != OutputFormat::Human {
Expand All @@ -34,10 +35,34 @@ pub fn query(
.current()
.with_context(|| "Cannot check excludes without a current worktree")?;
let index = worktree.open_index()?;
worktree.excludes(
let mut cache = worktree.excludes(
&index.state,
Some(git::attrs::MatchGroup::<git::attrs::Ignore>::from_overrides(overrides)),
)?;

todo!("impl");
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| (!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(())
}
2 changes: 1 addition & 1 deletion src/plumbing/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ pub fn main() -> Result<()> {
},
Subcommands::Repository(repo::Platform { repository, cmd }) => {
use git_repository as git;
let repository = git::ThreadSafeRepository::open(repository)?;
let repository = git::ThreadSafeRepository::discover(repository)?;
match cmd {
repo::Subcommands::Commit { cmd } => match cmd {
repo::commit::Subcommands::Describe {
Expand Down
1 change: 1 addition & 0 deletions src/plumbing/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ pub mod repo {
#[clap(long, short = 'p')]
patterns: Vec<OsString>,
/// The git path specifications to check for exclusion.
#[clap(parse(try_from_os_str = std::convert::TryFrom::try_from))]
pathspecs: Vec<git::path::Spec>,
},
}
Expand Down

0 comments on commit 9cb8385

Please sign in to comment.