Skip to content

integrate gix-status #1291

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Feb 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion gitoxide-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ serde = ["gix/serde", "dep:serde_json", "dep:serde", "bytesize/serde"]

[dependencies]
# deselect everything else (like "performance") as this should be controllable by the parent application.
gix = { version = "^0.59.0", path = "../gix", default-features = false, features = ["blob-diff", "revision", "mailmap", "excludes", "attributes", "worktree-mutation", "credentials", "interrupt", "status"] }
gix = { version = "^0.59.0", path = "../gix", default-features = false, features = ["blob-diff", "revision", "mailmap", "excludes", "attributes", "worktree-mutation", "credentials", "interrupt", "status", "dirwalk"] }
gix-pack-for-configuration-only = { package = "gix-pack", version = "^0.48.0", path = "../gix-pack", default-features = false, features = ["pack-cache-lru-dynamic", "pack-cache-lru-static", "generate", "streaming-input"] }
gix-transport-configuration-only = { package = "gix-transport", version = "^0.41.0", path = "../gix-transport", default-features = false }
gix-archive-for-configuration-only = { package = "gix-archive", version = "^0.9.0", path = "../gix-archive", optional = true, features = ["tar", "tar_gz"] }
Expand Down
1 change: 1 addition & 0 deletions gitoxide-core/src/query/engine/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ impl query::Engine {
let relpath = self
.repo
.pathspec(
true,
Some(spec.to_bstring()),
false,
&gix::index::State::new(self.repo.object_hash()),
Expand Down
30 changes: 16 additions & 14 deletions gitoxide-core/src/repository/attributes/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub struct Options {
pub(crate) mod function {
use std::{borrow::Cow, io, path::Path};

use anyhow::{anyhow, bail};
use anyhow::bail;
use gix::bstr::BStr;

use crate::{
Expand Down Expand Up @@ -52,43 +52,45 @@ pub(crate) mod function {
}
PathsOrPatterns::Patterns(patterns) => {
let mut pathspec = repo.pathspec(
true,
patterns.iter(),
true,
&index,
gix::worktree::stack::state::attributes::Source::WorktreeThenIdMapping
.adjust_for_bare(repo.is_bare()),
)?;
let mut pathspec_matched_entry = false;
for (path, _entry) in pathspec
.index_entries_with_paths(&index)
.ok_or_else(|| anyhow!("Pathspec didn't match a single path in the index"))?
{
pathspec_matched_entry = true;
let entry = cache.at_entry(path, Some(false))?;
if !entry.matching_attributes(&mut matches) {
continue;
if let Some(it) = pathspec.index_entries_with_paths(&index) {
for (path, _entry) in it {
pathspec_matched_entry = true;
let entry = cache.at_entry(path, Some(false))?;
if !entry.matching_attributes(&mut matches) {
continue;
}
print_match(&matches, path, &mut out)?;
}
print_match(&matches, path, &mut out)?;
}

if !pathspec_matched_entry {
// TODO(borrowchk): this shouldn't be necessary at all, but `pathspec` stays borrowed mutably for some reason.
// It's probably due to the strange lifetimes of `index_entries_with_paths()`.
let pathspec = repo.pathspec(
true,
patterns.iter(),
true,
&index,
gix::worktree::stack::state::attributes::Source::WorktreeThenIdMapping
.adjust_for_bare(repo.is_bare()),
)?;
let workdir = repo.work_dir();
for pattern in pathspec.search().patterns() {
let path = pattern.path();
let entry = cache.at_entry(
path,
pattern
.signature
.contains(gix::pathspec::MagicSignature::MUST_BE_DIR)
.into(),
Some(
workdir.map_or(false, |wd| wd.join(gix::path::from_bstr(path)).is_dir())
|| pattern.signature.contains(gix::pathspec::MagicSignature::MUST_BE_DIR),
),
)?;
if !entry.matching_attributes(&mut matches) {
continue;
Expand Down
48 changes: 34 additions & 14 deletions gitoxide-core/src/repository/clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ pub(crate) mod function {
use gix::dir::entry::{Kind, Status};
use gix::dir::walk::EmissionMode::CollapseDirectory;
use gix::dir::walk::ForDeletionMode::*;
use gix::dir::{walk, EntryRef};
use std::borrow::Cow;
use std::path::Path;

pub fn clean(
repo: gix::Repository,
Expand All @@ -37,7 +39,7 @@ pub(crate) mod function {
Options {
debug,
format,
execute,
mut execute,
ignored,
precious,
directories,
Expand All @@ -53,9 +55,9 @@ pub(crate) mod function {
bail!("Need a worktree to clean, this is a bare repository");
};

let index = repo.index()?;
let index = repo.index_or_empty()?;
let has_patterns = !patterns.is_empty();
let mut collect = gix::dir::walk::delegate::Collect::default();
let mut collect = InterruptableCollect::default();
let collapse_directories = CollapseDirectory;
let options = repo
.dirwalk_options()?
Expand All @@ -72,16 +74,12 @@ pub(crate) mod function {
.classify_untracked_bare_repositories(matches!(find_untracked_repositories, FindRepository::All))
.emit_untracked(collapse_directories)
.emit_ignored(Some(collapse_directories))
.empty_patterns_match_prefix(true)
.emit_empty_directories(true);
repo.dirwalk(&index, patterns, options, &mut collect)?;
let prefix = repo.prefix()?.expect("worktree and valid current dir");
let prefix_len = if prefix.as_os_str().is_empty() {
0
} else {
prefix.to_str().map_or(0, |s| s.len() + 1 /* slash */)
};
let prefix = repo.prefix()?.unwrap_or(Path::new(""));

let entries = collect.into_entries_by_path();
let entries = collect.inner.into_entries_by_path();
let mut entries_to_clean = 0;
let mut skipped_directories = 0;
let mut skipped_ignored = 0;
Expand Down Expand Up @@ -143,7 +141,7 @@ pub(crate) mod function {
&& gix::discover::is_git(&workdir.join(gix::path::from_bstr(entry.rela_path.as_bstr()))).is_ok()
{
if debug {
writeln!(err, "DBG: upgraded directory '{}' to repository", entry.rela_path).ok();
writeln!(err, "DBG: upgraded directory '{}' to bare repository", entry.rela_path).ok();
}
disk_kind = gix::dir::entry::Kind::Repository;
}
Expand Down Expand Up @@ -171,15 +169,16 @@ pub(crate) mod function {
};

let is_ignored = matches!(entry.status, gix::dir::entry::Status::Ignored(_));
let display_path = entry.rela_path[prefix_len..].as_bstr();
let entry_path = gix::path::from_bstr(entry.rela_path);
let display_path = gix::path::relativize_with_prefix(&entry_path, prefix);
if disk_kind == gix::dir::entry::Kind::Directory {
saw_ignored_directory |= is_ignored;
saw_untracked_directory |= entry.status == gix::dir::entry::Status::Untracked;
}
writeln!(
out,
"{maybe}{suffix} {}{} {status}",
display_path,
display_path.display(),
disk_kind.is_dir().then_some("/").unwrap_or_default(),
status = match entry.status {
Status::Ignored(kind) => {
Expand Down Expand Up @@ -215,8 +214,11 @@ pub(crate) mod function {
},
)?;

if gix::interrupt::is_triggered() {
execute = false;
}
if execute {
let path = workdir.join(gix::path::from_bstr(entry.rela_path));
let path = workdir.join(entry_path);
if disk_kind.is_dir() {
std::fs::remove_dir_all(path)?;
} else {
Expand Down Expand Up @@ -286,7 +288,25 @@ pub(crate) mod function {
} else {
writeln!(err, "Nothing to clean{}", wrap_in_parens(make_msg()))?;
}
if gix::interrupt::is_triggered() {
writeln!(err, "Result may be incomplete as it was interrupted")?;
}
}
Ok(())
}

#[derive(Default)]
struct InterruptableCollect {
inner: gix::dir::walk::delegate::Collect,
}

impl gix::dir::walk::Delegate for InterruptableCollect {
fn emit(&mut self, entry: EntryRef<'_>, collapsed_directory_status: Option<Status>) -> walk::Action {
let res = self.inner.emit(entry, collapsed_directory_status);
if gix::interrupt::is_triggered() {
return walk::Action::Cancel;
}
res
}
}
}
32 changes: 17 additions & 15 deletions gitoxide-core/src/repository/exclude.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{borrow::Cow, io};

use anyhow::{anyhow, bail};
use anyhow::bail;
use gix::bstr::BStr;

use crate::{repository::PathsOrPatterns, OutputFormat};
Expand Down Expand Up @@ -58,42 +58,44 @@ pub fn query(
PathsOrPatterns::Patterns(patterns) => {
let mut pathspec_matched_something = false;
let mut pathspec = repo.pathspec(
true,
patterns.iter(),
repo.work_dir().is_some(),
&index,
gix::worktree::stack::state::attributes::Source::WorktreeThenIdMapping.adjust_for_bare(repo.is_bare()),
)?;

for (path, _entry) in pathspec
.index_entries_with_paths(&index)
.ok_or_else(|| anyhow!("Pathspec didn't yield any entry"))?
{
pathspec_matched_something = true;
let entry = cache.at_entry(path, Some(false))?;
let match_ = entry
.matching_exclude_pattern()
.and_then(|m| (show_ignore_patterns || !m.pattern.is_negative()).then_some(m));
print_match(match_, path, &mut out)?;
if let Some(it) = pathspec.index_entries_with_paths(&index) {
for (path, _entry) in it {
pathspec_matched_something = true;
let entry = cache.at_entry(path, Some(false))?;
let match_ = entry
.matching_exclude_pattern()
.and_then(|m| (show_ignore_patterns || !m.pattern.is_negative()).then_some(m));
print_match(match_, path, &mut out)?;
}
}

if !pathspec_matched_something {
// TODO(borrowchk): this shouldn't be necessary at all, but `pathspec` stays borrowed mutably for some reason.
// It's probably due to the strange lifetimes of `index_entries_with_paths()`.
let pathspec = repo.pathspec(
true,
patterns.iter(),
repo.work_dir().is_some(),
&index,
gix::worktree::stack::state::attributes::Source::WorktreeThenIdMapping
.adjust_for_bare(repo.is_bare()),
)?;
let workdir = repo.work_dir();
for pattern in pathspec.search().patterns() {
let path = pattern.path();
let entry = cache.at_entry(
path,
pattern
.signature
.contains(gix::pathspec::MagicSignature::MUST_BE_DIR)
.into(),
Some(
workdir.map_or(false, |wd| wd.join(gix::path::from_bstr(path)).is_dir())
|| pattern.signature.contains(gix::pathspec::MagicSignature::MUST_BE_DIR),
),
)?;
let match_ = entry
.matching_exclude_pattern()
Expand Down
1 change: 1 addition & 0 deletions gitoxide-core/src/repository/index/entries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ pub(crate) mod function {
)> {
let index = repo.index_or_load_from_head()?;
let pathspec = repo.pathspec(
true,
pathspecs,
false,
&index,
Expand Down
Loading