Show worktree owner details and add confirmed deletion (#43942)

## What changed

- Display owner thread titles, relative update times, and archived or unavailable status in the managed worktree browser. Offer resume only for resumable owners, including those with compressed thread history.
- Add a delete action with a confirmation dialog that defaults to Cancel and preserves thread history.
- Restrict removal to managed worktrees in the current repository. Refuse the current checkout, including path aliases, and checkouts containing local changes, untracked files, or ignored files.

## Testing

Add browser snapshots and tests for owner states, deletion confirmation, and compressed thread history. Add worktree removal coverage for unrelated checkouts, current-directory aliases, untracked and ignored files, and successful removal of a clean checkout.

GitOrigin-RevId: e5867f1540d5d5bc8130acc76875e51e3d8b3b6a
This commit is contained in:
Benjamin Carlsson
2026-09-08 23:37:26 +00:00
committed by copyberry
parent c53f342fec
commit 973dcd80fc
12 changed files with 611 additions and 31 deletions
+47
View File
@@ -280,6 +280,53 @@ impl WorktreeManager {
metadata::owner(&checkout)
}
/// Remove a currently registered checkout from this repository, refusing local changes.
pub fn remove(&self, source_cwd: &Path, root: &Path) -> Result<()> {
let checkout = self
.list(source_cwd)?
.into_iter()
.find(|checkout| checkout.root == root)
.with_context(|| {
format!(
"{} is not a managed worktree in this repository",
root.display()
)
})?;
let source_cwd = dunce::canonicalize(source_cwd).with_context(|| {
format!("cannot resolve current directory {}", source_cwd.display())
})?;
let checkout_root = dunce::canonicalize(&checkout.root)
.with_context(|| format!("cannot resolve worktree {}", checkout.root.display()))?;
if source_cwd.starts_with(&checkout_root) {
bail!("switch to another checkout before deleting the current worktree");
}
let ignored = git_output(
&checkout.root,
GitOperation::WorkingTree,
[
OsStr::new("ls-files"),
OsStr::new("--others"),
OsStr::new("--ignored"),
OsStr::new("--exclude-standard"),
OsStr::new("-z"),
],
)?;
if !ignored.stdout.is_empty() {
bail!("worktree contains ignored local files; remove them before deleting it");
}
git_output(
&checkout.source_root,
GitOperation::WorkingTree,
[
OsStr::new("worktree"),
OsStr::new("remove"),
root.as_os_str(),
],
)?;
remove_empty_bucket(root);
Ok(())
}
fn managed_checkout(&self, checkout: &Path) -> Result<PathBuf> {
let managed_root = dunce::canonicalize(&self.settings.root).with_context(|| {
format!(