@@ -143,6 +143,7 @@ use worktree::{CreatedEntry, Snapshot, Traversal};
pub use worktree::{
Entry, EntryKind, FS_WATCH_LATENCY, File, LocalWorktree, PathChange, ProjectEntryId,
UpdatedEntriesSet, UpdatedGitRepositoriesSet, Worktree, WorktreeId, WorktreeSettings,
+ discover_root_repo_common_dir,
};
use worktree_store::{WorktreeStore, WorktreeStoreEvent};
@@ -605,7 +605,12 @@ impl MultiWorkspace {
}
pub fn restore_project_group_keys(&mut self, keys: Vec<ProjectGroupKey>) {
- let mut restored = keys;
+ let mut restored: Vec<ProjectGroupKey> = Vec::with_capacity(keys.len());
+ for key in keys {
+ if !restored.contains(&key) {
+ restored.push(key);
+ }
+ }
for existing_key in &self.project_group_keys {
if !restored.contains(existing_key) {
restored.push(existing_key.clone());
@@ -8712,34 +8712,92 @@ pub async fn restore_multiworkspace(
..
} = state;
- let window_handle = if active_workspace.paths.is_empty() {
+ let workspace_result = if active_workspace.paths.is_empty() {
cx.update(|cx| {
open_workspace_by_id(active_workspace.workspace_id, app_state.clone(), None, cx)
})
- .await?
+ .await
} else {
- let OpenResult { window, .. } = cx
- .update(|cx| {
- Workspace::new_local(
- active_workspace.paths.paths().to_vec(),
- app_state.clone(),
- None,
- None,
- None,
- OpenMode::Activate,
- cx,
- )
- })
- .await?;
- window
+ cx.update(|cx| {
+ Workspace::new_local(
+ active_workspace.paths.paths().to_vec(),
+ app_state.clone(),
+ None,
+ None,
+ None,
+ OpenMode::Activate,
+ cx,
+ )
+ })
+ .await
+ .map(|result| result.window)
+ };
+
+ let window_handle = match workspace_result {
+ Ok(handle) => handle,
+ Err(err) => {
+ log::error!("Failed to restore active workspace: {err:#}");
+
+ // Try each project group's paths as a fallback.
+ let mut fallback_handle = None;
+ for key in &project_group_keys {
+ let key: ProjectGroupKey = key.clone().into();
+ let paths = key.path_list().paths().to_vec();
+ match cx
+ .update(|cx| {
+ Workspace::new_local(
+ paths,
+ app_state.clone(),
+ None,
+ None,
+ None,
+ OpenMode::Activate,
+ cx,
+ )
+ })
+ .await
+ {
+ Ok(OpenResult { window, .. }) => {
+ fallback_handle = Some(window);
+ break;
+ }
+ Err(fallback_err) => {
+ log::error!("Fallback project group also failed: {fallback_err:#}");
+ }
+ }
+ }
+
+ fallback_handle.ok_or(err)?
+ }
};
if !project_group_keys.is_empty() {
- let restored_keys: Vec<ProjectGroupKey> =
- project_group_keys.into_iter().map(Into::into).collect();
+ let fs = app_state.fs.clone();
+
+ // Resolve linked worktree paths to their main repo paths so
+ // stale keys from previous sessions get normalized and deduped.
+ let mut resolved_keys: Vec<ProjectGroupKey> = Vec::new();
+ for key in project_group_keys.into_iter().map(ProjectGroupKey::from) {
+ let mut resolved_paths = Vec::new();
+ for path in key.path_list().paths() {
+ if let Some(common_dir) =
+ project::discover_root_repo_common_dir(path, fs.as_ref()).await
+ {
+ let main_path = common_dir.parent().unwrap_or(&common_dir);
+ resolved_paths.push(main_path.to_path_buf());
+ } else {
+ resolved_paths.push(path.to_path_buf());
+ }
+ }
+ let resolved = ProjectGroupKey::new(key.host(), PathList::new(&resolved_paths));
+ if !resolved_keys.contains(&resolved) {
+ resolved_keys.push(resolved);
+ }
+ }
+
window_handle
.update(cx, |multi_workspace, _window, _cx| {
- multi_workspace.restore_project_group_keys(restored_keys);
+ multi_workspace.restore_project_group_keys(resolved_keys);
})
.ok();
}
@@ -6109,7 +6109,7 @@ fn parse_gitfile(content: &str) -> anyhow::Result<&Path> {
Ok(Path::new(path.trim()))
}
-async fn discover_root_repo_common_dir(root_abs_path: &Path, fs: &dyn Fs) -> Option<Arc<Path>> {
+pub async fn discover_root_repo_common_dir(root_abs_path: &Path, fs: &dyn Fs) -> Option<Arc<Path>> {
let root_dot_git = root_abs_path.join(DOT_GIT);
if !fs.metadata(&root_dot_git).await.is_ok_and(|m| m.is_some()) {
return None;