@@ -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");
+ }
}