Skip to content
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

Ignore non-regular files #1629

Closed
wants to merge 2 commits into from
Closed
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
13 changes: 3 additions & 10 deletions gitoxide-core/src/repository/clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ pub(crate) mod function {
let mut pruned_entries = 0;
let mut saw_ignored_directory = false;
let mut saw_untracked_directory = false;
for (mut entry, dir_status) in entries.into_iter() {
for (entry, dir_status) in entries.into_iter() {
if dir_status.is_some() {
if debug {
writeln!(
Expand All @@ -130,7 +130,7 @@ pub(crate) mod function {
.pathspec_match
.map_or(false, |m| m != gix::dir::entry::PathspecMatch::Excluded),
Some(pathspec) => pathspec
.pattern_matching_relative_path(entry.rela_path.as_bstr(), entry.disk_kind.map(|k| k.is_dir()))
.pattern_matching_relative_path(entry.rela_path.as_bstr(), entry.disk_kind.is_dir())
.map_or(false, |m| !m.is_excluded()),
};
pruned_entries += usize::from(!pathspec_includes_entry);
Expand Down Expand Up @@ -158,14 +158,7 @@ pub(crate) mod function {
}
Status::Untracked => true,
};
if entry.disk_kind.is_none() {
entry.disk_kind = workdir
.join(gix::path::from_bstr(entry.rela_path.as_bstr()))
.metadata()
.ok()
.map(|e| e.file_type().into());
}
let mut disk_kind = entry.disk_kind.expect("present if not pruned");
let mut disk_kind = entry.disk_kind;
if !keep {
if debug {
writeln!(err, "DBG: prune '{}' as -x or -p is missing", entry.rela_path).ok();
Expand Down
42 changes: 19 additions & 23 deletions gitoxide-core/src/repository/index/entries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,30 +160,26 @@ pub(crate) mod function {
// Note that we intentionally ignore `_case` so that we act like git does, attribute matching case is determined
// by the repository, not the pathspec.
let entry_is_excluded = pathspec
.pattern_matching_relative_path(
entry.path(&index),
Some(false),
&mut |rela_path, _case, is_dir, out| {
cache
.as_mut()
.map(|(attrs, cache)| {
match last_match {
// The user wants the attributes for display, so the match happened already.
Some(matched) => {
attrs.copy_into(cache.attributes_collection(), out);
matched
}
// The user doesn't want attributes, so we set the cache position on demand only
None => cache
.at_entry(rela_path, Some(is_dir_to_mode(is_dir)))
.ok()
.map(|platform| platform.matching_attributes(out))
.unwrap_or_default(),
.pattern_matching_relative_path(entry.path(&index), true, &mut |rela_path, _case, is_dir, out| {
cache
.as_mut()
.map(|(attrs, cache)| {
match last_match {
// The user wants the attributes for display, so the match happened already.
Some(matched) => {
attrs.copy_into(cache.attributes_collection(), out);
matched
}
})
.unwrap_or_default()
},
)
// The user doesn't want attributes, so we set the cache position on demand only
None => cache
.at_entry(rela_path, Some(is_dir_to_mode(is_dir)))
.ok()
.map(|platform| platform.matching_attributes(out))
.unwrap_or_default(),
}
})
.unwrap_or_default()
})
.map_or(true, |m| m.is_excluded());

let entry_is_submodule = entry.mode.is_submodule();
Expand Down
6 changes: 1 addition & 5 deletions gitoxide-core/src/repository/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,7 @@ pub fn show(
status = "?",
rela_path =
gix::path::relativize_with_prefix(&gix::path::from_bstr(entry.rela_path), prefix).display(),
slash = if entry.disk_kind.unwrap_or(gix::dir::entry::Kind::File).is_dir() {
"/"
} else {
""
}
slash = if entry.disk_kind.is_dir() { "/" } else { "" }
)?;
}
}
Expand Down
27 changes: 14 additions & 13 deletions gix-dir/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,17 @@ impl Entry {
}
}

impl From<std::fs::FileType> for Kind {
fn from(value: std::fs::FileType) -> Self {
impl TryFrom<std::fs::FileType> for Kind {
type Error = ();
fn try_from(value: std::fs::FileType) -> Result<Self, Self::Error> {
if value.is_dir() {
Kind::Directory
Ok(Kind::Directory)
} else if value.is_symlink() {
Kind::Symlink
Ok(Kind::Symlink)
} else if value.is_file() {
Ok(Kind::File)
} else {
Kind::File
return Err(());
}
}
}
Expand All @@ -175,18 +178,16 @@ impl Status {
/// Use `pathspec_match` to determine if a pathspec matches in any way, affecting the decision to recurse.
pub fn can_recurse(
&self,
file_type: Option<Kind>,
file_type: Kind,
pathspec_match: Option<PathspecMatch>,
for_deletion: Option<ForDeletionMode>,
worktree_root_is_repository: bool,
) -> bool {
let is_dir_on_disk = file_type.map_or(false, |ft| {
if worktree_root_is_repository {
ft.is_dir()
} else {
ft.is_recursable_dir()
}
});
let is_dir_on_disk = if worktree_root_is_repository {
file_type.is_dir()
} else {
file_type.is_recursable_dir()
};
if !is_dir_on_disk {
return false;
}
Expand Down
5 changes: 2 additions & 3 deletions gix-dir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ pub struct EntryRef<'a> {
/// Additional properties of the entry.
pub property: Option<entry::Property>,
/// Further specify what the entry is on disk, similar to a file mode.
/// This is `None` if we decided it's not worth it to exit early and avoid trying to obtain this information.
pub disk_kind: Option<entry::Kind>,
pub disk_kind: entry::Kind,
/// The kind of entry according to the index, if tracked. *Usually* the same as `disk_kind`.
pub index_kind: Option<entry::Kind>,
/// Determines how the pathspec matched.
Expand All @@ -51,7 +50,7 @@ pub struct Entry {
/// Additional flags that further clarify properties of the entry.
pub property: Option<entry::Property>,
/// Further specify what the entry is on disk, similar to a file mode.
pub disk_kind: Option<entry::Kind>,
pub disk_kind: entry::Kind,
/// The kind of entry according to the index, if tracked. *Usually* the same as `disk_kind`.
/// Note that even if tracked, this might be `None` which indicates this is a worktree placed
/// within the parent repository.
Expand Down
70 changes: 37 additions & 33 deletions gix-dir/src/walk/classify.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{entry, Entry, EntryRef};
use std::borrow::Cow;

use crate::entry::PathspecMatch;
use crate::entry::{Kind, PathspecMatch};
use crate::walk::{Context, Error, ForDeletionMode, Options};
use bstr::{BStr, BString, ByteSlice};
use std::path::{Path, PathBuf};
Expand All @@ -20,26 +20,35 @@ pub fn root(
let mut last_length = None;
let mut path_buf = worktree_root.to_owned();
// These initial values kick in if worktree_relative_root.is_empty();
let file_kind = path_buf.symlink_metadata().map(|m| m.file_type().into()).ok();
let mut out = path(&mut path_buf, buf, 0, file_kind, || None, options, ctx)?;
let worktree_root_is_repository = out
.disk_kind
.map_or(false, |kind| matches!(kind, entry::Kind::Repository));
let compute_file_kind = |path_buf: &mut PathBuf| {
let file_type = path_buf
.symlink_metadata()
.map_err(|err| Error::SymlinkMetadata {
source: err,
path: std::mem::take(path_buf),
})?
.file_type();
Kind::try_from(file_type).map_err(|()| Error::WorktreeRootIsFile {
root: std::mem::take(path_buf),
})
};
let file_kind = compute_file_kind(&mut path_buf)?;
let mut out = path(&mut path_buf, buf, 0, file_kind, options, ctx)?;
let worktree_root_is_repository = out.disk_kind == entry::Kind::Repository;

for component in worktree_relative_root.components() {
if last_length.is_some() {
buf.push(b'/');
}
path_buf.push(component);
buf.extend_from_slice(gix_path::os_str_into_bstr(component.as_os_str()).expect("no illformed UTF8"));
let file_kind = path_buf.symlink_metadata().map(|m| m.file_type().into()).ok();
let file_kind = compute_file_kind(&mut path_buf)?;

out = path(
&mut path_buf,
buf,
last_length.map(|l| l + 1 /* slash */).unwrap_or_default(),
file_kind,
|| None,
options,
ctx,
)?;
Expand Down Expand Up @@ -67,7 +76,7 @@ pub struct Outcome {
///
/// Note that the index is used to avoid disk access provided its entries are marked uptodate
/// (possibly by a prior call to update the status).
pub disk_kind: Option<entry::Kind>,
pub disk_kind: entry::Kind,
/// What the entry looks like in the index, or `None` if we aborted early.
pub index_kind: Option<entry::Kind>,
/// If a pathspec matched, this is how it matched. Maybe `None` if computation didn't see the need to evaluate it.
Expand All @@ -80,7 +89,7 @@ impl Outcome {
self
}

fn with_kind(mut self, disk_kind: Option<entry::Kind>, index_kind: Option<entry::Kind>) -> Self {
fn with_kind(mut self, disk_kind: entry::Kind, index_kind: Option<entry::Kind>) -> Self {
self.disk_kind = disk_kind;
self.index_kind = index_kind;
self
Expand Down Expand Up @@ -126,8 +135,7 @@ pub fn path(
path: &mut PathBuf,
rela_path: &mut BString,
filename_start_idx: usize,
disk_kind: Option<entry::Kind>,
on_demand_disk_kind: impl FnOnce() -> Option<entry::Kind>,
disk_kind: entry::Kind,
Options {
ignore_case,
recurse_repositories,
Expand All @@ -150,11 +158,7 @@ pub fn path(
if is_eq(rela_path[filename_start_idx..].as_bstr(), ".git", ignore_case) {
out.pathspec_match = ctx
.pathspec
.pattern_matching_relative_path(
rela_path.as_bstr(),
disk_kind.map(|ft| ft.is_dir()),
ctx.pathspec_attributes,
)
.pattern_matching_relative_path(rela_path.as_bstr(), disk_kind.is_dir(), ctx.pathspec_attributes)
.map(Into::into);
if for_deletion.is_some() {
if let Some(excluded) = ctx
Expand All @@ -164,7 +168,7 @@ pub fn path(
stack
.at_entry(
rela_path.as_bstr(),
disk_kind.map(|ft| is_dir_to_mode(ft.is_dir())),
Some(is_dir_to_mode(disk_kind.is_dir())),
ctx.objects,
)
.map(|platform| platform.excluded_kind())
Expand All @@ -180,44 +184,44 @@ pub fn path(
}
let pathspec_could_match = ctx
.pathspec
.can_match_relative_path(rela_path.as_bstr(), disk_kind.map(|ft| ft.is_dir()));
.can_match_relative_path(rela_path.as_bstr(), disk_kind.is_dir());
if !pathspec_could_match {
return Ok(out.with_status(entry::Status::Pruned));
return Ok(out);
}

let (uptodate_index_kind, index_kind, property) = resolve_file_type_with_index(
rela_path,
ctx.index,
ctx.ignore_case_index_lookup.filter(|_| ignore_case),
);
let mut kind = uptodate_index_kind.or(disk_kind).or_else(on_demand_disk_kind);
let mut kind = uptodate_index_kind.unwrap_or(disk_kind);

// We always check the pathspec to have the value filled in reliably.
out.pathspec_match = ctx
.pathspec
.pattern_matching_relative_path(rela_path.as_bstr(), kind.map(|ft| ft.is_dir()), ctx.pathspec_attributes)
.pattern_matching_relative_path(rela_path.as_bstr(), kind.is_dir(), ctx.pathspec_attributes)
.map(Into::into);

if worktree_relative_worktree_dirs.map_or(false, |worktrees| worktrees.contains(&*rela_path)) {
return Ok(out
.with_kind(Some(entry::Kind::Repository), None)
.with_kind(entry::Kind::Repository, None)
.with_status(entry::Status::Tracked));
}

let maybe_status = if property.is_none() {
(index_kind.map(|k| k.is_dir()) == kind.map(|k| k.is_dir())).then_some(entry::Status::Tracked)
(index_kind.map(|k| k.is_dir()) == Some(kind.is_dir())).then_some(entry::Status::Tracked)
} else {
out.property = property;
Some(entry::Status::Pruned)
};

let is_dir = if symlinks_to_directories_are_ignored_like_directories
&& ctx.excludes.is_some()
&& kind.map_or(false, |ft| ft == entry::Kind::Symlink)
&& kind == entry::Kind::Symlink
{
path.metadata().ok().map(|md| is_dir_to_mode(md.is_dir()))
} else {
kind.map(|ft| is_dir_to_mode(ft.is_dir()))
Some(is_dir_to_mode(kind.is_dir()))
};

let mut maybe_upgrade_to_repository = |current_kind, find_harder: bool| {
Expand All @@ -231,7 +235,7 @@ pub fn path(
)
};
if let Some(status) = maybe_status {
if kind == Some(entry::Kind::Directory) && index_kind == Some(entry::Kind::Repository) {
if kind == entry::Kind::Directory && index_kind == Some(entry::Kind::Repository) {
kind = maybe_upgrade_to_repository(kind, false);
}
return Ok(out.with_status(status).with_kind(kind, index_kind));
Expand Down Expand Up @@ -265,7 +269,7 @@ pub fn path(
),
);
}
if kind.map_or(false, |d| d.is_recursable_dir())
if kind.is_recursable_dir()
&& (out.pathspec_match.is_none()
|| worktree_relative_worktree_dirs.map_or(false, |worktrees| {
for_deletion.is_some()
Expand All @@ -287,7 +291,7 @@ pub fn path(
debug_assert!(maybe_status.is_none());
let mut status = entry::Status::Untracked;

if kind.map_or(false, |ft| ft.is_dir()) {
if kind.is_dir() {
kind = maybe_upgrade_to_repository(kind, classify_untracked_bare_repositories);
} else if out.pathspec_match.is_none() {
status = entry::Status::Pruned;
Expand All @@ -296,13 +300,13 @@ pub fn path(
}

pub fn maybe_upgrade_to_repository(
current_kind: Option<entry::Kind>,
current_kind: entry::Kind,
find_harder: bool,
recurse_repositories: bool,
path: &mut PathBuf,
current_dir: &Path,
git_dir_realpath: &Path,
) -> Option<entry::Kind> {
) -> entry::Kind {
if recurse_repositories {
return current_kind;
}
Expand All @@ -315,7 +319,7 @@ pub fn maybe_upgrade_to_repository(
is_nested_repo = !git_dir_is_our_own;
}
if is_nested_repo {
return Some(entry::Kind::Repository);
return entry::Kind::Repository;
}
}
path.push(gix_discover::DOT_GIT_DIR);
Expand All @@ -329,7 +333,7 @@ pub fn maybe_upgrade_to_repository(
path.pop();

if is_nested_nonbare_repo {
Some(entry::Kind::Repository)
entry::Kind::Repository
} else {
current_kind
}
Expand Down
Loading
Loading