Skip to content

Commit 7e9d5fc

Browse files
committed
Replace tree-traversal with State
1 parent bb97f55 commit 7e9d5fc

File tree

1 file changed

+49
-106
lines changed

1 file changed

+49
-106
lines changed

Diff for: helix-vcs/src/git.rs

+49-106
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ use std::{
55
};
66

77
use anyhow::Result;
8+
use git::index::{entry::Mode, State};
89
use git::objs::tree::EntryMode;
9-
use git::sec::trust::DefaultForLevel;
10-
use git::{Commit, ObjectId, Repository, ThreadSafeRepository, Tree};
10+
use git::{prelude::FindExt, sec::trust::DefaultForLevel};
11+
use git::{Commit, ObjectId, Repository, ThreadSafeRepository};
1112
use git_repository as git;
1213
use ignore::WalkBuilder;
1314
use sha1::Digest;
@@ -19,12 +20,6 @@ mod test;
1920

2021
pub struct Git;
2122

22-
struct GitFileMeta {
23-
entry_mode: FileEntryMode,
24-
oid: ObjectId,
25-
path: PathBuf,
26-
}
27-
2823
/// A subset of `git_repository::objs::tree::EntryMode` that actually makes sense for tree nodes.
2924
#[derive(Hash, PartialEq, Eq)]
3025
enum FileEntryMode {
@@ -118,65 +113,65 @@ impl Git {
118113

119114
// TODO: allow diffing against another ref
120115
let head_tree = repo.head_commit()?.tree()?;
121-
122-
let mut head_tree_files = vec![];
123-
let mut submodule_paths = vec![PathBuf::from(".git")];
124-
traverse_tree(
125-
&head_tree,
126-
repo,
127-
PathBuf::new(),
128-
&mut head_tree_files,
129-
&mut submodule_paths,
130-
)?;
131-
132-
submodule_paths
133-
.iter_mut()
134-
.for_each(|path| *path = work_dir.join(&path));
116+
let head_state = State::from_tree(&head_tree.id, |oid, buf| {
117+
repo.objects.find_tree_iter(oid, buf).ok()
118+
})?;
135119

136120
let mut head_tree_set = HashSet::new();
121+
let mut submodule_paths = vec![];
137122

138123
let mut raw_changes = RawChanges::default();
139124

140-
// Looks for modified & deleted files by walking the head tree and probing the fs
141-
for item in head_tree_files.into_iter() {
142-
let full_path = work_dir.join(&item.path);
125+
for item in head_state.entries() {
126+
let full_path = work_dir.join(&PathBuf::from(item.path(&head_state).to_string()));
143127

144-
match git_meta_from_path(&full_path, autocrlf)? {
145-
Some((new_entry_mode, new_oid)) => {
146-
// On Windows, physical files are _always_ inferred as `Blob`. We simply don't
147-
// compare the entry mode as it's pointless.
148-
let entry_mode_changed = {
149-
#[cfg(unix)]
150-
{
151-
new_entry_mode != item.entry_mode
152-
}
128+
if item.mode == Mode::COMMIT {
129+
submodule_paths.push(full_path);
130+
} else {
131+
let old_entry_mode = match item.mode {
132+
Mode::FILE => FileEntryMode::Blob,
133+
Mode::FILE_EXECUTABLE => FileEntryMode::BlobExecutable,
134+
Mode::SYMLINK => FileEntryMode::Link,
135+
_ => anyhow::bail!("unexpected entry mode"),
136+
};
153137

154-
#[cfg(not(unix))]
155-
{
156-
false
138+
match git_meta_from_path(&full_path, autocrlf)? {
139+
Some((new_entry_mode, new_oid)) => {
140+
// On Windows, physical files are _always_ inferred as `Blob`. We simply don't
141+
// compare the entry mode as it's pointless.
142+
let entry_mode_changed = {
143+
#[cfg(unix)]
144+
{
145+
new_entry_mode != old_entry_mode
146+
}
147+
148+
#[cfg(not(unix))]
149+
{
150+
false
151+
}
152+
};
153+
154+
if entry_mode_changed || new_oid != item.id {
155+
raw_changes.add_modification(RawModification {
156+
previous_entry_mode: old_entry_mode,
157+
previous_oid: item.id,
158+
entry_mode: new_entry_mode,
159+
oid: new_oid,
160+
path: full_path.clone(),
161+
});
157162
}
158-
};
159-
160-
if entry_mode_changed || new_oid != item.oid {
161-
raw_changes.add_modification(RawModification {
162-
previous_entry_mode: item.entry_mode,
163-
previous_oid: item.oid,
164-
entry_mode: new_entry_mode,
165-
oid: new_oid,
163+
}
164+
None => {
165+
raw_changes.add_deletion(RawDeletion {
166+
entry_mode: old_entry_mode,
167+
oid: item.id,
166168
path: full_path.clone(),
167169
});
168170
}
169171
}
170-
None => {
171-
raw_changes.add_deletion(RawDeletion {
172-
entry_mode: item.entry_mode,
173-
oid: item.oid,
174-
path: full_path.clone(),
175-
});
176-
}
177-
}
178172

179-
head_tree_set.insert(full_path);
173+
head_tree_set.insert(full_path);
174+
}
180175
}
181176

182177
// Looks for untracked files by walking the fs and probing the (cached) head tree
@@ -346,58 +341,6 @@ fn find_file_in_commit(repo: &Repository, commit: &Commit, file: &Path) -> Optio
346341
}
347342
}
348343

349-
/// Traverses a tree with recursion.
350-
fn traverse_tree(
351-
tree: &Tree,
352-
repo: &Repository,
353-
base: PathBuf,
354-
all_files: &mut Vec<GitFileMeta>,
355-
submodules: &mut Vec<PathBuf>,
356-
) -> Result<()> {
357-
for entry in tree.iter() {
358-
let entry = entry?;
359-
let mut new_base = base.clone();
360-
new_base.push(entry.filename().to_string());
361-
362-
match entry.mode() {
363-
EntryMode::Tree => {
364-
let obj = repo.find_object(entry.id())?;
365-
let new_tree = obj.try_into_tree()?;
366-
traverse_tree(&new_tree, repo, new_base, all_files, submodules)?;
367-
}
368-
EntryMode::Commit => {
369-
// Submodules not supported yet. We return the path so that the fs walk can ignore
370-
// them.
371-
// TODO: support option for recursively looking into submodules
372-
submodules.push(new_base);
373-
}
374-
EntryMode::Link => {
375-
all_files.push(GitFileMeta {
376-
entry_mode: FileEntryMode::Link,
377-
oid: entry.oid(),
378-
path: new_base,
379-
});
380-
}
381-
EntryMode::Blob => {
382-
all_files.push(GitFileMeta {
383-
entry_mode: FileEntryMode::Blob,
384-
oid: entry.oid(),
385-
path: new_base,
386-
});
387-
}
388-
EntryMode::BlobExecutable => {
389-
all_files.push(GitFileMeta {
390-
entry_mode: FileEntryMode::BlobExecutable,
391-
oid: entry.oid(),
392-
path: new_base,
393-
});
394-
}
395-
}
396-
}
397-
398-
Ok(())
399-
}
400-
401344
fn git_meta_from_path(
402345
path: &Path,
403346
autocrlf: bool,

0 commit comments

Comments
 (0)