feat(git): add non-destructive merge-tree conflict probe
This commit is contained in:
@@ -3,6 +3,8 @@ using System.Text;
|
||||
|
||||
namespace ClaudeDo.Data.Git;
|
||||
|
||||
public sealed record MergePreview(bool Supported, bool Clean, IReadOnlyList<string> ConflictFiles);
|
||||
|
||||
public sealed class GitService
|
||||
{
|
||||
public async Task<bool> IsGitRepoAsync(string dir, CancellationToken ct = default)
|
||||
@@ -236,6 +238,49 @@ public sealed class GitService
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Non-destructive mergeability probe via `git merge-tree --write-tree`. Writes only
|
||||
/// loose objects — the working tree, index, and refs are left untouched.
|
||||
/// </summary>
|
||||
public async Task<MergePreview> PreviewMergeAsync(
|
||||
string repoDir, string targetBranch, string sourceBranch, CancellationToken ct = default)
|
||||
{
|
||||
var (exitCode, stdout, _) = await RunGitAsync(repoDir,
|
||||
["merge-tree", "--write-tree", "--name-only", targetBranch, sourceBranch], ct);
|
||||
|
||||
if (exitCode == 0)
|
||||
return new MergePreview(true, true, Array.Empty<string>());
|
||||
|
||||
if (exitCode == 1)
|
||||
{
|
||||
// stdout: <tree-oid>\n<file>\n...\n\n<informational messages>
|
||||
var lines = stdout.Split('\n');
|
||||
var files = new List<string>();
|
||||
for (int i = 1; i < lines.Length; i++)
|
||||
{
|
||||
var line = lines[i].TrimEnd('\r');
|
||||
if (string.IsNullOrWhiteSpace(line)) break;
|
||||
files.Add(line.Trim());
|
||||
}
|
||||
return new MergePreview(true, false, files);
|
||||
}
|
||||
|
||||
// Any other exit (e.g. git too old: "unknown option --write-tree").
|
||||
return new MergePreview(false, false, Array.Empty<string>());
|
||||
}
|
||||
|
||||
/// <summary>Count of files that differ on <paramref name="sourceBranch"/> since its merge base with the target.</summary>
|
||||
public async Task<int> CountChangedFilesAsync(
|
||||
string repoDir, string targetBranch, string sourceBranch, CancellationToken ct = default)
|
||||
{
|
||||
var (exitCode, stdout, _) = await RunGitAsync(repoDir,
|
||||
["diff", "--name-only", $"{targetBranch}...{sourceBranch}"], ct);
|
||||
if (exitCode != 0) return 0;
|
||||
return stdout
|
||||
.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||
.Count(s => s.Length > 0);
|
||||
}
|
||||
|
||||
public async Task MergeFfOnlyAsync(string repoDir, string branchName, CancellationToken ct = default)
|
||||
{
|
||||
var (exitCode, _, stderr) = await RunGitAsync(repoDir, ["merge", "--ff-only", branchName], ct);
|
||||
|
||||
Reference in New Issue
Block a user