feat(merge): Stash-Option fuer uncommittete Aenderungen im Ziel-Tree
Ein dirty Ziel-Arbeitsverzeichnis gibt jetzt den eigenen Status dirty_target zurueck statt blocked. Die UI bietet daraufhin einen Retry an, der per git merge --autostash stasht, merged und die Aenderungen danach wiederherstellt - opt-in pro Merge: - Approve & Merge: Bestaetigungsdialog - Merge-Modal: Button "Aenderungen stashen und mergen" - MCP (merge_task/review_task) wirft weiterhin, kein autostash dort
This commit is contained in:
@@ -70,6 +70,10 @@ public sealed class TaskMergeService
|
||||
public const string StatusMergedVerifyPending = "merged_verify_pending";
|
||||
public const string StatusVerifyPassed = "verify_passed";
|
||||
public const string StatusUntrackedCollision = "untracked_collision";
|
||||
// Nothing landed -- the target working dir has uncommitted changes. Its own status rather
|
||||
// than a Blocked reason string so callers can offer the autostash retry (MergeAsync's
|
||||
// autostash: true) without matching on prose.
|
||||
public const string StatusDirtyTarget = "dirty_target";
|
||||
|
||||
public const string StatusReverted = "reverted";
|
||||
public const string StatusConflictAborted = "conflict_aborted";
|
||||
@@ -375,7 +379,8 @@ public sealed class TaskMergeService
|
||||
bool leaveConflictsInTree,
|
||||
CancellationToken ct,
|
||||
IProgress<ProgressNotificationValue>? progress = null,
|
||||
bool skipVerify = false)
|
||||
bool skipVerify = false,
|
||||
bool autostash = false)
|
||||
{
|
||||
var (task, list, wt, verifyCommand) = await LoadMergeContextAsync(taskId, ct);
|
||||
|
||||
@@ -400,8 +405,10 @@ 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, includeUntracked: false, ct))
|
||||
return Blocked("target working tree has uncommitted changes");
|
||||
// autostash hands the dirty tree to git itself (merge --autostash) instead of refusing.
|
||||
if (!autostash && await _git.HasChangesAsync(list.WorkingDir, includeUntracked: false, ct))
|
||||
return new MergeResult(StatusDirtyTarget, Array.Empty<string>(),
|
||||
"target working tree has uncommitted changes");
|
||||
|
||||
var currentBranch = await _git.GetCurrentBranchAsync(list.WorkingDir, ct);
|
||||
if (!string.Equals(currentBranch, targetBranch, StringComparison.Ordinal))
|
||||
@@ -417,7 +424,7 @@ public sealed class TaskMergeService
|
||||
var message = string.IsNullOrWhiteSpace(commitMessage)
|
||||
? DefaultMergeMessage(task, list)
|
||||
: commitMessage;
|
||||
var (exitCode, stderr) = await _git.MergeNoFfAsync(list.WorkingDir, wt.BranchName, message, ct);
|
||||
var (exitCode, stderr) = await _git.MergeNoFfAsync(list.WorkingDir, wt.BranchName, message, ct, autostash);
|
||||
if (exitCode != 0)
|
||||
{
|
||||
List<string> files;
|
||||
@@ -973,13 +980,15 @@ public sealed class TaskMergeService
|
||||
}
|
||||
}
|
||||
|
||||
public Task<MergeResult> ApproveAndMergeAsync(string taskId, string targetBranch, CancellationToken ct)
|
||||
=> ApproveAndMergeAsync(taskId, targetBranch, leaveConflictsInTree: false, ct);
|
||||
public Task<MergeResult> ApproveAndMergeAsync(
|
||||
string taskId, string targetBranch, CancellationToken ct, bool autostash = false)
|
||||
=> ApproveAndMergeAsync(taskId, targetBranch, leaveConflictsInTree: false, ct, autostash: autostash);
|
||||
|
||||
public async Task<MergeResult> ApproveAndMergeAsync(
|
||||
string taskId, string targetBranch, bool leaveConflictsInTree, CancellationToken ct,
|
||||
IProgress<ProgressNotificationValue>? progress = null,
|
||||
bool skipVerify = false)
|
||||
bool skipVerify = false,
|
||||
bool autostash = false)
|
||||
{
|
||||
var (task, list, wt, verifyCommand) = await LoadMergeContextAsync(taskId, ct);
|
||||
|
||||
@@ -1070,7 +1079,7 @@ public sealed class TaskMergeService
|
||||
// MergeAsync transitions the task WaitingForReview -> Done on a successful merge.
|
||||
// Remove the worktree on approve (matching the unit-merge path) so merged
|
||||
// worktrees don't pile up; the merge commit on the target branch is the record.
|
||||
return await MergeAsync(taskId, target, removeWorktree: true, commitMessage: "", leaveConflictsInTree, ct, progress, skipVerify);
|
||||
return await MergeAsync(taskId, target, removeWorktree: true, commitMessage: "", leaveConflictsInTree, ct, progress, skipVerify, autostash);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user