Skip to content

Commit bb48c4c

Browse files
committed
Merge branch 'status'
2 parents e186199 + 417f99f commit bb48c4c

File tree

41 files changed

+1961
-760
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1961
-760
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gitoxide-core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ serde = ["gix/serde", "dep:serde_json", "dep:serde", "bytesize/serde"]
4747

4848
[dependencies]
4949
# deselect everything else (like "performance") as this should be controllable by the parent application.
50-
gix = { version = "^0.59.0", path = "../gix", default-features = false, features = ["blob-diff", "revision", "mailmap", "excludes", "attributes", "worktree-mutation", "credentials", "interrupt", "status"] }
50+
gix = { version = "^0.59.0", path = "../gix", default-features = false, features = ["blob-diff", "revision", "mailmap", "excludes", "attributes", "worktree-mutation", "credentials", "interrupt", "status", "dirwalk"] }
5151
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"] }
5252
gix-transport-configuration-only = { package = "gix-transport", version = "^0.41.0", path = "../gix-transport", default-features = false }
5353
gix-archive-for-configuration-only = { package = "gix-archive", version = "^0.9.0", path = "../gix-archive", optional = true, features = ["tar", "tar_gz"] }

gitoxide-core/src/query/engine/command.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ impl query::Engine {
2323
let relpath = self
2424
.repo
2525
.pathspec(
26+
true,
2627
Some(spec.to_bstring()),
2728
false,
2829
&gix::index::State::new(self.repo.object_hash()),

gitoxide-core/src/repository/attributes/query.rs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub struct Options {
1010
pub(crate) mod function {
1111
use std::{borrow::Cow, io, path::Path};
1212

13-
use anyhow::{anyhow, bail};
13+
use anyhow::bail;
1414
use gix::bstr::BStr;
1515

1616
use crate::{
@@ -52,43 +52,45 @@ pub(crate) mod function {
5252
}
5353
PathsOrPatterns::Patterns(patterns) => {
5454
let mut pathspec = repo.pathspec(
55+
true,
5556
patterns.iter(),
5657
true,
5758
&index,
5859
gix::worktree::stack::state::attributes::Source::WorktreeThenIdMapping
5960
.adjust_for_bare(repo.is_bare()),
6061
)?;
6162
let mut pathspec_matched_entry = false;
62-
for (path, _entry) in pathspec
63-
.index_entries_with_paths(&index)
64-
.ok_or_else(|| anyhow!("Pathspec didn't match a single path in the index"))?
65-
{
66-
pathspec_matched_entry = true;
67-
let entry = cache.at_entry(path, Some(false))?;
68-
if !entry.matching_attributes(&mut matches) {
69-
continue;
63+
if let Some(it) = pathspec.index_entries_with_paths(&index) {
64+
for (path, _entry) in it {
65+
pathspec_matched_entry = true;
66+
let entry = cache.at_entry(path, Some(false))?;
67+
if !entry.matching_attributes(&mut matches) {
68+
continue;
69+
}
70+
print_match(&matches, path, &mut out)?;
7071
}
71-
print_match(&matches, path, &mut out)?;
7272
}
7373

7474
if !pathspec_matched_entry {
7575
// TODO(borrowchk): this shouldn't be necessary at all, but `pathspec` stays borrowed mutably for some reason.
7676
// It's probably due to the strange lifetimes of `index_entries_with_paths()`.
7777
let pathspec = repo.pathspec(
78+
true,
7879
patterns.iter(),
7980
true,
8081
&index,
8182
gix::worktree::stack::state::attributes::Source::WorktreeThenIdMapping
8283
.adjust_for_bare(repo.is_bare()),
8384
)?;
85+
let workdir = repo.work_dir();
8486
for pattern in pathspec.search().patterns() {
8587
let path = pattern.path();
8688
let entry = cache.at_entry(
8789
path,
88-
pattern
89-
.signature
90-
.contains(gix::pathspec::MagicSignature::MUST_BE_DIR)
91-
.into(),
90+
Some(
91+
workdir.map_or(false, |wd| wd.join(gix::path::from_bstr(path)).is_dir())
92+
|| pattern.signature.contains(gix::pathspec::MagicSignature::MUST_BE_DIR),
93+
),
9294
)?;
9395
if !entry.matching_attributes(&mut matches) {
9496
continue;

gitoxide-core/src/repository/clean.rs

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ pub(crate) mod function {
2727
use gix::dir::entry::{Kind, Status};
2828
use gix::dir::walk::EmissionMode::CollapseDirectory;
2929
use gix::dir::walk::ForDeletionMode::*;
30+
use gix::dir::{walk, EntryRef};
3031
use std::borrow::Cow;
32+
use std::path::Path;
3133

3234
pub fn clean(
3335
repo: gix::Repository,
@@ -37,7 +39,7 @@ pub(crate) mod function {
3739
Options {
3840
debug,
3941
format,
40-
execute,
42+
mut execute,
4143
ignored,
4244
precious,
4345
directories,
@@ -53,9 +55,9 @@ pub(crate) mod function {
5355
bail!("Need a worktree to clean, this is a bare repository");
5456
};
5557

56-
let index = repo.index()?;
58+
let index = repo.index_or_empty()?;
5759
let has_patterns = !patterns.is_empty();
58-
let mut collect = gix::dir::walk::delegate::Collect::default();
60+
let mut collect = InterruptableCollect::default();
5961
let collapse_directories = CollapseDirectory;
6062
let options = repo
6163
.dirwalk_options()?
@@ -72,16 +74,12 @@ pub(crate) mod function {
7274
.classify_untracked_bare_repositories(matches!(find_untracked_repositories, FindRepository::All))
7375
.emit_untracked(collapse_directories)
7476
.emit_ignored(Some(collapse_directories))
77+
.empty_patterns_match_prefix(true)
7578
.emit_empty_directories(true);
7679
repo.dirwalk(&index, patterns, options, &mut collect)?;
77-
let prefix = repo.prefix()?.expect("worktree and valid current dir");
78-
let prefix_len = if prefix.as_os_str().is_empty() {
79-
0
80-
} else {
81-
prefix.to_str().map_or(0, |s| s.len() + 1 /* slash */)
82-
};
80+
let prefix = repo.prefix()?.unwrap_or(Path::new(""));
8381

84-
let entries = collect.into_entries_by_path();
82+
let entries = collect.inner.into_entries_by_path();
8583
let mut entries_to_clean = 0;
8684
let mut skipped_directories = 0;
8785
let mut skipped_ignored = 0;
@@ -143,7 +141,7 @@ pub(crate) mod function {
143141
&& gix::discover::is_git(&workdir.join(gix::path::from_bstr(entry.rela_path.as_bstr()))).is_ok()
144142
{
145143
if debug {
146-
writeln!(err, "DBG: upgraded directory '{}' to repository", entry.rela_path).ok();
144+
writeln!(err, "DBG: upgraded directory '{}' to bare repository", entry.rela_path).ok();
147145
}
148146
disk_kind = gix::dir::entry::Kind::Repository;
149147
}
@@ -171,15 +169,16 @@ pub(crate) mod function {
171169
};
172170

173171
let is_ignored = matches!(entry.status, gix::dir::entry::Status::Ignored(_));
174-
let display_path = entry.rela_path[prefix_len..].as_bstr();
172+
let entry_path = gix::path::from_bstr(entry.rela_path);
173+
let display_path = gix::path::relativize_with_prefix(&entry_path, prefix);
175174
if disk_kind == gix::dir::entry::Kind::Directory {
176175
saw_ignored_directory |= is_ignored;
177176
saw_untracked_directory |= entry.status == gix::dir::entry::Status::Untracked;
178177
}
179178
writeln!(
180179
out,
181180
"{maybe}{suffix} {}{} {status}",
182-
display_path,
181+
display_path.display(),
183182
disk_kind.is_dir().then_some("/").unwrap_or_default(),
184183
status = match entry.status {
185184
Status::Ignored(kind) => {
@@ -215,8 +214,11 @@ pub(crate) mod function {
215214
},
216215
)?;
217216

217+
if gix::interrupt::is_triggered() {
218+
execute = false;
219+
}
218220
if execute {
219-
let path = workdir.join(gix::path::from_bstr(entry.rela_path));
221+
let path = workdir.join(entry_path);
220222
if disk_kind.is_dir() {
221223
std::fs::remove_dir_all(path)?;
222224
} else {
@@ -286,7 +288,25 @@ pub(crate) mod function {
286288
} else {
287289
writeln!(err, "Nothing to clean{}", wrap_in_parens(make_msg()))?;
288290
}
291+
if gix::interrupt::is_triggered() {
292+
writeln!(err, "Result may be incomplete as it was interrupted")?;
293+
}
289294
}
290295
Ok(())
291296
}
297+
298+
#[derive(Default)]
299+
struct InterruptableCollect {
300+
inner: gix::dir::walk::delegate::Collect,
301+
}
302+
303+
impl gix::dir::walk::Delegate for InterruptableCollect {
304+
fn emit(&mut self, entry: EntryRef<'_>, collapsed_directory_status: Option<Status>) -> walk::Action {
305+
let res = self.inner.emit(entry, collapsed_directory_status);
306+
if gix::interrupt::is_triggered() {
307+
return walk::Action::Cancel;
308+
}
309+
res
310+
}
311+
}
292312
}

gitoxide-core/src/repository/exclude.rs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{borrow::Cow, io};
22

3-
use anyhow::{anyhow, bail};
3+
use anyhow::bail;
44
use gix::bstr::BStr;
55

66
use crate::{repository::PathsOrPatterns, OutputFormat};
@@ -58,42 +58,44 @@ pub fn query(
5858
PathsOrPatterns::Patterns(patterns) => {
5959
let mut pathspec_matched_something = false;
6060
let mut pathspec = repo.pathspec(
61+
true,
6162
patterns.iter(),
6263
repo.work_dir().is_some(),
6364
&index,
6465
gix::worktree::stack::state::attributes::Source::WorktreeThenIdMapping.adjust_for_bare(repo.is_bare()),
6566
)?;
6667

67-
for (path, _entry) in pathspec
68-
.index_entries_with_paths(&index)
69-
.ok_or_else(|| anyhow!("Pathspec didn't yield any entry"))?
70-
{
71-
pathspec_matched_something = true;
72-
let entry = cache.at_entry(path, Some(false))?;
73-
let match_ = entry
74-
.matching_exclude_pattern()
75-
.and_then(|m| (show_ignore_patterns || !m.pattern.is_negative()).then_some(m));
76-
print_match(match_, path, &mut out)?;
68+
if let Some(it) = pathspec.index_entries_with_paths(&index) {
69+
for (path, _entry) in it {
70+
pathspec_matched_something = true;
71+
let entry = cache.at_entry(path, Some(false))?;
72+
let match_ = entry
73+
.matching_exclude_pattern()
74+
.and_then(|m| (show_ignore_patterns || !m.pattern.is_negative()).then_some(m));
75+
print_match(match_, path, &mut out)?;
76+
}
7777
}
7878

7979
if !pathspec_matched_something {
8080
// TODO(borrowchk): this shouldn't be necessary at all, but `pathspec` stays borrowed mutably for some reason.
8181
// It's probably due to the strange lifetimes of `index_entries_with_paths()`.
8282
let pathspec = repo.pathspec(
83+
true,
8384
patterns.iter(),
8485
repo.work_dir().is_some(),
8586
&index,
8687
gix::worktree::stack::state::attributes::Source::WorktreeThenIdMapping
8788
.adjust_for_bare(repo.is_bare()),
8889
)?;
90+
let workdir = repo.work_dir();
8991
for pattern in pathspec.search().patterns() {
9092
let path = pattern.path();
9193
let entry = cache.at_entry(
9294
path,
93-
pattern
94-
.signature
95-
.contains(gix::pathspec::MagicSignature::MUST_BE_DIR)
96-
.into(),
95+
Some(
96+
workdir.map_or(false, |wd| wd.join(gix::path::from_bstr(path)).is_dir())
97+
|| pattern.signature.contains(gix::pathspec::MagicSignature::MUST_BE_DIR),
98+
),
9799
)?;
98100
let match_ = entry
99101
.matching_exclude_pattern()

gitoxide-core/src/repository/index/entries.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ pub(crate) mod function {
256256
)> {
257257
let index = repo.index_or_load_from_head()?;
258258
let pathspec = repo.pathspec(
259+
true,
259260
pathspecs,
260261
false,
261262
&index,

0 commit comments

Comments
 (0)