Merge claudedo/d86e63f916af4946bbc25098449669ac
This commit is contained in:
@@ -315,7 +315,11 @@ public sealed class TaskMergeService
|
|||||||
if (stillConflicted.Count > 0)
|
if (stillConflicted.Count > 0)
|
||||||
return new MergeResult(StatusConflict, stillConflicted, "conflicts not fully resolved");
|
return new MergeResult(StatusConflict, stillConflicted, "conflicts not fully resolved");
|
||||||
|
|
||||||
await _git.AddAllAsync(list.WorkingDir, ct);
|
// Stage exactly the resolved conflict paths — never `git add -A`, which would sweep
|
||||||
|
// untracked/unrelated changes left by other sessions into this merge commit (the
|
||||||
|
// target working dir is shared).
|
||||||
|
foreach (var path in unresolved)
|
||||||
|
await _git.AddPathAsync(list.WorkingDir, path, ct);
|
||||||
|
|
||||||
var remaining = await _git.ListConflictedFilesAsync(list.WorkingDir, ct);
|
var remaining = await _git.ListConflictedFilesAsync(list.WorkingDir, ct);
|
||||||
if (remaining.Count > 0)
|
if (remaining.Count > 0)
|
||||||
|
|||||||
@@ -447,6 +447,60 @@ public class TaskMergeServiceTests : IDisposable
|
|||||||
Assert.Equal(WorktreeState.Merged, wt.State);
|
Assert.Equal(WorktreeState.Merged, wt.State);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ContinueMergeAsync_UnrelatedChangesInSharedCheckout_AreNotStagedOrCommitted()
|
||||||
|
{
|
||||||
|
if (!GitRepoFixture.IsGitAvailable()) return;
|
||||||
|
|
||||||
|
var db = NewDb();
|
||||||
|
var repo = NewRepo();
|
||||||
|
GitRepoFixture.RunGit(repo.RepoDir, "branch", "-m", "main");
|
||||||
|
|
||||||
|
File.WriteAllText(Path.Combine(repo.RepoDir, "README.md"), "# main change\n");
|
||||||
|
File.WriteAllText(Path.Combine(repo.RepoDir, "tracked.txt"), "tracked v1\n");
|
||||||
|
GitRepoFixture.RunGit(repo.RepoDir, "add", "tracked.txt");
|
||||||
|
GitRepoFixture.RunGit(repo.RepoDir, "commit", "-am", "main change");
|
||||||
|
|
||||||
|
var wtPath = Path.Combine(Path.GetTempPath(), $"wt_{Guid.NewGuid():N}");
|
||||||
|
_wtCleanups.Add((repo.RepoDir, wtPath));
|
||||||
|
GitRepoFixture.RunGit(repo.RepoDir, "worktree", "add", "-b", "claudedo/t5", wtPath, repo.BaseCommit);
|
||||||
|
File.WriteAllText(Path.Combine(wtPath, "README.md"), "# branch change\n");
|
||||||
|
GitRepoFixture.RunGit(wtPath, "commit", "-am", "branch change");
|
||||||
|
|
||||||
|
var (_, task) = await SeedListAndTask(db, workingDir: repo.RepoDir, status: TaskStatus.Done);
|
||||||
|
await SeedWorktree(db, task.Id, wtPath, "claudedo/t5", repo.BaseCommit);
|
||||||
|
|
||||||
|
var (svc, _) = BuildService(db);
|
||||||
|
|
||||||
|
var first = await svc.MergeAsync(task.Id, "main", false, "msg",
|
||||||
|
leaveConflictsInTree: true, CancellationToken.None);
|
||||||
|
Assert.Equal(TaskMergeService.StatusConflict, first.Status);
|
||||||
|
|
||||||
|
// Simulate the user resolving the conflict.
|
||||||
|
File.WriteAllText(Path.Combine(repo.RepoDir, "README.md"), "# resolved\n");
|
||||||
|
|
||||||
|
// A concurrent session leaves stray changes in this shared checkout while the merge is
|
||||||
|
// paused — these must survive continue_merge untouched and never enter the merge commit.
|
||||||
|
File.WriteAllText(Path.Combine(repo.RepoDir, "untracked.txt"), "from another session\n");
|
||||||
|
File.WriteAllText(Path.Combine(repo.RepoDir, "tracked.txt"), "tracked v2 (unrelated edit)\n");
|
||||||
|
|
||||||
|
var result = await svc.ContinueMergeAsync(task.Id, CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Equal(TaskMergeService.StatusMerged, result.Status);
|
||||||
|
|
||||||
|
Assert.True(File.Exists(Path.Combine(repo.RepoDir, "untracked.txt")));
|
||||||
|
Assert.Equal("from another session\n", File.ReadAllText(Path.Combine(repo.RepoDir, "untracked.txt")));
|
||||||
|
Assert.Equal("tracked v2 (unrelated edit)\n", File.ReadAllText(Path.Combine(repo.RepoDir, "tracked.txt")));
|
||||||
|
|
||||||
|
var status = GitRepoFixture.RunGit(repo.RepoDir, "status", "--porcelain");
|
||||||
|
Assert.Contains("?? untracked.txt", status);
|
||||||
|
Assert.Contains(" M tracked.txt", status);
|
||||||
|
|
||||||
|
var committedFiles = GitRepoFixture.RunGit(repo.RepoDir, "show", "--name-only", "--pretty=format:", "HEAD");
|
||||||
|
Assert.DoesNotContain("untracked.txt", committedFiles);
|
||||||
|
Assert.DoesNotContain("tracked.txt", committedFiles);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ContinueMergeAsync_UnresolvedConflictMarkersLeft_RefusesAndDoesNotCommit()
|
public async Task ContinueMergeAsync_UnresolvedConflictMarkersLeft_RefusesAndDoesNotCommit()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user