8000 Add rename detection to tree comparisons by roend83 · Pull Request #278 · libgit2/libgit2sharp · GitHub
[go: up one dir, main page]

Skip to content

Add rename detection to tree comparisons #278

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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
8000 Prev Previous commit
Next Next commit
Refactored interface to enable rename and copy detection and added te…
…sts. Note that copy tests are currently failing.
  • Loading branch information
roend83 committed Feb 8, 2013
commit d868ca6b7596bc3a0f5140523c4165bef70b548b
149 changes: 148 additions & 1 deletion LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ public void CanCompareTwoVersionsOfAFileWithADiffOfTwoHunks()
Assert.Equal(expected.ToString(), changes.Patch);
}
}

[Fact]
public void CanCompareATreeAgainstANullTree()
{
Expand Down Expand Up @@ -436,6 +436,153 @@ public void ComparingReliesOnProvidedConfigEntriesIfAny()
}
}
8000
[Fact]
public void CanDetectTheRenamingOfNonModifiedFilesWhenEnabled()
{
SelfCleaningDirectory scd = BuildSelfCleaningDirectory();
using (var repo = Repository.Init(scd.DirectoryPath))
{
string originalPath = Path.Combine(repo.Info.WorkingDirectory, "original.txt");
string renamedPath = Path.Combine(repo.Info.WorkingDirectory, "renamed.txt");

File.WriteAllText(originalPath, "a\nb\nc\nd\n");

repo.Index.Stage(originalPath);

Commit old = repo.Commit("Initial", DummySignature, DummySignature);

repo.Index.Move(originalPath, renamedPath);

Commit @new = repo.Commit("Updated", DummySignature, DummySignature);

TreeChanges changes = repo.Diff.Compare(old.Tree, @new.Tree, detectRenames: true);

Assert.Equal(1, changes.Count());
Assert.Equal(1, changes.Renamed.Count());
Assert.Equal("original.txt", changes.Renamed.Single().OldPath);
Assert.Equal("renamed.txt", changes.Renamed.Single().Path);
}
}

[Fact]
public void CanNotDetectTheRenamingOfNonModifiedFilesWhenNotEnabled()
{
SelfCleaningDirectory scd = BuildSelfCleaningDirectory();
using (var repo = Repository.Init(scd.DirectoryPath))
{
string originalPath = Path.Combine(repo.Info.WorkingDirectory, "original.txt");
string renamedPath = Path.Combine(repo.Info.WorkingDirectory, "renamed.txt");

File.WriteAllText(originalPath, "a\nb\nc\nd\n");

repo.Index.Stage(originalPath);

Commit old = repo.Commit("Initial", DummySignature, DummySignature);

repo.Index.Move(originalPath, renamedPath);

Commit @new = repo.Commit("Updated", DummySignature, DummySignature);

TreeChanges changes = repo.Diff.Compare(old.Tree, @new.Tree, detectRenames: false);

Assert.Equal(2, changes.Count());
Assert.Equal(0, changes.Renamed.Count());
}
}

[Fact]
public void CanDetectTheCopyingOfNonModifiedFilesWhenEnabled()
{
SelfCleaningDirectory scd = BuildSelfCleaningDirectory();
using (var repo = Repository.Init(scd.DirectoryPath))
{
string originalPath = Path.Combine(repo.Info.WorkingDirectory, "original.txt");
string copiedPath = Path.Combine(repo.Info.WorkingDirectory, "copied.txt");

File.WriteAllText(originalPath, "a\nb\nc\nd\n");

repo.Index.Stage(originalPath);

Commit old = repo.Commit("Initial", DummySignature, DummySignature);

File.Copy(originalPath, copiedPath);
repo.Index.Stage(copiedPath);

Commit @new = repo.Commit("Updated", DummySignature, DummySignature);

TreeChanges changes = repo.Diff.Compare(old.Tree, @new.Tree, detectCopies: true);

Assert.Equal(1, changes.Count());
Assert.Equal(1, changes.Copied.Count());
Assert.Equal("original.txt", changes.Copied.Single().OldPath);
Assert.Equal("copied.txt", changes.Copied.Single().Path);
}
}

[Fact]
public void CanNotDetectTheCopyingOfNonModifiedFilesWhenNotEnabled()
{
SelfCleaningDirectory scd = BuildSelfCleaningDirectory();
using (var repo = Repository.Init(scd.DirectoryPath))
{
string originalPath = Path.Combine(repo.Info.WorkingDirectory, "original.txt");
string copiedPath = Path.Combine(repo.Info.WorkingDirectory, "copied.txt");

File.WriteAllText(originalPath, "a\nb\nc\nd\n");

repo.Index.Stage(originalPath);

Commit old = repo.Commit("Initial", DummySignature, DummySignature);

File.Copy(originalPath, copiedPath);
repo.Index.Stage(copiedPath);

Commit @new = repo.Commit("Updated", DummySignature, DummySignature);

TreeChanges changes = repo.Diff.Compare(old.Tree, @new.Tree, detectCopies: false);

Assert.Equal(1, changes.Count());
Assert.Equal(0, changes.Copied.Count());
}
}

[Fact]
public void CanDetectTheRenamingAndCopyingOfNonModifiedFilesWhenEnabled()
{
SelfCleaningDirectory scd = BuildSelfCleaningDirectory();
using (var repo = Repository.Init(scd.DirectoryPath))
{
string originalPath = Path.Combine(repo.Info.WorkingDirectory, "original.txt");
string renamedPath = Path.Combine(repo.Info.WorkingDirectory, "renamed.txt");
string otherOriginalPath = Path.Combine(repo.Info.WorkingDirectory, "original2.txt");
string copiedPath = Path.Combine(repo.Info.WorkingDirectory, "copied.txt");

File.WriteAllText(originalPath, "a\nb\nc\nd\n");
File.WriteAllText(otherOriginalPath, "1\n2\n3\n4\n");

repo.Index.Stage(originalPath);
repo.Index.Stage(otherOriginalPath);

Commit old = repo.Commit("Initial", DummySignature, DummySignature);

File.Copy(otherOriginalPath, copiedPath);
repo.Index.Stage(copiedPath);
repo.Index.Move(originalPath, renamedPath);

Commit @new = repo.Commit("Updated", DummySignature, DummySignature);

TreeChanges changes = repo.Diff.Compare(old.Tree, @new.Tree, detectRenames: true, detectCopies: true);

Assert.Equal(2, changes.Count());
Assert.Equal(1, changes.Renamed.Count());
Assert.Equal("original.txt", changes.Renamed.Single().OldPath);
Assert.Equal("renamed.txt", changes.Renamed.Single().Path);
Assert.Equal(1, changes.Copied.Count());
Assert.Equal("original2.txt", changes.Copied.Single().OldPath);
Assert.Equal("copied.txt", changes.Copied.Single().Path);
}
}

private RepositoryOptions BuildFakeSystemConfigFilemodeOption(
SelfCleaningDirectory scd,
bool value)
Expand Down
51 changes: 37 additions & 14 deletions LibGit2Sharp/Diff.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,32 +92,55 @@ internal Diff(Repository repo)
}

/// <summary>
/// Show changes between two <see cref = "Tree"/>s.
/// Show changes between two <see cref="Tree" />s.
/// </summary>
/// <param name = "oldTree">The <see cref = "Tree"/> you want to compare from.</param>
/// <param name = "newTree">The <see cref = "Tree"/> you want to compare to.</param>
/// <param name = "paths">The list of paths (either files or directories) that should be compared.</param>
/// <returns>A <see cref = "TreeChanges"/> containing the changes between the <paramref name = "oldTree"/> and the <paramref name = "newTree"/>.</returns>
public virtual TreeChanges Compare(Tree oldTree, Tree newTree, IEnumerable<string> paths = null)
/// <param name="oldTree">The <see cref="Tree" /> you want to compare from.</param>
/// <param name="newTree">The <see cref="Tree" /> you want to compare to.</param>
/// <param name="paths">The list of paths (either files or directories) that should be compared.</param>
/// <param name="detectRenames">if set to <c>true</c> renames will be detected in the diff.</param>
/// <param name="detectCopies">if set to <c>true</c> copies will be detected in the diff.</param>
/// <returns>
/// A <see cref="TreeChanges" /> containing the changes between the <paramref name="oldTree" /> and the <paramref name="newTree" />.
/// </returns>
public virtual TreeChanges Compare(Tree oldTree, Tree newTree, IEnumerable<string> paths = null, bool detectRenames = false, bool detectCopies = false)
{
using(GitDiffOptions options = BuildOptions(DiffOptions.None, paths))
using (DiffListSafeHandle diff = BuildDiffListFromTrees(
oldTree != null ? oldTree.Id : null,
newTree != null ? newTree.Id : null,
options))
options,
detectRenames,
detectCopies))
{
Proxy.git_diff_find_similar(diff, new GitDiffFindOptions
{
Flags = GitDiffFindOptionFlags.GIT_DIFF_FIND_RENAMES
});

return new TreeChanges(diff);
}
}

private DiffListSafeHandle BuildDiffListFromTrees(ObjectId oldTree, ObjectId newTree, GitDiffOptions options)
private DiffListSafeHandle BuildDiffListFromTrees(ObjectId oldTree, ObjectId newTree, GitDiffOptions options, bool detectRenames, bool detectCopies)
{
return Proxy.git_diff_tree_to_tree(repo.Handle, oldTree, newTree, options);
var diff = Proxy.git_diff_tree_to_tree(repo.Handle, 77E6 oldTree, newTree, options);
return HandleRenameAndCopyDetection(diff, detectRenames, detectCopies);
}

private DiffListSafeHandle HandleRenameAndCopyDetection(DiffListSafeHandle diff, bool detectRenames, bool detectCopies)
{
if (detectRenames || detectCopies)
{
var diffFindOptions = new GitDiffFindOptions();

if (!detectCopies)
{
diffFindOptions.Flags = GitDiffFindOptionFlags.GIT_DIFF_FIND_RENAMES;
}
else if (!detectRenames)
{
diffFindOptions.Flags = GitDiffFindOptionFlags.GIT_DIFF_FIND_COPIES;
}

Proxy.git_diff_find_similar(diff, diffFindOptions);
}

return diff;
}

/// <summary>
Expand Down
0