From db06e51bb8dd9fde9f529b8d7d1523520a050af1 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Thu, 2 Apr 2026 12:12:25 -0400 Subject: [PATCH] Open repo from disk in create_fresh_worktree instead of searching workspaces The fresh worktree fallback is used when the main repo isn't open in any workspace. Instead of bailing, open the repo directly from disk via fs.open_repo() and call create_worktree on the raw GitRepository. --- crates/agent_ui/src/thread_metadata_store.rs | 3 +- crates/sidebar/src/sidebar.rs | 67 ++++++++++---------- 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/crates/agent_ui/src/thread_metadata_store.rs b/crates/agent_ui/src/thread_metadata_store.rs index 9add4ea97724f48be247fc0ba96f2545f9a0ed3f..d85858e868957e64fc803f5abfa1813a4861f1e8 100644 --- a/crates/agent_ui/src/thread_metadata_store.rs +++ b/crates/agent_ui/src/thread_metadata_store.rs @@ -157,7 +157,8 @@ pub struct ArchivedGitWorktree { pub worktree_path: PathBuf, /// Absolute path of the main repository ("main worktree") that owned this worktree. /// Used when restoring, to reattach the recreated worktree to the correct main worktree. - /// If the main repo isn't found in any open workspace, restoration fails. + /// If the main repo isn't found on disk, unarchiving fails because we only store the + /// commit hash, and without the actual git repo's contents, we can't restore the files. pub main_repo_path: PathBuf, /// Branch that was checked out in the worktree at archive time. `None` if /// the worktree was in detached HEAD state, which isn't supported in Zed, but diff --git a/crates/sidebar/src/sidebar.rs b/crates/sidebar/src/sidebar.rs index d7879555c33a160e02cd0f5e4be203c810210e5e..71bfb98aa76db5ad115bd2a358c465856928b77b 100644 --- a/crates/sidebar/src/sidebar.rs +++ b/crates/sidebar/src/sidebar.rs @@ -2344,7 +2344,7 @@ impl Sidebar { let Some(main_repo) = main_repo else { // Main repo not found — fall back to fresh worktree. - return Self::create_fresh_worktree(row, workspaces, cx).await; + return Self::create_fresh_worktree(row, cx).await; }; // Check if the original worktree path is already in use. @@ -2419,7 +2419,7 @@ impl Sidebar { log::info!("Worktree creation failed ({err}) but path exists — reusing it"); } else { log::error!("Failed to create worktree: {err}"); - return Self::create_fresh_worktree(row, workspaces, cx).await; + return Self::create_fresh_worktree(row, cx).await; } } Err(_) => { @@ -2594,20 +2594,28 @@ impl Sidebar { async fn create_fresh_worktree( row: &ArchivedGitWorktree, - workspaces: &[Entity], cx: &mut AsyncWindowContext, ) -> anyhow::Result { - // Find the main repo entity. - let main_repo = cx.update(|_window, cx| { - find_main_repo_in_workspaces(workspaces, &row.main_repo_path, cx) - })?; + let fs = cx.update(|_window, cx| ::global(cx))?; + let main_repo_path = row.main_repo_path.clone(); + let dot_git_path = main_repo_path.join(".git"); - let Some(main_repo) = main_repo else { + if fs.metadata(&dot_git_path).await?.is_none() { anyhow::bail!( - "Main repository at {} not found in any open workspace", - row.main_repo_path.display() + "Cannot unarchive worktree because there is no longer a git repository at {}", + main_repo_path.display() ); - }; + } + + // Open the repo directly from disk — the main repo may not be + // open in any workspace. + let git_repo = cx + .background_spawn({ + let fs = fs.clone(); + let dot_git_path = dot_git_path.clone(); + async move { fs.open_repo(&dot_git_path, None) } + }) + .await?; // Generate a new branch name for the fresh worktree. let branch_name = { @@ -2620,30 +2628,23 @@ impl Sidebar { .collect::(); format!("restored-{suffix}") }; - let worktree_path = main_repo.update(cx, |repo, _cx| { - let setting = git_store::worktrees_directory_for_repo( - &repo.snapshot().original_repo_abs_path, - git::repository::DEFAULT_WORKTREE_DIRECTORY, - ) - .ok() - .map(|p| p.to_string_lossy().to_string()) - .unwrap_or_default(); - repo.path_for_new_linked_worktree(&branch_name, &setting) - })?; + + // Compute the worktree path (same logic as Repository::path_for_new_linked_worktree). + let project_name = main_repo_path + .file_name() + .ok_or_else(|| anyhow::anyhow!("git repo must have a directory name"))? + .to_string_lossy() + .to_string(); + let worktree_directory = git_store::worktrees_directory_for_repo( + &main_repo_path, + git::repository::DEFAULT_WORKTREE_DIRECTORY, + )?; + let worktree_path = worktree_directory.join(&branch_name).join(&project_name); // Create the fresh worktree. - let create_receiver = main_repo.update(cx, |repo, _cx| { - repo.create_worktree(branch_name, worktree_path.clone(), None) - }); - match create_receiver.await { - Ok(Ok(())) => {} - Ok(Err(err)) => { - anyhow::bail!("Failed to create fresh worktree: {err}"); - } - Err(_) => { - anyhow::bail!("Fresh worktree creation was canceled"); - } - } + git_repo + .create_worktree(Some(branch_name), worktree_path.clone(), None) + .await?; log::warn!( "Unable to restore the original git worktree. Created a fresh worktree instead."