Fix worktree cleanup looping on orphaned directories

When git worktree remove fails with "not a working tree", fall back to
removing the directory directly and run git worktree prune to clean
stale metadata. Fixes bug 364.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
dave
2026-03-22 18:18:08 +00:00
parent c32bab03a4
commit f9419e5ea7

View File

@@ -253,7 +253,23 @@ fn remove_worktree_sync(project_root: &Path, wt_path: &Path, branch: &str) -> Re
if !output.status.success() { if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr); let stderr = String::from_utf8_lossy(&output.stderr);
slog!("[worktree] remove warning: {stderr}"); if stderr.contains("not a working tree") {
// Orphaned directory: git doesn't recognise it as a worktree.
// Remove the directory directly and prune stale git metadata.
slog!(
"[worktree] orphaned worktree detected, removing directory: {}",
wt_path.display()
);
if let Err(e) = std::fs::remove_dir_all(wt_path) {
slog!("[worktree] failed to remove orphaned directory: {e}");
}
let _ = Command::new("git")
.args(["worktree", "prune"])
.current_dir(project_root)
.output();
} else {
slog!("[worktree] remove warning: {stderr}");
}
} }
// Delete branch (best effort) // Delete branch (best effort)
@@ -630,6 +646,28 @@ mod tests {
); );
} }
#[test]
fn remove_worktree_sync_removes_orphaned_directory() {
let tmp = TempDir::new().unwrap();
let project_root = tmp.path().join("my-project");
fs::create_dir_all(&project_root).unwrap();
init_git_repo(&project_root);
// Create a directory that looks like a worktree but isn't registered with git
let wt_path = project_root
.join(".storkit")
.join("worktrees")
.join("orphan");
fs::create_dir_all(&wt_path).unwrap();
fs::write(wt_path.join("some_file.txt"), "stale").unwrap();
assert!(wt_path.exists());
// git worktree remove will fail with "not a working tree",
// but the fallback should rm -rf the directory
remove_worktree_sync(&project_root, &wt_path, "feature/orphan").unwrap();
assert!(!wt_path.exists());
}
#[test] #[test]
fn remove_worktree_sync_cleans_up_directory() { fn remove_worktree_sync_cleans_up_directory() {
let tmp = TempDir::new().unwrap(); let tmp = TempDir::new().unwrap();