feat(worktrees): add ForceRemoveAsync for targeted removal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-05-19 09:34:32 +02:00
parent ce30d01b72
commit b095a29f97
2 changed files with 131 additions and 0 deletions

View File

@@ -9,6 +9,7 @@ public sealed class WorktreeMaintenanceService
{
public sealed record CleanupResult(int Removed);
public sealed record ResetResult(int Removed, int TasksAffected, bool Blocked, int RunningTasks);
public sealed record ForceRemoveResult(bool Removed, string? Reason);
private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
private readonly GitService _git;
@@ -95,6 +96,29 @@ public sealed class WorktreeMaintenanceService
PathExistsOnDisk: !string.IsNullOrWhiteSpace(x.Path) && Directory.Exists(x.Path))).ToList();
}
public async Task<ForceRemoveResult> ForceRemoveAsync(string taskId, CancellationToken ct = default)
{
using var context = _dbFactory.CreateDbContext();
var row = await (from w in context.Worktrees
join t in context.Tasks on w.TaskId equals t.Id
join l in context.Lists on t.ListId equals l.Id
where w.TaskId == taskId
select new { Row = new WorktreeRow(w.TaskId, w.Path, w.BranchName, l.WorkingDir),
Status = t.Status })
.AsNoTracking()
.FirstOrDefaultAsync(ct);
if (row is null)
return new ForceRemoveResult(false, "worktree not found");
if (row.Status == ClaudeDo.Data.Models.TaskStatus.Running)
return new ForceRemoveResult(false, "task is currently running");
var ok = await TryRemoveAsync(row.Row, force: true, ct);
return new ForceRemoveResult(ok, ok ? null : "remove failed");
}
private async Task<bool> TryRemoveAsync(WorktreeRow row, bool force, CancellationToken ct)
{
var repoDirExists = !string.IsNullOrWhiteSpace(row.WorkingDir) && Directory.Exists(row.WorkingDir);