Merge claudedo/e5aeaf59fae840adaea3f1e912e203e1

This commit is contained in:
mika kuns
2026-08-10 14:58:35 +02:00
6 changed files with 174 additions and 30 deletions
+25 -9
View File
@@ -143,10 +143,12 @@ public sealed class GitService
/// (committed-on-branch changes + uncommitted work). Used for viewing a Claude
/// task's total impact relative to where the branch started.
/// </summary>
public async Task<string> GetBranchDiffAsync(string worktreePath, string baseRef, CancellationToken ct = default)
public async Task<string> GetBranchDiffAsync(
string worktreePath, string baseRef, IReadOnlyList<string>? paths = null, CancellationToken ct = default)
{
var (exitCode, stdout, _) = await RunGitAsync(worktreePath,
["diff", baseRef], ct);
var args = new List<string> { "diff", baseRef };
AppendPathFilter(args, paths);
var (exitCode, stdout, _) = await RunGitAsync(worktreePath, args, ct);
if (exitCode == 0 && !string.IsNullOrWhiteSpace(stdout))
return stdout;
// Fallback: whatever the worktree has vs HEAD (uncommitted only).
@@ -158,24 +160,38 @@ public sealed class GitService
/// task's changes after its worktree has been merged away (the commits survive on
/// the target branch even though the worktree directory and branch ref are gone).
/// </summary>
public async Task<string> GetCommitRangeDiffAsync(string repoDir, string baseCommit, string headCommit, CancellationToken ct = default)
public async Task<string> GetCommitRangeDiffAsync(
string repoDir, string baseCommit, string headCommit, IReadOnlyList<string>? paths = null, CancellationToken ct = default)
{
var (exitCode, stdout, stderr) = await RunGitAsync(repoDir,
["diff", $"{baseCommit}..{headCommit}"], ct);
var args = new List<string> { "diff", $"{baseCommit}..{headCommit}" };
AppendPathFilter(args, paths);
var (exitCode, stdout, stderr) = await RunGitAsync(repoDir, args, ct);
if (exitCode != 0)
throw new InvalidOperationException($"git diff {baseCommit}..{headCommit} failed (exit {exitCode}): {stderr}");
return stdout;
}
public async Task<string> DiffStatAsync(string worktreePath, string baseCommit, string headCommit, CancellationToken ct = default)
public async Task<string> DiffStatAsync(
string worktreePath, string baseCommit, string headCommit, IReadOnlyList<string>? paths = null, CancellationToken ct = default)
{
var (exitCode, stdout, stderr) = await RunGitAsync(worktreePath,
["diff", "--stat", $"{baseCommit}..{headCommit}"], ct);
var args = new List<string> { "diff", "--stat", $"{baseCommit}..{headCommit}" };
AppendPathFilter(args, paths);
var (exitCode, stdout, stderr) = await RunGitAsync(worktreePath, args, ct);
if (exitCode != 0)
throw new InvalidOperationException($"git diff --stat failed (exit {exitCode}): {stderr}");
return stdout.Trim();
}
// Appends a `-- <paths>` pathspec filter so git itself narrows the diff instead of the
// caller filtering the result after the fact (works identically for --stat and full diffs).
// No-op when paths is null/empty so existing callers see no behavior change.
private static void AppendPathFilter(List<string> args, IReadOnlyList<string>? paths)
{
if (paths is not { Count: > 0 }) return;
args.Add("--");
args.AddRange(paths);
}
public async Task<string> GetFileDiffAsync(string worktreePath, string? baseCommit, string relativePath, CancellationToken ct = default)
{
string[] args = string.IsNullOrEmpty(baseCommit)