mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
Respect workspace override in git: diff (#48535)
Closes #ISSUE Release Notes: - Fixed an issue where the `git: diff` action would not respect the active worktree
This commit is contained in:
parent
980479fb7c
commit
101a53d904
5 changed files with 147 additions and 55 deletions
|
|
@ -25,7 +25,7 @@ use util::ResultExt;
|
|||
use workspace::notifications::DetachAndPromptErr;
|
||||
use workspace::{ModalView, Workspace};
|
||||
|
||||
use crate::{branch_picker, git_panel::show_error_toast};
|
||||
use crate::{branch_picker, git_panel::show_error_toast, resolve_active_repository};
|
||||
|
||||
actions!(
|
||||
branch_picker,
|
||||
|
|
@ -62,33 +62,7 @@ pub fn open(
|
|||
cx: &mut Context<Workspace>,
|
||||
) {
|
||||
let workspace_handle = workspace.weak_handle();
|
||||
let project = workspace.project().clone();
|
||||
|
||||
// Check if there's a worktree override from the project dropdown.
|
||||
// This ensures the branch picker shows branches for the project the user
|
||||
// explicitly selected in the title bar, not just the focused file's project.
|
||||
// This is only relevant if for multi-projects workspaces.
|
||||
let repository = workspace
|
||||
.active_worktree_override()
|
||||
.and_then(|override_id| {
|
||||
let project_ref = project.read(cx);
|
||||
project_ref
|
||||
.worktree_for_id(override_id, cx)
|
||||
.and_then(|worktree| {
|
||||
let worktree_abs_path = worktree.read(cx).abs_path();
|
||||
let git_store = project_ref.git_store().read(cx);
|
||||
git_store
|
||||
.repositories()
|
||||
.values()
|
||||
.find(|repo| {
|
||||
let repo_path = &repo.read(cx).work_directory_abs_path;
|
||||
*repo_path == worktree_abs_path
|
||||
|| worktree_abs_path.starts_with(repo_path.as_ref())
|
||||
})
|
||||
.cloned()
|
||||
})
|
||||
})
|
||||
.or_else(|| project.read(cx).active_repository(cx));
|
||||
let repository = resolve_active_repository(workspace, cx);
|
||||
|
||||
workspace.toggle_modal(window, cx, |window, cx| {
|
||||
BranchList::new(
|
||||
|
|
|
|||
|
|
@ -568,33 +568,7 @@ fn open_with_tab(
|
|||
cx: &mut Context<Workspace>,
|
||||
) {
|
||||
let workspace_handle = workspace.weak_handle();
|
||||
let project = workspace.project().clone();
|
||||
|
||||
// Check if there's a worktree override from the project dropdown.
|
||||
// This ensures the git picker shows info for the project the user
|
||||
// explicitly selected in the title bar, not just the focused file's project.
|
||||
// This is only relevant if for multi-projects workspaces.
|
||||
let repository = workspace
|
||||
.active_worktree_override()
|
||||
.and_then(|override_id| {
|
||||
let project_ref = project.read(cx);
|
||||
project_ref
|
||||
.worktree_for_id(override_id, cx)
|
||||
.and_then(|worktree| {
|
||||
let worktree_abs_path = worktree.read(cx).abs_path();
|
||||
let git_store = project_ref.git_store().read(cx);
|
||||
git_store
|
||||
.repositories()
|
||||
.values()
|
||||
.find(|repo| {
|
||||
let repo_path = &repo.read(cx).work_directory_abs_path;
|
||||
*repo_path == worktree_abs_path
|
||||
|| worktree_abs_path.starts_with(repo_path.as_ref())
|
||||
})
|
||||
.cloned()
|
||||
})
|
||||
})
|
||||
.or_else(|| project.read(cx).active_repository(cx));
|
||||
let repository = crate::resolve_active_repository(workspace, cx);
|
||||
|
||||
workspace.toggle_modal(window, cx, |window, cx| {
|
||||
GitPicker::new(workspace_handle, repository, tab, rems(34.), window, cx)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use anyhow::anyhow;
|
|||
use command_palette_hooks::CommandPaletteFilter;
|
||||
use commit_modal::CommitModal;
|
||||
use editor::{Editor, actions::DiffClipboardWithSelectionData};
|
||||
|
||||
use project::ProjectPath;
|
||||
use ui::{
|
||||
Headline, HeadlineSize, Icon, IconName, IconSize, IntoElement, ParentElement, Render, Styled,
|
||||
|
|
@ -308,6 +309,32 @@ fn open_modified_files(
|
|||
}
|
||||
}
|
||||
|
||||
/// Resolves the repository for git operations, respecting the workspace's
|
||||
/// active worktree override from the project dropdown.
|
||||
pub fn resolve_active_repository(workspace: &Workspace, cx: &App) -> Option<Entity<Repository>> {
|
||||
let project = workspace.project().read(cx);
|
||||
workspace
|
||||
.active_worktree_override()
|
||||
.and_then(|override_id| {
|
||||
project
|
||||
.worktree_for_id(override_id, cx)
|
||||
.and_then(|worktree| {
|
||||
let worktree_abs_path = worktree.read(cx).abs_path();
|
||||
let git_store = project.git_store().read(cx);
|
||||
git_store
|
||||
.repositories()
|
||||
.values()
|
||||
.find(|repo| {
|
||||
let repo_path = &repo.read(cx).work_directory_abs_path;
|
||||
*repo_path == worktree_abs_path
|
||||
|| worktree_abs_path.starts_with(repo_path.as_ref())
|
||||
})
|
||||
.cloned()
|
||||
})
|
||||
})
|
||||
.or_else(|| project.active_repository(cx))
|
||||
}
|
||||
|
||||
pub fn git_status_icon(status: FileStatus) -> impl IntoElement {
|
||||
GitStatusIcon::new(status)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use crate::{
|
|||
git_panel::{GitPanel, GitPanelAddon, GitStatusEntry},
|
||||
git_panel_settings::GitPanelSettings,
|
||||
remote_button::{render_publish_button, render_push_button},
|
||||
resolve_active_repository,
|
||||
};
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus};
|
||||
|
|
@ -154,6 +155,8 @@ impl ProjectDiff {
|
|||
"Action"
|
||||
}
|
||||
);
|
||||
let intended_repo = resolve_active_repository(workspace, cx);
|
||||
|
||||
let existing = workspace
|
||||
.items_of_type::<Self>(cx)
|
||||
.find(|item| matches!(item.read(cx).diff_base(cx), DiffBase::Head));
|
||||
|
|
@ -177,6 +180,23 @@ impl ProjectDiff {
|
|||
);
|
||||
project_diff
|
||||
};
|
||||
|
||||
if let Some(intended) = &intended_repo {
|
||||
let needs_switch = project_diff
|
||||
.read(cx)
|
||||
.branch_diff
|
||||
.read(cx)
|
||||
.repo()
|
||||
.map_or(true, |current| current.read(cx).id != intended.read(cx).id);
|
||||
if needs_switch {
|
||||
project_diff.update(cx, |project_diff, cx| {
|
||||
project_diff.branch_diff.update(cx, |branch_diff, cx| {
|
||||
branch_diff.set_repo(Some(intended.clone()), cx);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(entry) = entry {
|
||||
project_diff.update(cx, |project_diff, cx| {
|
||||
project_diff.move_to_entry(entry, window, cx);
|
||||
|
|
@ -2619,4 +2639,92 @@ mod tests {
|
|||
|
||||
cx.assert_excerpts_with_selections("[EXCERPT]\nˇ# My cool project\nDetails to come.\n");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_deploy_at_respects_worktree_override(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
path!("/project_a"),
|
||||
json!({
|
||||
".git": {},
|
||||
"a.txt": "CHANGED_A\n",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
fs.insert_tree(
|
||||
path!("/project_b"),
|
||||
json!({
|
||||
".git": {},
|
||||
"b.txt": "CHANGED_B\n",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
fs.set_head_and_index_for_repo(
|
||||
Path::new(path!("/project_a/.git")),
|
||||
&[("a.txt", "original_a\n".to_string())],
|
||||
);
|
||||
fs.set_head_and_index_for_repo(
|
||||
Path::new(path!("/project_b/.git")),
|
||||
&[("b.txt", "original_b\n".to_string())],
|
||||
);
|
||||
|
||||
let project = Project::test(
|
||||
fs.clone(),
|
||||
[
|
||||
Path::new(path!("/project_a")),
|
||||
Path::new(path!("/project_b")),
|
||||
],
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
|
||||
let (worktree_a_id, worktree_b_id) = project.read_with(cx, |project, cx| {
|
||||
let mut worktrees: Vec<_> = project.worktrees(cx).collect();
|
||||
worktrees.sort_by_key(|w| w.read(cx).abs_path());
|
||||
(worktrees[0].read(cx).id(), worktrees[1].read(cx).id())
|
||||
});
|
||||
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
cx.run_until_parked();
|
||||
|
||||
// Select project A via the dropdown override and open the diff.
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.set_active_worktree_override(Some(worktree_a_id), cx);
|
||||
});
|
||||
cx.focus(&workspace);
|
||||
cx.update(|window, cx| {
|
||||
window.dispatch_action(project_diff::Diff.boxed_clone(), cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
let diff_item = workspace.update(cx, |workspace, cx| {
|
||||
workspace.active_item_as::<ProjectDiff>(cx).unwrap()
|
||||
});
|
||||
let paths_a = diff_item.read_with(cx, |diff, cx| diff.excerpt_paths(cx));
|
||||
assert_eq!(paths_a.len(), 1);
|
||||
assert_eq!(*paths_a[0], *"a.txt");
|
||||
|
||||
// Switch the override to project B and re-run the diff action.
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.set_active_worktree_override(Some(worktree_b_id), cx);
|
||||
});
|
||||
cx.focus(&workspace);
|
||||
cx.update(|window, cx| {
|
||||
window.dispatch_action(project_diff::Diff.boxed_clone(), cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
let same_diff_item = workspace.update(cx, |workspace, cx| {
|
||||
workspace.active_item_as::<ProjectDiff>(cx).unwrap()
|
||||
});
|
||||
assert_eq!(diff_item.entity_id(), same_diff_item.entity_id());
|
||||
|
||||
let paths_b = diff_item.read_with(cx, |diff, cx| diff.excerpt_paths(cx));
|
||||
assert_eq!(paths_b.len(), 1);
|
||||
assert_eq!(*paths_b[0], *"b.txt");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,6 +110,15 @@ impl BranchDiff {
|
|||
&self.diff_base
|
||||
}
|
||||
|
||||
pub fn set_repo(&mut self, repo: Option<Entity<Repository>>, cx: &mut Context<Self>) {
|
||||
self.repo = repo;
|
||||
self.tree_diff = None;
|
||||
self.base_commit = None;
|
||||
self.head_commit = None;
|
||||
cx.emit(BranchDiffEvent::FileListChanged);
|
||||
*self.update_needed.borrow_mut() = ();
|
||||
}
|
||||
|
||||
pub async fn handle_status_updates(
|
||||
this: WeakEntity<Self>,
|
||||
mut recv: postage::watch::Receiver<()>,
|
||||
|
|
|
|||
Loading…
Reference in a new issue