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) {