Skip to content

Commit b4691eb

Browse files
committed
frame for dir walking and handling of untracked files.
1 parent f6f79eb commit b4691eb

File tree

13 files changed

+1442
-28
lines changed

13 files changed

+1442
-28
lines changed

Cargo.lock

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

DEVELOPMENT.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,3 +309,5 @@ GIT_SSH_COMMAND="ssh -VVV" \
309309
git <command>
310310
```
311311

312+
Consider adding `GIT_TRACE2_PERF=1` (possibly add `GIT_TRACE2_PERF_BRIEF=1` for brevity) as well for statistics and variables
313+
(see [their source for more](https://github.com/git/git/blob/b50a608ba20348cb3dfc16a696816d51780e3f0f/trace2/tr2_sysenv.c#L50).

gix-dir/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,18 @@ rust-version = "1.65"
1212
doctest = false
1313

1414
[dependencies]
15+
gix-index = { version = "^0.29.0", path = "../gix-index" }
16+
gix-discover = { version = "^0.29.0", path = "../gix-discover" }
17+
gix-path = { version = "^0.10.4", path = "../gix-path" }
18+
gix-pathspec = { version = "^0.6.0", path = "../gix-pathspec" }
19+
gix-worktree = { version = "^0.30.0", path = "../gix-worktree", default-features = false }
20+
gix-object = { version = "^0.41.0", path = "../gix-object" }
21+
gix-ignore = { version = "^0.11.0", path = "../gix-ignore" }
22+
gix-utils = { version = "^0.1.9", path = "../gix-utils", features = ["bstr"] }
23+
24+
bstr = { version = "1.5.0", default-features = false }
25+
thiserror = "1.0.56"
26+
27+
[dev-dependencies]
28+
gix-testtools = { path = "../tests/tools" }
29+
gix-fs = { path = "../gix-fs" }

gix-dir/src/entry.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use crate::{Entry, EntryRef};
2+
3+
/// The kind of the entry.
4+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
5+
pub enum Kind {
6+
/// The entry is a blob, executable or not.
7+
File,
8+
/// The entry is a symlink.
9+
Symlink,
10+
/// The entry is an ordinary directory.
11+
Directory,
12+
/// The entry is a directory which *contains* a `.git` folder.
13+
Repository,
14+
}
15+
16+
/// The kind of entry as obtained from a directory.
17+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
18+
pub enum Status {
19+
/// The filename of an entry was `.git`, which is generally pruned.
20+
DotGit,
21+
/// The provided pathspec prevented further processing as the path didn't match, or it is a `.git` directory.
22+
/// If this happens, no further checks are done so we wouldn't know if the path is also ignored for example (by mention in `.gitignore`).
23+
Pruned,
24+
/// The entry is ignored as per `.gitignore` files and their rules.
25+
///
26+
/// If this is a directory, then its entire contents is ignored. Otherwise, possibly due to configuration, individual ignored files are listed.
27+
Ignored(gix_ignore::Kind),
28+
/// The entry is not tracked by git yet, it was not found in the [index](gix_index::State).
29+
///
30+
/// If it's a directory, the entire directory contents is untracked.
31+
Untracked,
32+
/// The entry is tracked in git.
33+
Tracked,
34+
}
35+
36+
impl Status {
37+
/// Returns `true` if this entry will generally not be traversed into.
38+
pub fn is_pruned(&self) -> bool {
39+
matches!(self, Status::DotGit | Status::Pruned)
40+
}
41+
}
42+
43+
impl EntryRef<'_> {
44+
/// Strip the lifetime to obtain a fully owned copy.
45+
pub fn to_owned(&self) -> Entry {
46+
Entry {
47+
rela_path: self.rela_path.to_owned(),
48+
status: self.status,
49+
kind: self.kind,
50+
}
51+
}
52+
}
53+
54+
impl Entry {
55+
/// Obtain an [`EntryRef`] from this instance.
56+
pub fn to_ref(&self) -> EntryRef<'_> {
57+
EntryRef {
58+
rela_path: self.rela_path.as_ref(),
59+
status: self.status,
60+
kind: self.kind,
61+
}
62+
}
63+
}
64+
65+
impl From<std::fs::FileType> for Kind {
66+
fn from(value: std::fs::FileType) -> Self {
67+
if value.is_dir() {
68+
Kind::Directory
69+
} else if value.is_symlink() {
70+
Kind::Symlink
71+
} else {
72+
Kind::File
73+
}
74+
}
75+
}
76+
77+
impl Status {
78+
/// Return `true` if this directory isn't ignored, and is not a repository.
79+
/// This implements the default rules of `git status`, which is good for a minimal traversal through
80+
/// tracked and non-ignored portions of a worktree.
81+
pub fn can_recurse(&self, file_type: Option<Kind>) -> bool {
82+
if file_type.map_or(true, |ft| !ft.is_recursable_dir()) {
83+
return false;
84+
}
85+
match self {
86+
Status::DotGit | Status::Pruned => false,
87+
Status::Ignored(_) => false, // TODO: needs option to allow recursing into ignored directories
88+
Status::Untracked | Status::Tracked => true,
89+
}
90+
}
91+
}
92+
93+
impl Kind {
94+
fn is_recursable_dir(&self) -> bool {
95+
matches!(self, Kind::Directory)
96+
}
97+
98+
/// Return `true` if this is a directory on disk. Note that this is true for repositories as well.
99+
pub fn is_dir(&self) -> bool {
100+
matches!(self, Kind::Directory | Kind::Repository)
101+
}
102+
}

gix-dir/src/lib.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,40 @@
11
//! A crate for handling a git-style directory walk.
2-
#![deny(rust_2018_idioms)]
2+
#![deny(missing_docs, rust_2018_idioms)]
33
#![forbid(unsafe_code)]
4+
use bstr::{BStr, BString};
5+
6+
/// A directory entry, typically obtained using [`walk()`].
7+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
8+
pub struct EntryRef<'a> {
9+
/// The repository-relative path at which the file or directory could be found, with unix-style component separators.
10+
///
11+
/// To obtain the respective file, join it with the `worktree_root` passed to [`walk()`].
12+
/// The rationale here is that this is a compressed and normalized version compared to the paths we would otherwise get,
13+
/// which is preferable especially when converted to [`Entry`] due to lower memory requirements.
14+
///
15+
/// This also means that the original path to be presented to the user needs to be computed separately.
16+
pub rela_path: &'a BStr,
17+
/// The status of entry, most closely related to what we know from `git status`, but not the same.
18+
pub status: entry::Status,
19+
/// Further specify the what the entry is, similar to a file mode.
20+
pub kind: entry::Kind,
21+
}
22+
23+
/// Just like [`EntryRef`], but with all fields owned (and thus without a lifetime to consider).
24+
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
25+
pub struct Entry {
26+
/// The path at which the file or directory could be found, always with `root` as prefix,
27+
/// the first parameter of [`walk()`].
28+
pub rela_path: BString,
29+
/// The status of entry, most closely related to what we know from `git status`, but not the same.
30+
pub status: entry::Status,
31+
/// Further specify the what the entry is, similar to a file mode.
32+
pub kind: entry::Kind,
33+
}
34+
35+
///
36+
pub mod entry;
37+
38+
///
39+
pub mod walk;
40+
pub use walk::function::walk;

0 commit comments

Comments
 (0)