Skip to content

Add support for git diff --indent-heuristic option #1652

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 25, 2018
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
81 changes: 81 additions & 0 deletions LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using System.Linq;
using System.Text;
using LibGit2Sharp.Tests.TestHelpers;
using Xunit;
Expand Down Expand Up @@ -126,5 +127,85 @@ public void ComparingTwoNullBlobsReturnsAnEmptyContentChanges()
Assert.Equal(0, changes.LinesDeleted);
}
}

[Fact]
public void ComparingBlobsWithNoSpacesAndIndentHeuristicOptionMakesADifference()
{
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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we validate that changes0 is a superset of changes1?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or validate the results of the patch strings?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering about what exactly to validate in this case. The main thing I wanted to validate was that the flag was making its way to indent heuristic code. By checking when we expect the flag to make a difference and when we don't expect it to make a difference, I hope that is pretty much covered.

I've added a sanity check in 2f583cb to make sure that the same lines are being added or removed. I'm trusting the algorithm to add or remove them in the correct order. 🙂

Assert.Equal(CanonicalChangedLines(changes0), CanonicalChangedLines(changes1));
}
}

[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);
}
}

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));
}
}
}
6 changes: 6 additions & 0 deletions LibGit2Sharp/CompareOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,11 @@ public CompareOptions()
/// By default, <see cref="DiffAlgorithm.Myers"/> will be used.
/// </summary>
public DiffAlgorithm Algorithm { get; set; }

/// <summary>
/// Enable --indent-heuristic Diff option, that attempts to produce more aesthetically pleasing diffs.
/// By default, this option will be false.
/// </summary>
public bool IndentHeuristic { get; set; }
}
}
15 changes: 11 additions & 4 deletions LibGit2Sharp/Core/GitDiff.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ internal enum GitDiffOptionFlags
* Options controlling how output will be generated
*/

/// <summary>
/// Use a heuristic that takes indentation and whitespace into account
/// which generally can produce better diffs when dealing with ambiguous
/// diff hunks.
/// </summary>
GIT_DIFF_INDENT_HEURISTIC = (1 << 18),

/// <summary>
/// Treat all files as text, disabling binary attributes and detection
/// </summary>
Expand Down Expand Up @@ -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]
Expand Down
7 changes: 6 additions & 1 deletion LibGit2Sharp/Diff.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -351,7 +356,7 @@ public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<s
}

DiffHandle diff = BuildDiffList(oldTreeId, null, comparer, diffOptions, paths, explicitPathsOptions, compareOptions);

try
{
return BuildDiffResult<T>(diff);
Expand Down