diff --git a/src/ClaudeDo.Data/Git/GitService.cs b/src/ClaudeDo.Data/Git/GitService.cs index d0a27f8..8cf5466 100644 --- a/src/ClaudeDo.Data/Git/GitService.cs +++ b/src/ClaudeDo.Data/Git/GitService.cs @@ -198,6 +198,19 @@ public sealed class GitService throw new InvalidOperationException($"git merge --abort failed (exit {exitCode}): {stderr}"); } + public async Task> ListConflictedFilesAsync(string repoDir, CancellationToken ct = default) + { + var (exitCode, stdout, stderr) = await RunGitAsync(repoDir, + ["diff", "--name-only", "--diff-filter=U"], ct); + if (exitCode != 0) + throw new InvalidOperationException($"git diff --diff-filter=U failed (exit {exitCode}): {stderr}"); + + return stdout + .Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Where(s => s.Length > 0) + .ToList(); + } + public async Task MergeFfOnlyAsync(string repoDir, string branchName, CancellationToken ct = default) { var (exitCode, _, stderr) = await RunGitAsync(repoDir, ["merge", "--ff-only", branchName], ct); diff --git a/tests/ClaudeDo.Worker.Tests/Runner/GitServiceMergeTests.cs b/tests/ClaudeDo.Worker.Tests/Runner/GitServiceMergeTests.cs index 21939ea..d97031b 100644 --- a/tests/ClaudeDo.Worker.Tests/Runner/GitServiceMergeTests.cs +++ b/tests/ClaudeDo.Worker.Tests/Runner/GitServiceMergeTests.cs @@ -154,4 +154,30 @@ public class GitServiceMergeTests : IDisposable await git.MergeAbortAsync(repo.RepoDir); Assert.False(await git.IsMidMergeAsync(repo.RepoDir)); } + + [Fact] + public async Task ListConflictedFilesAsync_MidConflict_ReturnsConflictedFile() + { + if (!GitRepoFixture.IsGitAvailable()) return; + var repo = NewRepo(); + + GitRepoFixture.RunGit(repo.RepoDir, "checkout", "-b", "feature/cflist"); + File.WriteAllText(Path.Combine(repo.RepoDir, "README.md"), "# feat\n"); + GitRepoFixture.RunGit(repo.RepoDir, "add", "-A"); + GitRepoFixture.RunGit(repo.RepoDir, "commit", "-m", "edit"); + + try { GitRepoFixture.RunGit(repo.RepoDir, "checkout", "main"); } + catch { GitRepoFixture.RunGit(repo.RepoDir, "checkout", "master"); } + File.WriteAllText(Path.Combine(repo.RepoDir, "README.md"), "# main\n"); + GitRepoFixture.RunGit(repo.RepoDir, "add", "-A"); + GitRepoFixture.RunGit(repo.RepoDir, "commit", "-m", "edit"); + + var git = new GitService(); + await git.MergeNoFfAsync(repo.RepoDir, "feature/cflist", "merge"); + + var files = await git.ListConflictedFilesAsync(repo.RepoDir); + Assert.Contains("README.md", files); + + await git.MergeAbortAsync(repo.RepoDir); + } }