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."