feat(ui): dirty Ziel-Verzeichnis beim Oeffnen des Merge-Dialogs melden statt nach dem Fehlschlag

MergeTargetsDto/MergeTargets bekommen TargetDirty (via HasChangesAsync,
includeUntracked: false). Das Merge-Modal zeigt Warnung + Stash-Retry
sofort in InitializeAsync statt erst nach dem dirty_target-Fehlschlag;
der REVIEW-Block in der Detail-Pane zeigt denselben Hinweis neben
Approve. Der dirty_target-Rueckgabepfad bleibt als Absicherung bestehen.

Behebt Befund F6 aus docs/test-session-2026-09-16.md.
This commit is contained in:
Mika Kuns
2026-09-16 15:43:39 +02:00
committed by mika kuns
parent 86cc309ba9
commit 09bcd696dd
12 changed files with 92 additions and 6 deletions
@@ -374,6 +374,38 @@ public class TaskMergeServiceTests : IDisposable
Assert.Contains(targets.DefaultBranch, targets.LocalBranches);
}
[Fact]
public async Task GetTargetsAsync_UncommittedChangeInTarget_ReportsTargetDirty()
{
if (!GitRepoFixture.IsGitAvailable()) return;
var repo = NewRepo();
var db = NewDb();
var (_, task) = await SeedListAndTask(db, workingDir: repo.RepoDir, status: TaskStatus.Done);
File.WriteAllText(Path.Combine(repo.RepoDir, "README.md"), "dirty\n");
var (svc, _) = BuildService(db);
var targets = await svc.GetTargetsAsync(task.Id, CancellationToken.None);
Assert.True(targets.TargetDirty);
}
[Fact]
public async Task GetTargetsAsync_OnlyUntrackedFileInTarget_DoesNotReportTargetDirty()
{
if (!GitRepoFixture.IsGitAvailable()) return;
var repo = NewRepo();
var db = NewDb();
var (_, task) = await SeedListAndTask(db, workingDir: repo.RepoDir, status: TaskStatus.Done);
File.WriteAllText(Path.Combine(repo.RepoDir, "untracked.txt"), "stray\n");
var (svc, _) = BuildService(db);
var targets = await svc.GetTargetsAsync(task.Id, CancellationToken.None);
Assert.False(targets.TargetDirty);
}
[Fact]
public async Task MergeAsync_DirtyWorkingTree_ReturnsBlocked()
{