Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,12 @@ Non-obvious, load-bearing design points:
`Commit()` reuses the `Log()` vector for its read-back, so without the flag a signed commit that
git did make is reported to the caller as a failure, and a retry makes a duplicate.

16. **`Commit()` passes `--cleanup=whitespace`.** With `commit.cleanup=strip` (or `scissors`) in the
host's config, git treats every `#`-leading line of a `--message` as a comment and drops it, so
`#123 fixed the crash` silently vanishes from the body, and a `#42: fix login` subject aborts the
commit as empty. `whitespace` is git's own default for a message that never opens an editor, so
the stored message on an unconfigured host is unchanged.

**Hosting layer.** `GitProvider` is an abstract base with two implementations: `GitHubProvider` over
Octokit, and `AzureDevOpsProvider` over a raw `HttpClient` — Azure DevOps has no client library this
library uses (see the dependency note below). Both go through the same shape: every request-issuing
Expand Down
17 changes: 17 additions & 0 deletions GitIntegration.Test/Builders/GitCommitBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"-c", "core.quotepath=false",
"-c", "color.ui=false",
"commit",
"--cleanup=whitespace",
"--message", "subject here",
];
CollectionAssert.AreEqual(expectedArguments, builder.BuildArguments().ToArray());
Expand All @@ -61,6 +62,22 @@
Assert.AreEqual("body text", arguments[subject + 2]);
}

[TestMethod]
public void PinsTheMessageCleanupModeAheadOfTheMessage()
{
// commit.cleanup=strip on the host would otherwise drop every "#"-leading line of the message.
RecordingGitProcessRunner runner = new();
GitCommitBuilder builder = new(runner, TestPaths.Root, Message);

_ = builder.WithBody("#123 fixed the crash").AllowEmpty().StageTrackedFiles();

string[] arguments = [.. builder.BuildArguments()];
Assert.AreEqual(1, Array.FindAll(arguments, a => a.StartsWith("--cleanup", StringComparison.Ordinal)).Length);

Check warning on line 75 in GitIntegration.Test/Builders/GitCommitBuilderTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'Assert.HasCount' instead of 'Assert.AreEqual'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_GitIntegration&issues=AaEe66NOFg1XlrnQhtTn&open=AaEe66NOFg1XlrnQhtTn&pullRequest=203
Assert.IsLessThan(
Array.IndexOf(arguments, "--message"),
Array.IndexOf(arguments, "--cleanup=whitespace"));
}

[TestMethod]
public void MapsTheOptionFlags()
{
Expand Down
34 changes: 34 additions & 0 deletions GitIntegration.Test/Integration/GitRoundTripTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,40 @@
Assert.IsTrue(suppressed.IsClean);
}

[TestMethod]
public async Task CommitKeepsHashLinesEvenWhereTheHostStripsCommentsAsync()
{
// commit.cleanup is a host setting, and under "strip" git treats every "#"-leading line as a
// comment. Written into the throwaway repository's own config for the same reason as the
// status.showUntrackedFiles test above: same variable, nearest scope, host left alone.
CancellationToken cancellationToken = TestContext.CancellationTokenSource.Token;

Check warning on line 370 in GitIntegration.Test/Integration/GitRoundTripTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'TestContext.CancellationToken' instead of 'TestContext.CancellationTokenSource.Token'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_GitIntegration&issues=AaEe66SFFg1XlrnQhtTo&open=AaEe66SFFg1XlrnQhtTo&pullRequest=203
await IntegrationGitFixture.RequireGitAsync(cancellationToken).ConfigureAwait(false);

using TemporaryRepository temporary = new();
GitRepository repository = await InitializeAsync(temporary, cancellationToken).ConfigureAwait(false);

_ = await new GitTextBuilder(
repository.ProcessRunner!, repository.LocalPath, "config", "commit.cleanup", "strip")
.ExecuteAsync(cancellationToken).ConfigureAwait(false);

temporary.WriteFile("a.txt", "one\n");
_ = await repository.Add().All().ExecuteAsync(cancellationToken).ConfigureAwait(false);
GitCommit commit = await repository.Commit("Subject".As<GitCommitMessage>())
.WithBody("#123 fixed the crash\nsecond")
.ExecuteAsync(cancellationToken).ConfigureAwait(false);

Assert.AreEqual("#123 fixed the crash\nsecond", commit.Body, "Commit() dropped the body's \"#\" line.");

// A "#"-leading subject is the whole message as far as strip is concerned; without the pin
// git aborts with "empty commit message" instead of recording it.
temporary.WriteFile("a.txt", "two\n");
_ = await repository.Add().All().ExecuteAsync(cancellationToken).ConfigureAwait(false);
GitCommit hashSubject = await repository.Commit("#42: fix login".As<GitCommitMessage>())
.ExecuteAsync(cancellationToken).ConfigureAwait(false);

Assert.AreEqual("#42: fix login", hashSubject.Subject);
}

[TestMethod]
public async Task BranchCreateCheckoutAndDeleteRoundTripAsync()
{
Expand Down
6 changes: 6 additions & 0 deletions GitIntegration/Builders/GitCommitBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ protected override void AppendVerbArguments(ICollection<string> arguments)
arguments.Add("--author=" + _author);
}

// Pinned because commit.cleanup is a host setting: under "strip" or "scissors" git treats every
// "#"-leading line as a comment and drops it, so "#123 fixed the crash" vanishes from the body
// and a "#42: fix login" subject aborts the commit as empty. "whitespace" is git's own default
// for a message that never opens an editor, so an unconfigured host stores the same message.
arguments.Add("--cleanup=whitespace");

// --message rather than -m, and repeated for the body: git joins repeated values with a
// blank line between them, which is precisely the subject-then-body convention.
arguments.Add("--message");
Expand Down
Loading