Move git status out of Entry (#22224)
- [x] Rewrite worktree git handling - [x] Fix tests - [x] Fix `test_propagate_statuses_for_repos_under_project` - [x] Replace `WorkDirectoryEntry` with `WorkDirectory` in `RepositoryEntry` - [x] Add a worktree event for capturing git status changes - [x] Confirm that the local repositories are correctly updating the new WorkDirectory field - [x] Implement the git statuses query as a join when pulling entries out of worktree - [x] Use this new join to implement the project panel and outline panel. - [x] Synchronize git statuses over the wire for collab and remote dev (use the existing `worktree_repository_statuses` table, adjust as needed) - [x] Only send changed statuses to collab Release Notes: - N/A --------- Co-authored-by: Cole Miller <cole@zed.dev> Co-authored-by: Mikayla <mikayla@zed.com> Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
parent
72057e5716
commit
9613084f59
57 changed files with 2824 additions and 1254 deletions
|
@ -63,7 +63,7 @@ use workspace::{
|
|||
notifications::{DetachAndPromptErr, NotifyTaskExt},
|
||||
DraggedSelection, OpenInTerminal, PreviewTabsSettings, SelectedEntry, Workspace,
|
||||
};
|
||||
use worktree::CreatedEntry;
|
||||
use worktree::{CreatedEntry, GitEntry, GitEntryRef};
|
||||
|
||||
const PROJECT_PANEL_KEY: &str = "ProjectPanel";
|
||||
const NEW_ENTRY_ID: ProjectEntryId = ProjectEntryId::MAX;
|
||||
|
@ -76,7 +76,7 @@ pub struct ProjectPanel {
|
|||
// An update loop that keeps incrementing/decrementing scroll offset while there is a dragged entry that's
|
||||
// hovered over the start/end of a list.
|
||||
hover_scroll_task: Option<Task<()>>,
|
||||
visible_entries: Vec<(WorktreeId, Vec<Entry>, OnceCell<HashSet<Arc<Path>>>)>,
|
||||
visible_entries: Vec<(WorktreeId, Vec<GitEntry>, OnceCell<HashSet<Arc<Path>>>)>,
|
||||
/// Maps from leaf project entry ID to the currently selected ancestor.
|
||||
/// Relevant only for auto-fold dirs, where a single project panel entry may actually consist of several
|
||||
/// project entries (and all non-leaf nodes are guaranteed to be directories).
|
||||
|
@ -311,7 +311,8 @@ impl ProjectPanel {
|
|||
this.update_visible_entries(None, cx);
|
||||
cx.notify();
|
||||
}
|
||||
project::Event::WorktreeUpdatedEntries(_, _)
|
||||
project::Event::GitRepositoryUpdated
|
||||
| project::Event::WorktreeUpdatedEntries(_, _)
|
||||
| project::Event::WorktreeAdded(_)
|
||||
| project::Event::WorktreeOrderChanged => {
|
||||
this.update_visible_entries(None, cx);
|
||||
|
@ -1366,9 +1367,10 @@ impl ProjectPanel {
|
|||
let parent_entry = worktree.entry_for_path(parent_path)?;
|
||||
|
||||
// Remove all siblings that are being deleted except the last marked entry
|
||||
let mut siblings: Vec<Entry> = worktree
|
||||
let mut siblings: Vec<_> = worktree
|
||||
.snapshot()
|
||||
.child_entries(parent_path)
|
||||
.with_git_statuses()
|
||||
.filter(|sibling| {
|
||||
sibling.id == latest_entry.id
|
||||
|| !marked_entries_in_worktree.contains(&&SelectedEntry {
|
||||
|
@ -1376,7 +1378,7 @@ impl ProjectPanel {
|
|||
entry_id: sibling.id,
|
||||
})
|
||||
})
|
||||
.cloned()
|
||||
.map(|entry| entry.to_owned())
|
||||
.collect();
|
||||
|
||||
project::sort_worktree_entries(&mut siblings);
|
||||
|
@ -2334,7 +2336,7 @@ impl ProjectPanel {
|
|||
}
|
||||
|
||||
let mut visible_worktree_entries = Vec::new();
|
||||
let mut entry_iter = snapshot.entries(true, 0);
|
||||
let mut entry_iter = snapshot.entries(true, 0).with_git_statuses();
|
||||
let mut auto_folded_ancestors = vec![];
|
||||
while let Some(entry) = entry_iter.entry() {
|
||||
if auto_collapse_dirs && entry.kind.is_dir() {
|
||||
|
@ -2376,7 +2378,7 @@ impl ProjectPanel {
|
|||
}
|
||||
}
|
||||
auto_folded_ancestors.clear();
|
||||
visible_worktree_entries.push(entry.clone());
|
||||
visible_worktree_entries.push(entry.to_owned());
|
||||
let precedes_new_entry = if let Some(new_entry_id) = new_entry_parent_id {
|
||||
entry.id == new_entry_id || {
|
||||
self.ancestors.get(&entry.id).map_or(false, |entries| {
|
||||
|
@ -2390,25 +2392,27 @@ impl ProjectPanel {
|
|||
false
|
||||
};
|
||||
if precedes_new_entry {
|
||||
visible_worktree_entries.push(Entry {
|
||||
id: NEW_ENTRY_ID,
|
||||
kind: new_entry_kind,
|
||||
path: entry.path.join("\0").into(),
|
||||
inode: 0,
|
||||
mtime: entry.mtime,
|
||||
size: entry.size,
|
||||
is_ignored: entry.is_ignored,
|
||||
is_external: false,
|
||||
is_private: false,
|
||||
is_always_included: entry.is_always_included,
|
||||
visible_worktree_entries.push(GitEntry {
|
||||
entry: Entry {
|
||||
id: NEW_ENTRY_ID,
|
||||
kind: new_entry_kind,
|
||||
path: entry.path.join("\0").into(),
|
||||
inode: 0,
|
||||
mtime: entry.mtime,
|
||||
size: entry.size,
|
||||
is_ignored: entry.is_ignored,
|
||||
is_external: false,
|
||||
is_private: false,
|
||||
is_always_included: entry.is_always_included,
|
||||
canonical_path: entry.canonical_path.clone(),
|
||||
char_bag: entry.char_bag,
|
||||
is_fifo: entry.is_fifo,
|
||||
},
|
||||
git_status: entry.git_status,
|
||||
canonical_path: entry.canonical_path.clone(),
|
||||
char_bag: entry.char_bag,
|
||||
is_fifo: entry.is_fifo,
|
||||
});
|
||||
}
|
||||
let worktree_abs_path = worktree.read(cx).abs_path();
|
||||
let (depth, path) = if Some(entry) == worktree.read(cx).root_entry() {
|
||||
let (depth, path) = if Some(entry.entry) == worktree.read(cx).root_entry() {
|
||||
let Some(path_name) = worktree_abs_path
|
||||
.file_name()
|
||||
.with_context(|| {
|
||||
|
@ -2485,8 +2489,8 @@ impl ProjectPanel {
|
|||
entry_iter.advance();
|
||||
}
|
||||
|
||||
snapshot.propagate_git_statuses(&mut visible_worktree_entries);
|
||||
project::sort_worktree_entries(&mut visible_worktree_entries);
|
||||
|
||||
self.visible_entries
|
||||
.push((worktree_id, visible_worktree_entries, OnceCell::new()));
|
||||
}
|
||||
|
@ -2714,13 +2718,13 @@ impl ProjectPanel {
|
|||
None
|
||||
}
|
||||
|
||||
fn entry_at_index(&self, index: usize) -> Option<(WorktreeId, &Entry)> {
|
||||
fn entry_at_index(&self, index: usize) -> Option<(WorktreeId, GitEntryRef)> {
|
||||
let mut offset = 0;
|
||||
for (worktree_id, visible_worktree_entries, _) in &self.visible_entries {
|
||||
if visible_worktree_entries.len() > offset + index {
|
||||
return visible_worktree_entries
|
||||
.get(index)
|
||||
.map(|entry| (*worktree_id, entry));
|
||||
.map(|entry| (*worktree_id, entry.to_ref()));
|
||||
}
|
||||
offset += visible_worktree_entries.len();
|
||||
}
|
||||
|
@ -2753,7 +2757,7 @@ impl ProjectPanel {
|
|||
.collect()
|
||||
});
|
||||
for entry in visible_worktree_entries[entry_range].iter() {
|
||||
callback(entry, entries, cx);
|
||||
callback(&entry, entries, cx);
|
||||
}
|
||||
ix = end_ix;
|
||||
}
|
||||
|
@ -2822,7 +2826,7 @@ impl ProjectPanel {
|
|||
};
|
||||
|
||||
let (depth, difference) =
|
||||
ProjectPanel::calculate_depth_and_difference(entry, entries);
|
||||
ProjectPanel::calculate_depth_and_difference(&entry, entries);
|
||||
|
||||
let filename = match difference {
|
||||
diff if diff > 1 => entry
|
||||
|
@ -2951,9 +2955,9 @@ impl ProjectPanel {
|
|||
worktree_id: WorktreeId,
|
||||
reverse_search: bool,
|
||||
only_visible_entries: bool,
|
||||
predicate: impl Fn(&Entry, WorktreeId) -> bool,
|
||||
predicate: impl Fn(GitEntryRef, WorktreeId) -> bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<Entry> {
|
||||
) -> Option<GitEntry> {
|
||||
if only_visible_entries {
|
||||
let entries = self
|
||||
.visible_entries
|
||||
|
@ -2968,15 +2972,18 @@ impl ProjectPanel {
|
|||
.clone();
|
||||
|
||||
return utils::ReversibleIterable::new(entries.iter(), reverse_search)
|
||||
.find(|ele| predicate(ele, worktree_id))
|
||||
.find(|ele| predicate(ele.to_ref(), worktree_id))
|
||||
.cloned();
|
||||
}
|
||||
|
||||
let worktree = self.project.read(cx).worktree_for_id(worktree_id, cx)?;
|
||||
worktree.update(cx, |tree, _| {
|
||||
utils::ReversibleIterable::new(tree.entries(true, 0usize), reverse_search)
|
||||
.find_single_ended(|ele| predicate(ele, worktree_id))
|
||||
.cloned()
|
||||
utils::ReversibleIterable::new(
|
||||
tree.entries(true, 0usize).with_git_statuses(),
|
||||
reverse_search,
|
||||
)
|
||||
.find_single_ended(|ele| predicate(*ele, worktree_id))
|
||||
.map(|ele| ele.to_owned())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -2984,7 +2991,7 @@ impl ProjectPanel {
|
|||
&self,
|
||||
start: Option<&SelectedEntry>,
|
||||
reverse_search: bool,
|
||||
predicate: impl Fn(&Entry, WorktreeId) -> bool,
|
||||
predicate: impl Fn(GitEntryRef, WorktreeId) -> bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<SelectedEntry> {
|
||||
let mut worktree_ids: Vec<_> = self
|
||||
|
@ -3006,7 +3013,9 @@ impl ProjectPanel {
|
|||
let root_entry = tree.root_entry()?;
|
||||
let tree_id = tree.id();
|
||||
|
||||
let mut first_iter = tree.traverse_from_path(true, true, true, entry.path.as_ref());
|
||||
let mut first_iter = tree
|
||||
.traverse_from_path(true, true, true, entry.path.as_ref())
|
||||
.with_git_statuses();
|
||||
|
||||
if reverse_search {
|
||||
first_iter.next();
|
||||
|
@ -3014,25 +3023,25 @@ impl ProjectPanel {
|
|||
|
||||
let first = first_iter
|
||||
.enumerate()
|
||||
.take_until(|(count, ele)| *ele == root_entry && *count != 0usize)
|
||||
.map(|(_, ele)| ele)
|
||||
.find(|ele| predicate(ele, tree_id))
|
||||
.cloned();
|
||||
.take_until(|(count, entry)| entry.entry == root_entry && *count != 0usize)
|
||||
.map(|(_, entry)| entry)
|
||||
.find(|ele| predicate(*ele, tree_id))
|
||||
.map(|ele| ele.to_owned());
|
||||
|
||||
let second_iter = tree.entries(true, 0usize);
|
||||
let second_iter = tree.entries(true, 0usize).with_git_statuses();
|
||||
|
||||
let second = if reverse_search {
|
||||
second_iter
|
||||
.take_until(|ele| ele.id == start.entry_id)
|
||||
.filter(|ele| predicate(ele, tree_id))
|
||||
.filter(|ele| predicate(*ele, tree_id))
|
||||
.last()
|
||||
.cloned()
|
||||
.map(|ele| ele.to_owned())
|
||||
} else {
|
||||
second_iter
|
||||
.take_while(|ele| ele.id != start.entry_id)
|
||||
.filter(|ele| predicate(ele, tree_id))
|
||||
.filter(|ele| predicate(*ele, tree_id))
|
||||
.last()
|
||||
.cloned()
|
||||
.map(|ele| ele.to_owned())
|
||||
};
|
||||
|
||||
if reverse_search {
|
||||
|
@ -3089,7 +3098,7 @@ impl ProjectPanel {
|
|||
&self,
|
||||
start: Option<&SelectedEntry>,
|
||||
reverse_search: bool,
|
||||
predicate: impl Fn(&Entry, WorktreeId) -> bool,
|
||||
predicate: impl Fn(GitEntryRef, WorktreeId) -> bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<SelectedEntry> {
|
||||
let mut worktree_ids: Vec<_> = self
|
||||
|
@ -3131,8 +3140,8 @@ impl ProjectPanel {
|
|||
)
|
||||
};
|
||||
|
||||
let first_search = first_iter.find(|ele| predicate(ele, start.worktree_id));
|
||||
let second_search = second_iter.find(|ele| predicate(ele, start.worktree_id));
|
||||
let first_search = first_iter.find(|ele| predicate(ele.to_ref(), start.worktree_id));
|
||||
let second_search = second_iter.find(|ele| predicate(ele.to_ref(), start.worktree_id));
|
||||
|
||||
if first_search.is_some() {
|
||||
return first_search.map(|entry| SelectedEntry {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue