feat(worker): implement TaskMergeService happy path

This commit is contained in:
Mika Kuns
2026-04-22 09:37:35 +02:00
parent 1c20d8f846
commit 3331c24898

View File

@@ -75,8 +75,55 @@ public sealed class TaskMergeService
if (await _git.HasChangesAsync(list.WorkingDir, ct)) if (await _git.HasChangesAsync(list.WorkingDir, ct))
return Blocked("target working tree has uncommitted changes"); return Blocked("target working tree has uncommitted changes");
// Body added in later tasks. var (exitCode, stderr) = await _git.MergeNoFfAsync(list.WorkingDir, wt.BranchName, commitMessage, ct);
throw new NotImplementedException(); if (exitCode != 0)
{
List<string> files;
try { files = await _git.ListConflictedFilesAsync(list.WorkingDir, ct); }
catch { files = new(); }
try { await _git.MergeAbortAsync(list.WorkingDir, ct); }
catch (Exception ex) { _logger.LogWarning(ex, "git merge --abort failed after conflict"); }
if (files.Count == 0)
{
// Non-conflict failure (e.g. unrelated histories).
return new MergeResult(StatusBlocked, Array.Empty<string>(), $"merge failed: {stderr}");
}
return new MergeResult(StatusConflict, files, null);
}
string? cleanupWarning = null;
if (removeWorktree)
{
try
{
await _git.WorktreeRemoveAsync(list.WorkingDir, wt.Path, force: false, ct);
try { await _git.BranchDeleteAsync(list.WorkingDir, wt.BranchName, force: false, ct); }
catch (Exception ex)
{
_logger.LogWarning(ex, "branch delete failed for {Branch}", wt.BranchName);
cleanupWarning = $"worktree removed, branch delete failed: {ex.Message}";
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "worktree remove failed for {Path}", wt.Path);
cleanupWarning = $"worktree remove failed: {ex.Message}";
}
}
using (var ctx = _dbFactory.CreateDbContext())
{
await new WorktreeRepository(ctx).SetStateAsync(taskId, WorktreeState.Merged, ct);
}
await _broadcaster.WorktreeUpdated(taskId);
_logger.LogInformation(
"Merged task {TaskId} branch {Branch} into {Target} (remove worktree: {Remove})",
taskId, wt.BranchName, targetBranch, removeWorktree);
return new MergeResult(StatusMerged, Array.Empty<string>(), cleanupWarning);
} }
public async Task<MergeTargets> GetTargetsAsync(string taskId, CancellationToken ct) public async Task<MergeTargets> GetTargetsAsync(string taskId, CancellationToken ct)