Skip to content

Commit 69ee6a3

Browse files
authored
Merge pull request GitoxideLabs#1727 from GitoxideLabs/dirwalk-ignore-non-regulars
dirwalk ignores non-regular files
2 parents a542775 + a49c960 commit 69ee6a3

File tree

10 files changed

+133
-11
lines changed

10 files changed

+133
-11
lines changed

gitoxide-core/src/repository/clean.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ pub(crate) mod function {
163163
.join(gix::path::from_bstr(entry.rela_path.as_bstr()))
164164
.metadata()
165165
.ok()
166-
.map(|e| e.file_type().into());
166+
.and_then(|e| gix::dir::entry::Kind::try_from_file_type(e.file_type()));
167167
}
168168
let mut disk_kind = entry.disk_kind.expect("present if not pruned");
169169
if !keep {

gix-dir/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ rust-version = "1.65"
1212

1313
[lib]
1414
doctest = false
15+
test = false
1516

1617
[dependencies]
1718
gix-trace = { version = "^0.1.11", path = "../gix-trace" }

gix-dir/src/entry.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -147,15 +147,20 @@ impl Entry {
147147
}
148148
}
149149

150-
impl From<std::fs::FileType> for Kind {
151-
fn from(value: std::fs::FileType) -> Self {
152-
if value.is_dir() {
150+
impl Kind {
151+
/// Try to represent the file type `t` as `Entry`, or return `None` if it cannot be represented.
152+
///
153+
/// The latter can happen if it's a `pipe` for instance.
154+
pub fn try_from_file_type(t: std::fs::FileType) -> Option<Self> {
155+
Some(if t.is_dir() {
153156
Kind::Directory
154-
} else if value.is_symlink() {
157+
} else if t.is_symlink() {
155158
Kind::Symlink
156-
} else {
159+
} else if t.is_file() {
157160
Kind::File
158-
}
161+
} else {
162+
return None;
163+
})
159164
}
160165
}
161166

gix-dir/src/walk/classify.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ pub fn root(
2020
let mut last_length = None;
2121
let mut path_buf = worktree_root.to_owned();
2222
// These initial values kick in if worktree_relative_root.is_empty();
23-
let file_kind = path_buf.symlink_metadata().map(|m| m.file_type().into()).ok();
23+
let file_kind = path_buf
24+
.symlink_metadata()
25+
.ok()
26+
.and_then(|m| entry::Kind::try_from_file_type(m.file_type()));
2427
let mut out = path(&mut path_buf, buf, 0, file_kind, || None, options, ctx)?;
2528
let worktree_root_is_repository = out
2629
.disk_kind
@@ -32,7 +35,10 @@ pub fn root(
3235
}
3336
path_buf.push(component);
3437
buf.extend_from_slice(gix_path::os_str_into_bstr(component.as_os_str()).expect("no illformed UTF8"));
35-
let file_kind = path_buf.symlink_metadata().map(|m| m.file_type().into()).ok();
38+
let file_kind = path_buf
39+
.symlink_metadata()
40+
.ok()
41+
.and_then(|m| entry::Kind::try_from_file_type(m.file_type()));
3642

3743
out = path(
3844
&mut path_buf,
@@ -122,6 +128,8 @@ impl<'a> EntryRef<'a> {
122128
///
123129
/// Returns `(status, file_kind, pathspec_matches_how)` to identify the `status` on disk, along with a classification `file_kind`,
124130
/// and if `file_kind` is not a directory, the way the pathspec matched with `pathspec_matches_how`.
131+
///
132+
/// Note that non-files are pruned by default.
125133
pub fn path(
126134
path: &mut PathBuf,
127135
rela_path: &mut BString,

gix-dir/src/walk/readdir.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ pub(super) fn recursive(
6464
current_bstr,
6565
if prev_len == 0 { 0 } else { prev_len + 1 },
6666
None,
67-
|| entry.file_type().ok().map(Into::into),
67+
|| entry.file_type().ok().and_then(entry::Kind::try_from_file_type),
6868
opts,
6969
ctx,
7070
)?;
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub use gix_testtools::Result;
22

33
mod walk;
4+
#[path = "../walk_utils/mod.rs"]
45
pub mod walk_utils;

gix-dir/tests/walk/mod.rs gix-dir/tests/dir/walk.rs

+88
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,94 @@ use gix_dir::walk::EmissionMode::*;
1818
use gix_dir::walk::ForDeletionMode;
1919
use gix_ignore::Kind::*;
2020

21+
#[test]
22+
#[cfg(unix)]
23+
fn root_is_fifo() {
24+
let root = fixture_in("fifo", "top-level");
25+
26+
let err = try_collect(&root, None, |keep, ctx| {
27+
walk(
28+
&root,
29+
ctx,
30+
gix_dir::walk::Options {
31+
emit_ignored: Some(Matching),
32+
..options()
33+
},
34+
keep,
35+
)
36+
})
37+
.unwrap_err();
38+
assert!(
39+
matches!(err, gix_dir::walk::Error::WorktreeRootIsFile { .. }),
40+
"roots simply need to be directories to work"
41+
);
42+
}
43+
44+
#[test]
45+
#[cfg(unix)]
46+
fn one_top_level_fifo() {
47+
let root = fixture_in("fifo", "single-top-level-fifo");
48+
49+
let ((out, _root), entries) = collect(&root, None, |keep, ctx| {
50+
walk(
51+
&root,
52+
ctx,
53+
gix_dir::walk::Options {
54+
emit_pruned: false,
55+
..options()
56+
},
57+
keep,
58+
)
59+
});
60+
assert_eq!(
61+
out,
62+
walk::Outcome {
63+
read_dir_calls: 1,
64+
returned_entries: entries.len(),
65+
seen_entries: 2,
66+
}
67+
);
68+
69+
assert_eq!(entries, &[], "Non-files are simply pruned by default");
70+
}
71+
72+
#[test]
73+
#[cfg(unix)]
74+
fn fifo_in_traversal() {
75+
let root = fixture_in("fifo", "two-fifos-two-files");
76+
77+
let ((out, _root), entries) = collect(&root, None, |keep, ctx| {
78+
walk(
79+
&root,
80+
ctx,
81+
gix_dir::walk::Options {
82+
emit_pruned: true,
83+
..options()
84+
},
85+
keep,
86+
)
87+
});
88+
assert_eq!(
89+
out,
90+
walk::Outcome {
91+
read_dir_calls: 3,
92+
returned_entries: entries.len(),
93+
seen_entries: 5,
94+
}
95+
);
96+
97+
assert_eq!(
98+
entries,
99+
&[
100+
entry_nokind(".git", Pruned).with_property(DotGit).with_match(Always),
101+
entry("dir-with-file/nested-file", Untracked, File),
102+
entry("file", Untracked, File),
103+
],
104+
"Non-files are not even pruned, they are ignored entirely.\
105+
If one day this isn't what we want, we can create an own filetype for them"
106+
);
107+
}
108+
21109
#[test]
22110
fn symlink_to_dir_can_be_excluded() -> crate::Result {
23111
let root = fixture_in("many-symlinks", "excluded-symlinks-to-dir");
File renamed without changes.

gix-dir/tests/fixtures/fifo.sh

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/usr/bin/env bash
2+
set -eu -o pipefail
3+
4+
mkfifo top-level
5+
6+
git init single-top-level-fifo
7+
(cd single-top-level-fifo
8+
mkfifo top
9+
)
10+
11+
git init two-fifos-two-files
12+
(cd two-fifos-two-files
13+
mkdir dir dir-with-file
14+
touch file dir-with-file/nested-file
15+
16+
mkfifo top
17+
mkfifo dir/nested
18+
)
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
many.tar
2-
many-symlinks.tar
2+
many-symlinks.tar
3+
fifo.tar

0 commit comments

Comments
 (0)