Skip to content

Commit 4ed507e

Browse files
committed
frame for dir walking and handling of untracked files.
1 parent 5d20e57 commit 4ed507e

File tree

17 files changed

+3073
-28
lines changed

17 files changed

+3073
-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: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,19 @@ 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-fs = { version = "^0.10.0", path = "../gix-fs" }
18+
gix-path = { version = "^0.10.4", path = "../gix-path" }
19+
gix-pathspec = { version = "^0.6.0", path = "../gix-pathspec" }
20+
gix-worktree = { version = "^0.30.0", path = "../gix-worktree", default-features = false }
21+
gix-object = { version = "^0.41.0", path = "../gix-object" }
22+
gix-ignore = { version = "^0.11.0", path = "../gix-ignore" }
23+
gix-utils = { version = "^0.1.9", path = "../gix-utils", features = ["bstr"] }
24+
25+
bstr = { version = "1.5.0", default-features = false }
26+
thiserror = "1.0.56"
27+
28+
[dev-dependencies]
29+
gix-testtools = { path = "../tests/tools" }
30+
gix-fs = { path = "../gix-fs" }

gix-dir/src/entry.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
use crate::{Entry, EntryRef};
2+
use std::borrow::Cow;
3+
4+
/// The kind of the entry.
5+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
6+
pub enum Kind {
7+
/// The entry is a blob, executable or not.
8+
File,
9+
/// The entry is a symlink.
10+
Symlink,
11+
/// A directory that contains no file or directory.
12+
EmptyDirectory,
13+
/// The entry is an ordinary directory.
14+
Directory,
15+
/// The entry is a directory which *contains* a `.git` folder.
16+
Repository,
17+
}
18+
19+
/// The kind of entry as obtained from a directory.
20+
///
21+
/// The order of variants roughly relates from cheap-to-compute to most expensive, as each level needs more tests to assert.
22+
/// Thus, `DotGit` is the cheapest, while `Untracked` is among the most expensive and one of the major outcomes of any
23+
/// [`walk`](crate::walk()) run.
24+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
25+
pub enum Status {
26+
/// The filename of an entry was `.git`, which is generally pruned.
27+
DotGit,
28+
/// The provided pathspec prevented further processing as the path didn't match, or it is a `.git` directory.
29+
/// 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`).
30+
Pruned,
31+
/// Always in conjunction with a directory on disk that is also known as cone-mode sparse-checkout exclude marker - i.e. a directory
32+
/// that is excluded, so its whole content is excluded and not checked out nor is part of the index.
33+
TrackedExcluded,
34+
/// The entry is tracked in Git.
35+
Tracked,
36+
/// The entry is ignored as per `.gitignore` files and their rules.
37+
///
38+
/// If this is a directory, then its entire contents is ignored. Otherwise, possibly due to configuration, individual ignored files are listed.
39+
Ignored(gix_ignore::Kind),
40+
/// The entry is not tracked by git yet, it was not found in the [index](gix_index::State).
41+
///
42+
/// If it's a directory, the entire directory contents is untracked.
43+
Untracked,
44+
}
45+
46+
impl EntryRef<'_> {
47+
/// Strip the lifetime to obtain a fully owned copy.
48+
pub fn to_owned(&self) -> Entry {
49+
Entry {
50+
rela_path: self.rela_path.clone().into_owned(),
51+
status: self.status,
52+
kind: self.kind,
53+
}
54+
}
55+
56+
/// Turn this instance into a fully owned copy.
57+
pub fn into_owned(self) -> Entry {
58+
Entry {
59+
rela_path: self.rela_path.into_owned(),
60+
status: self.status,
61+
kind: self.kind,
62+
}
63+
}
64+
}
65+
66+
impl Entry {
67+
/// Obtain an [`EntryRef`] from this instance.
68+
pub fn to_ref(&self) -> EntryRef<'_> {
69+
EntryRef {
70+
rela_path: Cow::Borrowed(self.rela_path.as_ref()),
71+
status: self.status,
72+
kind: self.kind,
73+
}
74+
}
75+
}
76+
77+
impl From<std::fs::FileType> for Kind {
78+
fn from(value: std::fs::FileType) -> Self {
79+
if value.is_dir() {
80+
Kind::Directory
81+
} else if value.is_symlink() {
82+
Kind::Symlink
83+
} else {
84+
Kind::File
85+
}
86+
}
87+
}
88+
89+
impl Status {
90+
/// Return true if this status is considered pruned. A pruned entry is typically hidden from view due to a pathspec.
91+
pub fn is_pruned(&self) -> bool {
92+
match self {
93+
Status::DotGit | Status::TrackedExcluded | Status::Pruned => true,
94+
Status::Ignored(_) | Status::Untracked | Status::Tracked => false,
95+
}
96+
}
97+
/// Return `true` if this directory isn't ignored, and is not a repository.
98+
/// This implements the default rules of `git status`, which is good for a minimal traversal through
99+
/// tracked and non-ignored portions of a worktree.
100+
pub fn can_recurse(&self, file_type: Option<Kind>) -> bool {
101+
if file_type.map_or(true, |ft| !ft.is_recursable_dir()) {
102+
return false;
103+
}
104+
match self {
105+
Status::DotGit | Status::TrackedExcluded | Status::Pruned | Status::Ignored(_) => false,
106+
Status::Untracked | Status::Tracked => true,
107+
}
108+
}
109+
}
110+
111+
impl Kind {
112+
fn is_recursable_dir(&self) -> bool {
113+
matches!(self, Kind::Directory)
114+
}
115+
116+
/// Return `true` if this is a directory on disk. Note that this is true for repositories as well.
117+
pub fn is_dir(&self) -> bool {
118+
matches!(self, Kind::Directory | Kind::Repository)
119+
}
120+
}

0 commit comments

Comments
 (0)