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
19 changes: 19 additions & 0 deletions GitIntegration.Test/Builders/GitDiffBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"diff",
"--name-status",
"-z",
"--no-renames",
];
CollectionAssert.AreEqual(expectedArguments, arguments.ToArray());
}
Expand All @@ -47,6 +48,24 @@
CollectionAssert.Contains(arguments, "--find-copies");
}

[TestMethod]
public void TurnsRenameDetectionOffUnlessRequested()
{
// Git defaults diff.renames to true, so the vector has to say "off" rather than say nothing.
string[] plain = [.. new GitDiffBuilder(new RecordingGitProcessRunner(), TestPaths.Root).BuildArguments()];
CollectionAssert.Contains(plain, "--no-renames");

Check warning on line 56 in GitIntegration.Test/Builders/GitDiffBuilderTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'Assert.Contains' instead of 'CollectionAssert.Contains'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_GitIntegration&issues=AaERAdBBQuutVwIrJcWg&open=AaERAdBBQuutVwIrJcWg&pullRequest=180
CollectionAssert.DoesNotContain(plain, "--find-renames");

Check warning on line 57 in GitIntegration.Test/Builders/GitDiffBuilderTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'Assert.DoesNotContain' instead of 'CollectionAssert.DoesNotContain'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_GitIntegration&issues=AaERAdBBQuutVwIrJcWh&open=AaERAdBBQuutVwIrJcWh&pullRequest=180
CollectionAssert.DoesNotContain(plain, "--find-copies");

Check warning on line 58 in GitIntegration.Test/Builders/GitDiffBuilderTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'Assert.DoesNotContain' instead of 'CollectionAssert.DoesNotContain'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_GitIntegration&issues=AaERAdBBQuutVwIrJcWi&open=AaERAdBBQuutVwIrJcWi&pullRequest=180

GitDiffBuilder renames = new(new RecordingGitProcessRunner(), TestPaths.Root);
_ = renames.DetectRenames();
CollectionAssert.DoesNotContain(renames.BuildArguments().ToArray(), "--no-renames");

Check warning on line 62 in GitIntegration.Test/Builders/GitDiffBuilderTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'Assert.DoesNotContain' instead of 'CollectionAssert.DoesNotContain'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_GitIntegration&issues=AaERAdBBQuutVwIrJcWj&open=AaERAdBBQuutVwIrJcWj&pullRequest=180

GitDiffBuilder copies = new(new RecordingGitProcessRunner(), TestPaths.Root);
_ = copies.DetectCopies();
CollectionAssert.DoesNotContain(copies.BuildArguments().ToArray(), "--no-renames");

Check warning on line 66 in GitIntegration.Test/Builders/GitDiffBuilderTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'Assert.DoesNotContain' instead of 'CollectionAssert.DoesNotContain'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_GitIntegration&issues=AaERAdBBQuutVwIrJcWk&open=AaERAdBBQuutVwIrJcWk&pullRequest=180
}

[TestMethod]
public void PutsASingleRevisionBehindTheEndOfOptionsMarker()
{
Expand Down
43 changes: 43 additions & 0 deletions GitIntegration.Test/Integration/GitPatchRoundTripTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,49 @@
"A patch read with no WithContext call must still apply, whatever diff.context the host sets.");
}

[TestMethod]
[DataRow("true")]
[DataRow("false")]
[DataRow("copies")]
public async Task DiffReportsAStagedRenameAsPatchDoesWhateverTheHostsDiffRenamesAsync(string diffRenames)
{
await IntegrationGitFixture.RequireGitAsync(TestContext.CancellationTokenSource.Token).ConfigureAwait(false);

Check warning on line 283 in GitIntegration.Test/Integration/GitPatchRoundTripTests.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=AaERAdEkQuutVwIrJcWr&open=AaERAdEkQuutVwIrJcWr&pullRequest=180

using TemporaryRepository repository = new();
GitClient client = IntegrationGitFixture.CreateClient();

GitRepository seeded = await SeedAsync(client, repository, [("diff.renames", diffRenames)]).ConfigureAwait(false);

repository.WriteFile("old.txt", "one\ntwo\nthree\nfour\nfive\n");
await CommitAllAsync(seeded).ConfigureAwait(false);

repository.DeleteFile("old.txt");
repository.WriteFile("new.txt", "one\ntwo\nthree\nfour\nfive\n");
_ = await seeded.Add().All().ExecuteAsync(TestContext.CancellationTokenSource.Token).ConfigureAwait(false);

Check warning on line 295 in GitIntegration.Test/Integration/GitPatchRoundTripTests.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=AaERAdEkQuutVwIrJcWs&open=AaERAdEkQuutVwIrJcWs&pullRequest=180

GitRepository opened = await client.OpenAsync(repository.Root).ConfigureAwait(false);

Check warning on line 297 in GitIntegration.Test/Integration/GitPatchRoundTripTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_GitIntegration&issues=AaERAdEkQuutVwIrJcWn&open=AaERAdEkQuutVwIrJcWn&pullRequest=180

// Without DetectRenames, Diff() must agree with Patch(): a delete and an add, never a rename or
// a copy — a staging UI pairs the file list from one with the hunks from the other.
IReadOnlyList<GitDiffEntry> plain = await opened.Diff().Staged().ExecuteAsync().ConfigureAwait(false);

Check warning on line 301 in GitIntegration.Test/Integration/GitPatchRoundTripTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_GitIntegration&issues=AaERAdEkQuutVwIrJcWo&open=AaERAdEkQuutVwIrJcWo&pullRequest=180
GitPatch patch = await opened.Patch().Staged().ExecuteAsync().ConfigureAwait(false);

Check warning on line 302 in GitIntegration.Test/Integration/GitPatchRoundTripTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_GitIntegration&issues=AaERAdEkQuutVwIrJcWp&open=AaERAdEkQuutVwIrJcWp&pullRequest=180

CollectionAssert.AreEquivalent(
patch.Files.Select(file => (file.Kind, file.Path.WeakString)).ToArray(),
plain.Select(entry => (entry.Kind, entry.Path.WeakString)).ToArray(),
$"Diff() and Patch() disagree with diff.renames={diffRenames}.");

Check warning on line 307 in GitIntegration.Test/Integration/GitPatchRoundTripTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'Assert.AreSequenceEqual' instead of 'CollectionAssert.AreEquivalent'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_GitIntegration&issues=AaERAdEkQuutVwIrJcWl&open=AaERAdEkQuutVwIrJcWl&pullRequest=180
CollectionAssert.AreEquivalent(
new[] { GitChangeKind.Added, GitChangeKind.Deleted },
plain.Select(entry => entry.Kind).ToArray());

Check warning on line 310 in GitIntegration.Test/Integration/GitPatchRoundTripTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'Assert.AreSequenceEqual' instead of 'CollectionAssert.AreEquivalent'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_GitIntegration&issues=AaERAdEkQuutVwIrJcWm&open=AaERAdEkQuutVwIrJcWm&pullRequest=180

IReadOnlyList<GitDiffEntry> detected =
await opened.Diff().Staged().DetectRenames().ExecuteAsync().ConfigureAwait(false);

Check warning on line 313 in GitIntegration.Test/Integration/GitPatchRoundTripTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_GitIntegration&issues=AaERAdEkQuutVwIrJcWq&open=AaERAdEkQuutVwIrJcWq&pullRequest=180
GitDiffEntry renamed = detected.Single();

Assert.AreEqual(GitChangeKind.Renamed, renamed.Kind);
Assert.AreEqual("old.txt", renamed.OriginalPath?.WeakString);
}

[TestMethod]
public async Task ReportsAStagedRenameAsDeleteAndAddUnlessRequestedAsync()
{
Expand Down
16 changes: 16 additions & 0 deletions GitIntegration/Builders/GitDiffBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,18 @@ public interface IGitDiffBuilder : IGitCommandBuilder<IReadOnlyList<GitDiffEntry
public IGitDiffBuilder Between(GitRefName fromRevision, GitRefName toRevision);

/// <summary>Reports a delete and an add of similar content as a rename.</summary>
/// <remarks>
/// Detection is off until this or <see cref="DetectCopies"/> is called, whatever
/// <c>diff.renames</c> the host has configured.
/// </remarks>
/// <returns>The same builder, to allow chaining.</returns>
public IGitDiffBuilder DetectRenames();

/// <summary>Reports an add whose content came from an existing file as a copy.</summary>
/// <remarks>
/// Copies are not reported until this is called, whatever <c>diff.renames</c> the host has
/// configured.
/// </remarks>
/// <returns>The same builder, to allow chaining.</returns>
public IGitDiffBuilder DetectCopies();

Expand Down Expand Up @@ -172,6 +180,9 @@ protected override void AppendVerbArguments(ICollection<string> arguments)
arguments.Add("--cached");
}

// Git has defaulted diff.renames to true since 2.9, so leaving detection unspecified hands the
// choice to the host's config: DetectRenames() becomes a no-op, diff.renames=copies reports
// copies nobody asked for, and Diff() disagrees with Patch(), which pins this the same way.
if (_detectRenames)
{
arguments.Add("--find-renames");
Expand All @@ -182,6 +193,11 @@ protected override void AppendVerbArguments(ICollection<string> arguments)
arguments.Add("--find-copies");
}

if (!_detectRenames && !_detectCopies)
{
arguments.Add("--no-renames");
}

if (_revisions.Length > 0)
{
AppendOperands(arguments, _revisions);
Expand Down
Loading