Skip to content
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

Add revwalk globs #184

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 22 additions & 3 deletions LibGit2Sharp.Tests/CommitFixture.cs
Expand Up @@ -98,7 +98,6 @@ public void QueryingTheCommitHistoryWithUnknownShaOrInvalidEntryPointThrows()
{
Assert.Throws<LibGit2SharpException>(() => repo.Commits.QueryBy(new Filter { Since = Constants.UnknownSha }).Count());
Assert.Throws<LibGit2SharpException>(() => repo.Commits.QueryBy(new Filter { Since = "refs/heads/deadbeef" }).Count());
Assert.Throws<ArgumentNullException>(() => repo.Commits.QueryBy(new Filter { Since = null }).Count());
}
Copy link
Member

Choose a reason for hiding this comment

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

Why removing this? What should the following statements be expected to return?

  • repo.Commits.QueryBy(new Filter { Since = null })
  • repo.Commits.QueryBy(new Filter { Since = string.Empty })

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch - see aec0563

}

Expand All @@ -120,8 +119,6 @@ public void QueryingTheCommitHistoryWithBadParamsThrows()
{
using (var repo = new Repository(BareTestRepoPath))
{
Assert.Throws<ArgumentException>(() => repo.Commits.QueryBy(new Filter { Since = string.Empty }));
Assert.Throws<ArgumentNullException>(() => repo.Commits.QueryBy(new Filter { Since = null }));
Assert.Throws<ArgumentNullException>(() => repo.Commits.QueryBy(null));
}
}
Expand Down Expand Up @@ -289,6 +286,28 @@ public void CanEnumerateCommitsFromMixedStartingPoints()
});
}

[Fact]
public void CanEnumerateCommitsUsingGlob()
{
AssertEnumerationOfCommits(
repo => new Filter { SinceGlob = "heads" },
new[]
{
"4c062a6", "e90810b", "6dcf9bf", "a4a7dce", "be3563a", "c47800c", "9fd738e", "4a202b3", "41bc8c6", "5001298", "5b5b025", "8496071"
});
}

[Fact]
public void CanHideCommitsUsingGlob()
{
AssertEnumerationOfCommits(
repo => new Filter { Since = "refs/heads/packed-test", UntilGlob = "packed" },
new[]
{
"4a202b3", "5b5b025", "8496071"
});
}

[Fact]
public void CanEnumerateCommitsFromAnAnnotatedTag()
{
Expand Down
71 changes: 23 additions & 48 deletions LibGit2Sharp/CommitLog.cs
Expand Up @@ -14,37 +14,35 @@ namespace LibGit2Sharp
public class CommitLog : IQueryableCommitLog
{
private readonly Repository repo;
private IList<object> includedIdentifier = new List<object> { "HEAD" };
private IList<object> excludedIdentifier = new List<object>();
private readonly GitSortOptions sortOptions;
readonly Filter queryFilter;

/// <summary>
/// Initializes a new instance of the <see cref = "CommitLog" /> class.
/// The commits will be enumerated according in reverse chronological order.
/// </summary>
/// <param name = "repo">The repository.</param>
internal CommitLog(Repository repo)
: this(repo, GitSortOptions.Time)
: this(repo, new Filter())
{
}

/// <summary>
/// Initializes a new instance of the <see cref = "CommitLog" /> class.
/// </summary>
/// <param name = "repo">The repository.</param>
/// <param name = "sortingStrategy">The sorting strategy which should be applied when enumerating the commits.</param>
internal CommitLog(Repository repo, GitSortOptions sortingStrategy)
/// <param name="queryFilter">The filter to use in querying commits</param>
internal CommitLog(Repository repo, Filter queryFilter)
{
this.repo = repo;
sortOptions = sortingStrategy;
this.queryFilter = queryFilter;
}

/// <summary>
/// Gets the current sorting strategy applied when enumerating the log
/// </summary>
public GitSortOptions SortedBy
{
get { return sortOptions; }
get { return queryFilter.SortBy; }
}

#region IEnumerable<Commit> Members
Expand All @@ -55,12 +53,12 @@ public GitSortOptions SortedBy
/// <returns>An <see cref = "IEnumerator{T}" /> object that can be used to iterate through the log.</returns>
public IEnumerator<Commit> GetEnumerator()
{
if ((repo.Info.IsEmpty) && includedIdentifier.Any(o => PointsAtTheHead(o.ToString()))) // TODO: ToString() == fragile
if ((repo.Info.IsEmpty) && queryFilter.SinceList.Any(o => PointsAtTheHead(o.ToString()))) // TODO: ToString() == fragile
{
return Enumerable.Empty<Commit>().GetEnumerator();
}

return new CommitEnumerator(repo, includedIdentifier, excludedIdentifier, sortOptions);
return new CommitEnumerator(repo, queryFilter);
}

/// <summary>
Expand All @@ -82,41 +80,8 @@ IEnumerator IEnumerable.GetEnumerator()
public ICommitLog QueryBy(Filter filter)
{
Ensure.ArgumentNotNull(filter, "filter");
Ensure.ArgumentNotNull(filter.Since, "filter.Since");
Ensure.ArgumentNotNullOrEmptyString(filter.Since.ToString(), "filter.Since");

return new CommitLog(repo, filter.SortBy)
{
includedIdentifier = ToList(filter.Since),
excludedIdentifier = ToList(filter.Until)
};
}

private static IList<object> ToList(object obj)
{
var list = new List<object>();

if (obj == null)
{
return list;
}

var types = new[]
{
typeof(string), typeof(ObjectId),
typeof(Commit), typeof(TagAnnotation),
typeof(Tag), typeof(Branch), typeof(DetachedHead),
typeof(Reference), typeof(DirectReference), typeof(SymbolicReference)
};

if (types.Contains(obj.GetType()))
{
list.Add(obj);
return list;
}

list.AddRange(((IEnumerable)obj).Cast<object>());
return list;
return new CommitLog(repo, filter);
}

private static bool PointsAtTheHead(string shaOrRefName)
Expand Down Expand Up @@ -215,17 +180,27 @@ private class CommitEnumerator : IEnumerator<Commit>
private readonly RevWalkerSafeHandle handle;
private ObjectId currentOid;

public CommitEnumerator(Repository repo, IList<object> includedIdentifier, IList<object> excludedIdentifier, GitSortOptions sortingStrategy)
public CommitEnumerator(Repository repo, Filter filter)
{
this.repo = repo;
int res = NativeMethods.git_revwalk_new(out handle, repo.Handle);
repo.RegisterForCleanup(handle);

Ensure.Success(res);

Sort(sortingStrategy);
Push(includedIdentifier);
Hide(excludedIdentifier);
Sort(filter.SortBy);
Push(filter.SinceList);
Hide(filter.UntilList);

if(!string.IsNullOrEmpty(filter.SinceGlob))
{
Ensure.Success(NativeMethods.git_revwalk_push_glob(handle, filter.SinceGlob));
}

if(!string.IsNullOrEmpty(filter.UntilGlob))
{
Ensure.Success(NativeMethods.git_revwalk_hide_glob(handle, filter.UntilGlob));
}
}

#region IEnumerator<Commit> Members
Expand Down
6 changes: 6 additions & 0 deletions LibGit2Sharp/Core/NativeMethods.cs
Expand Up @@ -584,6 +584,9 @@ public static bool RepositoryStateChecker(RepositorySafeHandle repositoryPtr, Fu
[DllImport(libgit2)]
public static extern int git_revwalk_hide(RevWalkerSafeHandle walker, ref GitOid oid);

[DllImport(libgit2)]
public static extern int git_revwalk_hide_glob(RevWalkerSafeHandle walker, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string glob);

[DllImport(libgit2)]
public static extern int git_revwalk_new(out RevWalkerSafeHandle walker, RepositorySafeHandle repo);

Expand All @@ -593,6 +596,9 @@ public static bool RepositoryStateChecker(RepositorySafeHandle repositoryPtr, Fu
[DllImport(libgit2)]
public static extern int git_revwalk_push(RevWalkerSafeHandle walker, ref GitOid oid);

[DllImport(libgit2)]
public static extern int git_revwalk_push_glob(RevWalkerSafeHandle walker, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string glob);

[DllImport(libgit2)]
public static extern void git_revwalk_reset(RevWalkerSafeHandle walker);

Expand Down
69 changes: 68 additions & 1 deletion LibGit2Sharp/Filter.cs
@@ -1,10 +1,17 @@
namespace LibGit2Sharp
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace LibGit2Sharp
{
/// <summary>
/// Criterias used to filter out and order the commits of the repository when querying its history.
/// </summary>
public class Filter
{
IList<object> sinceList;
IList<object> untilList;

/// <summary>
/// Initializes a new instance of <see cref = "Filter" />.
/// </summary>
Expand Down Expand Up @@ -33,6 +40,23 @@ public Filter()
/// </summary>
public object Since { get; set; }


/// <summary>
/// Return a parsed list of Since objects.
/// </summary>
public IList<object> SinceList
{
get
{
return sinceList ?? (sinceList = ToList(Since));
}
}

/// <summary>
/// A string glob to using as a starting point for the revwalk.
/// </summary>
public string SinceGlob { get; set; }

/// <summary>
/// A pointer to a commit object or a list of pointers which will be excluded (along with ancestors) from the enumeration.
/// <para>
Expand All @@ -42,5 +66,48 @@ public Filter()
/// </para>
/// </summary>
public object Until { get; set; }

/// <summary>
/// Return a parsed list of Until objects.
/// </summary>
public IList<object> UntilList
{
get
{
return untilList ?? (untilList = ToList(Until));
}
}

/// <summary>
/// A string glob to hide from the revwalk.
/// </summary>
public string UntilGlob { get; set; }

static IList<object> ToList(object obj)
{
var list = new List<object>();

if (obj == null)
{
return list;
}

var types = new[]
{
typeof(string), typeof(ObjectId),
typeof(Commit), typeof(TagAnnotation),
typeof(Tag), typeof(Branch), typeof(DetachedHead),
typeof(Reference), typeof(DirectReference), typeof(SymbolicReference)
};

if (types.Contains(obj.GetType()))
{
list.Add(obj);
return list;
}

list.AddRange(((IEnumerable)obj).Cast<object>());
return list;
}
}
}