agent: Clean up old remove worktree manage folder code (#54205)

Anthony Eid created

This is a follow up to: #54202 

`thread_worktree_archive.rs` had its own
walk-up-and-remove-empty-parents pass that ran after archiving a Zed
managed git worktree. It duplicated logic that #54202 moved into
`Repository::remove_worktree` (which also adds remote support for this)

This PR deletes the dead code path and its associated tests.

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #ISSUE

Release Notes:

- N/A

Change summary

crates/agent_ui/src/thread_worktree_archive.rs | 181 --------------------
1 file changed, 181 deletions(-)

Detailed changes

crates/agent_ui/src/thread_worktree_archive.rs 🔗

@@ -247,78 +247,9 @@ async fn remove_root_after_worktree_removal(
     // alive until the repo removes the worktree
     drop(project);
     result.context("git worktree metadata cleanup failed")?;
-
-    // Empty-parent cleanup uses local std::fs — skip for remote projects.
-    if root.remote_connection.is_none() {
-        remove_empty_parent_dirs_up_to_worktrees_base(
-            root.root_path.clone(),
-            root.main_repo_path.clone(),
-            cx,
-        )
-        .await;
-    }
-
     Ok(())
 }
 
-/// After `git worktree remove` deletes the worktree directory, clean up any
-/// empty parent directories between it and the Zed-managed worktrees base
-/// directory (configured via `git.worktree_directory`). The base directory
-/// itself is never removed.
-///
-/// If the base directory is not an ancestor of `root_path`, no parent
-/// directories are removed.
-async fn remove_empty_parent_dirs_up_to_worktrees_base(
-    root_path: PathBuf,
-    main_repo_path: PathBuf,
-    cx: &mut AsyncApp,
-) {
-    let worktrees_base = cx.update(|cx| worktrees_base_for_repo(&main_repo_path, cx));
-
-    if let Some(worktrees_base) = worktrees_base {
-        cx.background_executor()
-            .spawn(async move {
-                remove_empty_ancestors(&root_path, &worktrees_base);
-            })
-            .await;
-    }
-}
-
-/// Removes empty directories between `child_path` and `base_path`.
-///
-/// Walks upward from `child_path`, removing each empty parent directory,
-/// stopping before `base_path` itself is removed. If `base_path` is not
-/// an ancestor of `child_path`, nothing is removed. If any directory is
-/// non-empty (i.e. `std::fs::remove_dir` fails), the walk stops.
-fn remove_empty_ancestors(child_path: &Path, base_path: &Path) {
-    let mut current = child_path;
-    while let Some(parent) = current.parent() {
-        if parent == base_path {
-            break;
-        }
-        if !parent.starts_with(base_path) {
-            break;
-        }
-        match std::fs::remove_dir(parent) {
-            Ok(()) => {
-                log::info!("Removed empty parent directory: {}", parent.display());
-            }
-            Err(err) if err.kind() == std::io::ErrorKind::DirectoryNotEmpty => break,
-            Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
-                // Already removed by a concurrent process; keep walking upward.
-            }
-            Err(err) => {
-                log::error!(
-                    "Failed to remove parent directory {}: {err}",
-                    parent.display()
-                );
-                break;
-            }
-        }
-        current = parent;
-    }
-}
-
 /// Finds a live `Repository` entity for the given path, or creates a temporary
 /// project to obtain one.
 ///
@@ -913,7 +844,6 @@ mod tests {
     use project::Project;
     use serde_json::json;
     use settings::SettingsStore;
-    use tempfile::TempDir;
     use workspace::MultiWorkspace;
 
     fn init_test(cx: &mut TestAppContext) {
@@ -926,117 +856,6 @@ mod tests {
         });
     }
 
-    #[test]
-    fn test_remove_empty_ancestors_single_empty_parent() {
-        let tmp = TempDir::new().unwrap();
-        let base = tmp.path().join("worktrees");
-        let branch_dir = base.join("my-branch");
-        let child = branch_dir.join("zed");
-
-        std::fs::create_dir_all(&child).unwrap();
-        // Simulate git worktree remove having deleted the child.
-        std::fs::remove_dir(&child).unwrap();
-
-        assert!(branch_dir.exists());
-        remove_empty_ancestors(&child, &base);
-        assert!(!branch_dir.exists(), "empty parent should be removed");
-        assert!(base.exists(), "base directory should be preserved");
-    }
-
-    #[test]
-    fn test_remove_empty_ancestors_nested_empty_parents() {
-        let tmp = TempDir::new().unwrap();
-        let base = tmp.path().join("worktrees");
-        // Branch name with slash creates nested dirs: fix/thing/zed
-        let child = base.join("fix").join("thing").join("zed");
-
-        std::fs::create_dir_all(&child).unwrap();
-        std::fs::remove_dir(&child).unwrap();
-
-        assert!(base.join("fix").join("thing").exists());
-        remove_empty_ancestors(&child, &base);
-        assert!(!base.join("fix").join("thing").exists());
-        assert!(
-            !base.join("fix").exists(),
-            "all empty ancestors should be removed"
-        );
-        assert!(base.exists(), "base directory should be preserved");
-    }
-
-    #[test]
-    fn test_remove_empty_ancestors_stops_at_non_empty_parent() {
-        let tmp = TempDir::new().unwrap();
-        let base = tmp.path().join("worktrees");
-        let branch_dir = base.join("my-branch");
-        let child = branch_dir.join("zed");
-        let sibling = branch_dir.join("other-file.txt");
-
-        std::fs::create_dir_all(&child).unwrap();
-        std::fs::write(&sibling, "content").unwrap();
-        std::fs::remove_dir(&child).unwrap();
-
-        remove_empty_ancestors(&child, &base);
-        assert!(branch_dir.exists(), "non-empty parent should be preserved");
-        assert!(sibling.exists());
-    }
-
-    #[test]
-    fn test_remove_empty_ancestors_not_an_ancestor() {
-        let tmp = TempDir::new().unwrap();
-        let base = tmp.path().join("worktrees");
-        let unrelated = tmp.path().join("other-place").join("branch").join("zed");
-
-        std::fs::create_dir_all(&base).unwrap();
-        std::fs::create_dir_all(&unrelated).unwrap();
-        std::fs::remove_dir(&unrelated).unwrap();
-
-        let parent = unrelated.parent().unwrap();
-        assert!(parent.exists());
-        remove_empty_ancestors(&unrelated, &base);
-        assert!(parent.exists(), "should not remove dirs outside base");
-    }
-
-    #[test]
-    fn test_remove_empty_ancestors_child_is_direct_child_of_base() {
-        let tmp = TempDir::new().unwrap();
-        let base = tmp.path().join("worktrees");
-        let child = base.join("zed");
-
-        std::fs::create_dir_all(&child).unwrap();
-        std::fs::remove_dir(&child).unwrap();
-
-        remove_empty_ancestors(&child, &base);
-        assert!(base.exists(), "base directory should be preserved");
-    }
-
-    #[test]
-    fn test_remove_empty_ancestors_partially_non_empty_chain() {
-        let tmp = TempDir::new().unwrap();
-        let base = tmp.path().join("worktrees");
-        // Structure: base/a/b/c/zed where a/ has another child besides b/
-        let child = base.join("a").join("b").join("c").join("zed");
-        let other_in_a = base.join("a").join("other-branch");
-
-        std::fs::create_dir_all(&child).unwrap();
-        std::fs::create_dir_all(&other_in_a).unwrap();
-        std::fs::remove_dir(&child).unwrap();
-
-        remove_empty_ancestors(&child, &base);
-        assert!(
-            !base.join("a").join("b").join("c").exists(),
-            "c/ should be removed (empty)"
-        );
-        assert!(
-            !base.join("a").join("b").exists(),
-            "b/ should be removed (empty)"
-        );
-        assert!(
-            base.join("a").exists(),
-            "a/ should be preserved (has other-branch sibling)"
-        );
-        assert!(other_in_a.exists());
-    }
-
     #[gpui::test]
     async fn test_build_root_plan_returns_none_for_main_worktree(cx: &mut TestAppContext) {
         init_test(cx);