Remove ``wt`` using the three-step Windows-safe strategy.
(wt: Path)
| 173 | |
| 174 | |
| 175 | def remove_worktree(wt: Path) -> RemovalResult: |
| 176 | """Remove ``wt`` using the three-step Windows-safe strategy.""" |
| 177 | # Step 1: plain git worktree remove --force |
| 178 | first = run_git(["worktree", "remove", "--force", str(wt)], cwd=PROJECT_ROOT) |
| 179 | if first.returncode == 0 and not wt.exists(): |
| 180 | return RemovalResult(path=wt, removed=True, method="git", error="") |
| 181 | |
| 182 | # Step 2: clear read-only attrs, retry |
| 183 | if wt.exists(): |
| 184 | clear_readonly(wt) |
| 185 | second = run_git(["worktree", "remove", "--force", str(wt)], cwd=PROJECT_ROOT) |
| 186 | if second.returncode == 0 and not wt.exists(): |
| 187 | return RemovalResult(path=wt, removed=True, method="git+chmod", error="") |
| 188 | |
| 189 | # If git removed the admin entry but the directory remains, fall through |
| 190 | # to manual deletion / clud trash. |
| 191 | rmtree_error = "" |
| 192 | if wt.exists(): |
| 193 | try: |
| 194 | shutil.rmtree(wt, ignore_errors=False, onerror=_rmtree_onerror) |
| 195 | except KeyboardInterrupt as ki: |
| 196 | handle_keyboard_interrupt(ki) |
| 197 | raise |
| 198 | except OSError as exc: |
| 199 | rmtree_error = str(exc) |
| 200 | if not wt.exists(): |
| 201 | run_git(["worktree", "prune"], cwd=PROJECT_ROOT) |
| 202 | return RemovalResult(path=wt, removed=True, method="git+rmtree", error="") |
| 203 | |
| 204 | # Step 3: clud trash as last-resort quarantine |
| 205 | clud = shutil.which("clud") |
| 206 | if clud is None: |
| 207 | err = rmtree_error or (second.stderr.strip() or first.stderr.strip()) |
| 208 | return RemovalResult( |
| 209 | path=wt, |
| 210 | removed=False, |
| 211 | method="failed", |
| 212 | error=f"git remove failed and clud not on PATH: {err}", |
| 213 | ) |
| 214 | trash = subprocess.run( |
| 215 | [clud, "trash", "--cross-volume", str(wt)], |
| 216 | capture_output=True, |
| 217 | text=True, |
| 218 | encoding="utf-8", |
| 219 | errors="replace", |
| 220 | check=False, |
| 221 | ) |
| 222 | if trash.returncode == 0: |
| 223 | run_git(["worktree", "prune"], cwd=PROJECT_ROOT) |
| 224 | return RemovalResult(path=wt, removed=True, method="clud-trash", error="") |
| 225 | |
| 226 | return RemovalResult( |
| 227 | path=wt, |
| 228 | removed=False, |
| 229 | method="failed", |
| 230 | error=f"clud trash failed: {trash.stderr.strip() or trash.stdout.strip()}", |
| 231 | ) |
| 232 |
no test coverage detected