using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using LibGit2Sharp.Core;
using LibGit2Sharp.Core.Handles;
namespace LibGit2Sharp
{
///
/// A log of commits in a
///
public class CommitLog : IQueryableCommitLog
{
private readonly Repository repo;
private readonly Filter queryFilter;
///
/// Needed for mocking purposes.
///
protected CommitLog()
{ }
///
/// Initializes a new instance of the class.
/// The commits will be enumerated according in reverse chronological order.
///
/// The repository.
internal CommitLog(Repository repo)
: this(repo, new Filter())
{
}
///
/// Initializes a new instance of the class.
///
/// The repository.
/// The filter to use in querying commits
internal CommitLog(Repository repo, Filter queryFilter)
{
this.repo = repo;
this.queryFilter = queryFilter;
}
///
/// Gets the current sorting strategy applied when enumerating the log
///
public virtual GitSortOptions SortedBy
{
get { return queryFilter.SortBy; }
}
#region IEnumerable Members
///
/// Returns an enumerator that iterates through the log.
///
/// An object that can be used to iterate through the log.
public virtual IEnumerator GetEnumerator()
{
return new CommitEnumerator(repo, queryFilter);
}
///
/// Returns an enumerator that iterates through the log.
///
/// An object that can be used to iterate through the log.
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
///
/// Returns the list of commits of the repository matching the specified .
///
/// The options used to control which commits will be returned.
/// A list of commits, ready to be enumerated.
public virtual 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);
}
///
/// Find the best possible common ancestor given two s.
///
/// The first .
/// The second .
/// The common ancestor or null if none found.
public virtual Commit FindCommonAncestor(Commit first, Commit second)
{
Ensure.ArgumentNotNull(first, "first");
Ensure.ArgumentNotNull(second, "second");
ObjectId id = Proxy.git_merge_base(repo.Handle, first, second);
return id == null ? null : repo.Lookup(id);
}
///
/// Find the best possible common ancestor given two or more .
///
/// The s for which to find the common ancestor.
/// The common ancestor or null if none found.
public virtual Commit FindCommonAncestor(IEnumerable commits)
{
Ensure.ArgumentNotNull(commits, "commits");
Commit ret = null;
int count = 0;
foreach (var commit in commits)
{
if (commit == null)
{
throw new ArgumentException("Enumerable contains null at position: " + count.ToString(CultureInfo.InvariantCulture), "commits");
}
count++;
if (count == 1)
{
ret = commit;
continue;
}
ret = FindCommonAncestor(ret, commit);
if (ret == null)
{
break;
}
}
if (count < 2)
{
throw new ArgumentException("The enumerable must contains at least two commits.", "commits");
}
return ret;
}
private class CommitEnumerator : IEnumerator
{
private readonly Repository repo;
private readonly RevWalkerSafeHandle handle;
private ObjectId currentOid;
public CommitEnumerator(Repository repo, Filter filter)
{
this.repo = repo;
handle = Proxy.git_revwalk_new(repo.Handle);
repo.RegisterForCleanup(handle);
Sort(filter.SortBy);
Push(filter.SinceList);
Hide(filter.UntilList);
}
#region IEnumerator Members
public Commit Current
{
get { return repo.Lookup(currentOid); }
}
object IEnumerator.Current
{
get { return Current; }
}
public bool MoveNext()
{
ObjectId id = Proxy.git_revwalk_next(handle);
if (id == null)
{
return false;
}
currentOid = id;
return true;
}
public void Reset()
{
Proxy.git_revwalk_reset(handle);
}
#endregion
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
handle.SafeDispose();
}
private delegate void HidePushSignature(RevWalkerSafeHandle handle, ObjectId id);
private void InternalHidePush(IList