From da7942457331193dee5b0d7ae11925ebd5670c16 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 05:34:29 +0000 Subject: [PATCH] Fix metadata commit failing when files are identical The metadata update checked HasUncommittedChangesAsync (git status --porcelain) before committing, which reports changes across the entire working tree rather than only the staged metadata files. If the generated metadata files were identical to what was already committed (nothing staged) but some unrelated change existed in the working tree, the check returned true and 'git commit' ran with nothing staged, failing with "nothing to commit" and erroring the whole process. Add IGitService.HasStagedChangesAsync (git diff --cached --name-only) and use it after staging so we only commit when the staged metadata files actually changed. --- KtsuBuild.Tests/Metadata/MetadataServiceTests.cs | 3 +++ KtsuBuild/Abstractions/IGitService.cs | 8 ++++++++ KtsuBuild/Git/GitService.cs | 10 ++++++++++ KtsuBuild/Metadata/MetadataService.cs | 5 ++++- 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/KtsuBuild.Tests/Metadata/MetadataServiceTests.cs b/KtsuBuild.Tests/Metadata/MetadataServiceTests.cs index 933fffe..ad081fc 100644 --- a/KtsuBuild.Tests/Metadata/MetadataServiceTests.cs +++ b/KtsuBuild.Tests/Metadata/MetadataServiceTests.cs @@ -229,6 +229,9 @@ public Task PushAsync(string workingDirectory, CancellationToken cancellationTok public Task HasUncommittedChangesAsync(string workingDirectory, CancellationToken cancellationToken = default) => Task.FromResult(false); + public Task HasStagedChangesAsync(string workingDirectory, CancellationToken cancellationToken = default) + => Task.FromResult(false); + public Task SetIdentityAsync(string workingDirectory, string name, string email, CancellationToken cancellationToken = default) => Task.CompletedTask; } diff --git a/KtsuBuild/Abstractions/IGitService.cs b/KtsuBuild/Abstractions/IGitService.cs index 731d247..bff3389 100644 --- a/KtsuBuild/Abstractions/IGitService.cs +++ b/KtsuBuild/Abstractions/IGitService.cs @@ -140,6 +140,14 @@ public interface IGitService /// True if there are uncommitted changes. public Task HasUncommittedChangesAsync(string workingDirectory, CancellationToken cancellationToken = default); + /// + /// Checks if there are staged changes ready to be committed. + /// + /// The repository directory. + /// A cancellation token. + /// True if there are staged changes. + public Task HasStagedChangesAsync(string workingDirectory, CancellationToken cancellationToken = default); + /// /// Sets the git user identity for commits. /// diff --git a/KtsuBuild/Git/GitService.cs b/KtsuBuild/Git/GitService.cs index 4e2f965..049c724 100644 --- a/KtsuBuild/Git/GitService.cs +++ b/KtsuBuild/Git/GitService.cs @@ -221,6 +221,16 @@ public async Task HasUncommittedChangesAsync(string workingDirectory, Canc return !string.IsNullOrWhiteSpace(result.StandardOutput); } + /// + public async Task HasStagedChangesAsync(string workingDirectory, CancellationToken cancellationToken = default) + { + Ensure.NotNull(workingDirectory); + // --cached limits the diff to staged changes, which is exactly what `git commit` would commit. + // --name-only keeps the output empty when nothing is staged so we can detect a no-op commit. + ProcessResult result = await processRunner.RunAsync("git", "diff --cached --name-only", workingDirectory, cancellationToken).ConfigureAwait(false); + return !string.IsNullOrWhiteSpace(result.StandardOutput); + } + /// public async Task SetIdentityAsync(string workingDirectory, string name, string email, CancellationToken cancellationToken = default) { diff --git a/KtsuBuild/Metadata/MetadataService.cs b/KtsuBuild/Metadata/MetadataService.cs index c5ac738..17ccfee 100644 --- a/KtsuBuild/Metadata/MetadataService.cs +++ b/KtsuBuild/Metadata/MetadataService.cs @@ -93,7 +93,10 @@ public async Task UpdateAllAsync(MetadataUpdateOptions opt logger.WriteInfo($"Adding files to git: {string.Join(", ", filesToAdd)}"); await gitService.StageFilesAsync(config.WorkspacePath, filesToAdd, cancellationToken).ConfigureAwait(false); - hasChanges = await gitService.HasUncommittedChangesAsync(config.WorkspacePath, cancellationToken).ConfigureAwait(false); + // Only the staged metadata files are committed, so check for staged changes specifically. + // If the generated files are identical to what's already committed, nothing is staged + // and committing would fail with "nothing to commit", erroring the whole process. + hasChanges = await gitService.HasStagedChangesAsync(config.WorkspacePath, cancellationToken).ConfigureAwait(false); if (hasChanges) {