From e34ae5b3da37e77728fae6264ac53346a3c2d1b8 Mon Sep 17 00:00:00 2001 From: Jamie Cansdale Date: Wed, 19 Dec 2018 18:00:52 +0000 Subject: [PATCH 1/5] Add IndentHeuristic option to CompareOptions --- LibGit2Sharp/CompareOptions.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/LibGit2Sharp/CompareOptions.cs b/LibGit2Sharp/CompareOptions.cs index fbd147c79..fb4234439 100644 --- a/LibGit2Sharp/CompareOptions.cs +++ b/LibGit2Sharp/CompareOptions.cs @@ -44,5 +44,11 @@ public CompareOptions() /// By default, will be used. /// public DiffAlgorithm Algorithm { get; set; } + + /// + /// Enable --indent-heuristic Diff option, that attempts to produce more aesthetically pleasing diffs. + /// By default, this option will be false. + /// + public bool IndentHeuristic { get; set; } } } From 6a6f1345d8bcbbd560fd75098621336581777e46 Mon Sep 17 00:00:00 2001 From: Jamie Cansdale Date: Wed, 19 Dec 2018 18:02:20 +0000 Subject: [PATCH 2/5] Test for IndentHeuristic Diff option This test is based on example from: https://github.com/git/git/blob/433860f3d0beb0c6f205290bd16cda413148f098/t/t4061-diff-indent.sh#L17 --- LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs | 39 +++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs b/LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs index 00ef0ab2b..9173674b8 100644 --- a/LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs +++ b/LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs @@ -126,5 +126,44 @@ public void ComparingTwoNullBlobsReturnsAnEmptyContentChanges() Assert.Equal(0, changes.LinesDeleted); } } + + [Fact] + public void ComparingBlobsWithIndentHeuristicOptionMakesADifference() + { + var path = SandboxStandardTestRepoGitDir(); + using (var repo = new Repository(path)) + { + // Based on test diff indent heuristic from: + // https://github.com/git/git/blob/433860f3d0beb0c6f205290bd16cda413148f098/t/t4061-diff-indent.sh#L17 + var oldContent = +@" 1 + 2 + a + + b + 3 + 4"; + var newContent = +@" 1 + 2 + a + + b + a + + b + 3 + 4"; + var oldBlob = repo.ObjectDatabase.CreateBlob(new MemoryStream(Encoding.UTF8.GetBytes(oldContent))); + var newBlob = repo.ObjectDatabase.CreateBlob(new MemoryStream(Encoding.UTF8.GetBytes(newContent))); + var noIndentHeuristicOption = new CompareOptions { IndentHeuristic = false }; + var indentHeuristicOption = new CompareOptions { IndentHeuristic = true }; + + ContentChanges changes0 = repo.Diff.Compare(oldBlob, newBlob, noIndentHeuristicOption); + ContentChanges changes1 = repo.Diff.Compare(oldBlob, newBlob, indentHeuristicOption); + + Assert.NotEqual(changes0.Patch, changes1.Patch); + } + } } } From 98be00ae5269f468d25394986e0e42716294cf7a Mon Sep 17 00:00:00 2001 From: Jamie Cansdale Date: Wed, 19 Dec 2018 18:18:34 +0000 Subject: [PATCH 3/5] Take account of CompareOptions.IndentHeuristic When this flag is set, enable the GIT_DIFF_INDENT_HEURISTIC option. --- LibGit2Sharp/Core/GitDiff.cs | 15 +++++++++++---- LibGit2Sharp/Diff.cs | 7 ++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/LibGit2Sharp/Core/GitDiff.cs b/LibGit2Sharp/Core/GitDiff.cs index eb21d6881..956bc89ab 100644 --- a/LibGit2Sharp/Core/GitDiff.cs +++ b/LibGit2Sharp/Core/GitDiff.cs @@ -133,6 +133,13 @@ internal enum GitDiffOptionFlags * Options controlling how output will be generated */ + /// + /// Use a heuristic that takes indentation and whitespace into account + /// which generally can produce better diffs when dealing with ambiguous + /// diff hunks. + /// + GIT_DIFF_INDENT_HEURISTIC = (1 << 18), + /// /// Treat all files as text, disabling binary attributes and detection /// @@ -304,11 +311,11 @@ enum GitDiffLineOrigin : byte enum GitDiffFormat { - GIT_DIFF_FORMAT_PATCH = 1, // < full git diff + GIT_DIFF_FORMAT_PATCH = 1, // < full git diff GIT_DIFF_FORMAT_PATCH_HEADER = 2, // < just the file headers of patch - GIT_DIFF_FORMAT_RAW = 3, // < like git diff --raw - GIT_DIFF_FORMAT_NAME_ONLY = 4, // < like git diff --name-only - GIT_DIFF_FORMAT_NAME_STATUS = 5, // < like git diff --name-status + GIT_DIFF_FORMAT_RAW = 3, // < like git diff --raw + GIT_DIFF_FORMAT_NAME_ONLY = 4, // < like git diff --name-only + GIT_DIFF_FORMAT_NAME_STATUS = 5, // < like git diff --name-status } [Flags] diff --git a/LibGit2Sharp/Diff.cs b/LibGit2Sharp/Diff.cs index 9bf14660c..087ee8d6d 100644 --- a/LibGit2Sharp/Diff.cs +++ b/LibGit2Sharp/Diff.cs @@ -63,6 +63,11 @@ private static GitDiffOptions BuildOptions(DiffModifiers diffOptions, FilePath[] options.Flags |= GitDiffOptionFlags.GIT_DIFF_DISABLE_PATHSPEC_MATCH; } + if (compareOptions.IndentHeuristic) + { + options.Flags |= GitDiffOptionFlags.GIT_DIFF_INDENT_HEURISTIC; + } + if (matchedPathsAggregator != null) { options.NotifyCallback = matchedPathsAggregator.OnGitDiffNotify; @@ -351,7 +356,7 @@ public virtual T Compare(Tree oldTree, DiffTargets diffTargets, IEnumerable(diff); From e0636802a901a57ed9a6498b2ee3220cf64ac951 Mon Sep 17 00:00:00 2001 From: Jamie Cansdale Date: Wed, 19 Dec 2018 18:25:17 +0000 Subject: [PATCH 4/5] Test for when IndentHeuristic makes no difference When compared files don't contain spaces, the --indent-heuristic option won't make a difference. --- LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs | 37 ++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs b/LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs index 9173674b8..0107294af 100644 --- a/LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs +++ b/LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs @@ -128,7 +128,7 @@ public void ComparingTwoNullBlobsReturnsAnEmptyContentChanges() } [Fact] - public void ComparingBlobsWithIndentHeuristicOptionMakesADifference() + public void ComparingBlobsWithNoSpacesAndIndentHeuristicOptionMakesADifference() { var path = SandboxStandardTestRepoGitDir(); using (var repo = new Repository(path)) @@ -165,5 +165,40 @@ public void ComparingBlobsWithIndentHeuristicOptionMakesADifference() Assert.NotEqual(changes0.Patch, changes1.Patch); } } + + + [Fact] + public void ComparingBlobsWithNoSpacesIndentHeuristicOptionMakesNoDifference() + { + var path = SandboxStandardTestRepoGitDir(); + using (var repo = new Repository(path)) + { + var oldContent = +@" 1 + 2 + a + b + 3 + 4"; + var newContent = +@" 1 + 2 + a + b + a + b + 3 + 4"; + var oldBlob = repo.ObjectDatabase.CreateBlob(new MemoryStream(Encoding.UTF8.GetBytes(oldContent))); + var newBlob = repo.ObjectDatabase.CreateBlob(new MemoryStream(Encoding.UTF8.GetBytes(newContent))); + var noIndentHeuristicOption = new CompareOptions { IndentHeuristic = false }; + var indentHeuristicOption = new CompareOptions { IndentHeuristic = true }; + + ContentChanges changes0 = repo.Diff.Compare(oldBlob, newBlob, noIndentHeuristicOption); + ContentChanges changes1 = repo.Diff.Compare(oldBlob, newBlob, indentHeuristicOption); + + Assert.Equal(changes0.Patch, changes1.Patch); + } + } } } From 2f583cbf16dee63fcbcf16f0d6fcace8a81f3a2c Mon Sep 17 00:00:00 2001 From: Jamie Cansdale Date: Wed, 19 Dec 2018 23:25:34 +0000 Subject: [PATCH 5/5] Check the same lines have been added or removed The --indent-heuristic option should create patches that add or remove the same lines, but potentially in a different order. --- LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs b/LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs index 0107294af..fea0bbb74 100644 --- a/LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs +++ b/LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Linq; using System.Text; using LibGit2Sharp.Tests.TestHelpers; using Xunit; @@ -163,10 +164,10 @@ public void ComparingBlobsWithNoSpacesAndIndentHeuristicOptionMakesADifference() ContentChanges changes1 = repo.Diff.Compare(oldBlob, newBlob, indentHeuristicOption); Assert.NotEqual(changes0.Patch, changes1.Patch); + Assert.Equal(CanonicalChangedLines(changes0), CanonicalChangedLines(changes1)); } } - [Fact] public void ComparingBlobsWithNoSpacesIndentHeuristicOptionMakesNoDifference() { @@ -200,5 +201,11 @@ public void ComparingBlobsWithNoSpacesIndentHeuristicOptionMakesNoDifference() Assert.Equal(changes0.Patch, changes1.Patch); } } + + static string CanonicalChangedLines(ContentChanges changes) + { + // Create an ordered representation of lines that have been added or removed + return string.Join("\n", changes.Patch.Split('\n').Where(l => l.StartsWith("+") || l.StartsWith("-")).OrderBy(l => l)); + } } }