fix(data): merge preflights ignore untracked files in target working tree
HasChangesAsync counted untracked files, so a stray file in the shared target working dir (e.g. left by a concurrent session) blocked merge preflights even though nothing tracked changed. Add an includeUntracked overload defaulting to true, and pass includeUntracked: false only from the two target-working-tree merge preflights (TaskMergeService.MergeAsync, PlanningMergeOrchestrator.StartAsync). Auto-commit and the worktree cleanup data-loss guard keep counting untracked files, since those callers need to know about them.
This commit is contained in:
@@ -71,9 +71,22 @@ public sealed class GitService
|
||||
return stdout;
|
||||
}
|
||||
|
||||
public async Task<bool> HasChangesAsync(string worktreePath, CancellationToken ct = default)
|
||||
public Task<bool> HasChangesAsync(string worktreePath, CancellationToken ct = default) =>
|
||||
HasChangesAsync(worktreePath, includeUntracked: true, ct);
|
||||
|
||||
/// <summary>
|
||||
/// Uncommitted-changes check. <paramref name="includeUntracked"/>=false ignores untracked
|
||||
/// files — use this for merge preflights on a shared target working dir, where stray
|
||||
/// untracked files (e.g. from a concurrent session) shouldn't block a merge. Auto-commit
|
||||
/// and data-loss-guard callers keep the default (true): a new file a task created, or an
|
||||
/// untracked file about to be discarded, is a real uncommitted change.
|
||||
/// </summary>
|
||||
public async Task<bool> HasChangesAsync(string worktreePath, bool includeUntracked, CancellationToken ct = default)
|
||||
{
|
||||
var (exitCode, stdout, stderr) = await RunGitAsync(worktreePath, ["status", "--porcelain"], ct);
|
||||
string[] args = includeUntracked
|
||||
? ["status", "--porcelain"]
|
||||
: ["status", "--porcelain", "--untracked-files=no"];
|
||||
var (exitCode, stdout, stderr) = await RunGitAsync(worktreePath, args, ct);
|
||||
if (exitCode != 0)
|
||||
throw new InvalidOperationException($"git status --porcelain failed (exit {exitCode}): {stderr}");
|
||||
return !string.IsNullOrWhiteSpace(stdout);
|
||||
|
||||
@@ -115,7 +115,7 @@ public sealed class TaskMergeService
|
||||
return Blocked("working directory is not a git repository");
|
||||
if (await _git.IsMidMergeAsync(list.WorkingDir, ct))
|
||||
return Blocked("target working directory is mid-merge");
|
||||
if (await _git.HasChangesAsync(list.WorkingDir, ct))
|
||||
if (await _git.HasChangesAsync(list.WorkingDir, includeUntracked: false, ct))
|
||||
return Blocked("target working tree has uncommitted changes");
|
||||
|
||||
var currentBranch = await _git.GetCurrentBranchAsync(list.WorkingDir, ct);
|
||||
|
||||
@@ -87,7 +87,7 @@ public sealed class PlanningMergeOrchestrator
|
||||
if (await _git.IsMidMergeAsync(workingDir, ct))
|
||||
throw new InvalidOperationException(
|
||||
"repo is mid-merge; use AbortPlanningMerge to reset the repository, then Approve again");
|
||||
if (await _git.HasChangesAsync(workingDir, ct))
|
||||
if (await _git.HasChangesAsync(workingDir, includeUntracked: false, ct))
|
||||
throw new InvalidOperationException("working tree has uncommitted changes");
|
||||
|
||||
var idsToMerge = new List<string>();
|
||||
|
||||
Reference in New Issue
Block a user