feat(worker): add WorktreeManager.DiscardAsync for task reset
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -140,4 +140,32 @@ public sealed class WorktreeManager
|
|||||||
_logger.LogInformation("Committed changes for task {TaskId}: {Head}", task.Id, head);
|
_logger.LogInformation("Committed changes for task {TaskId}: {Head}", task.Id, head);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task DiscardAsync(WorktreeEntity wt, string workingDir, CancellationToken ct)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _git.WorktreeRemoveAsync(workingDir, wt.Path, force: true, ct);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "git worktree remove failed for {Path}", wt.Path);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _git.BranchDeleteAsync(workingDir, wt.BranchName, force: true, ct);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "git branch -D {Branch} failed after worktree removal; continuing", wt.BranchName);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var context = _dbFactory.CreateDbContext();
|
||||||
|
var wtRepo = new WorktreeRepository(context);
|
||||||
|
await wtRepo.SetStateAsync(wt.TaskId, WorktreeState.Discarded, ct);
|
||||||
|
|
||||||
|
_logger.LogInformation("Discarded worktree for task {TaskId} (branch {Branch})", wt.TaskId, wt.BranchName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,6 +181,32 @@ public class WorktreeManagerTests : IDisposable
|
|||||||
return (task, list);
|
return (task, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DiscardAsync_RemovesWorktreeAndBranch_AndSetsStateDiscarded()
|
||||||
|
{
|
||||||
|
if (!GitAvailable) { Assert.True(true, "git not available -- skipping"); return; }
|
||||||
|
|
||||||
|
var repo = CreateRepo();
|
||||||
|
var (task, list) = MakeEntities(repo.RepoDir);
|
||||||
|
var (mgr, db) = await CreateManagerAsync(task, list);
|
||||||
|
|
||||||
|
var ctx = await mgr.CreateAsync(task, list, CancellationToken.None);
|
||||||
|
var worktreePath = ctx.WorktreePath;
|
||||||
|
|
||||||
|
WorktreeEntity wt;
|
||||||
|
using (var readCtx = db.CreateContext())
|
||||||
|
wt = (await new WorktreeRepository(readCtx).GetByTaskIdAsync(task.Id))!;
|
||||||
|
|
||||||
|
await mgr.DiscardAsync(wt, list.WorkingDir!, CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.False(Directory.Exists(worktreePath), "worktree directory should be gone");
|
||||||
|
|
||||||
|
using var readCtx2 = db.CreateContext();
|
||||||
|
var row = await new WorktreeRepository(readCtx2).GetByTaskIdAsync(task.Id);
|
||||||
|
Assert.NotNull(row);
|
||||||
|
Assert.Equal(WorktreeState.Discarded, row!.State);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
foreach (var (repoDir, wtPath) in _worktreeCleanups)
|
foreach (var (repoDir, wtPath) in _worktreeCleanups)
|
||||||
|
|||||||
Reference in New Issue
Block a user