Skip to content

Exposed git_merge_base as CommitCollection.GetCommonAncestor(...) #151

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 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
Binary file modified Lib/NativeBinaries/x86/git2.dll
Binary file not shown.
Binary file modified Lib/NativeBinaries/x86/git2.pdb
Binary file not shown.
57 changes: 57 additions & 0 deletions LibGit2Sharp.Tests/CommitFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,63 @@ public void CanCorrectlyCountCommitsWhenSwitchingToAnotherBranch()
}
}

/* BareTestRepoPath structure

* commit 4c062a6361ae6959e06292c1fa5e2822d9c96345
|
* commit be3563ae3f795b2b4353bcce3a527ad0a4f7f644
|\
| |
| * commit c47800c7266a2be04c571c04d5a6614691ea99bd
| |
* | commit 9fd738e8f7967c078dceed8190330fc8648ee56a
| |
* | commit 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
|/
|
* commit 5b5b025afb0b4c913b4c338a42934a3863bf3644
|
* commit 8496071c1b46c854b31185ea97743be6a877447

*/
[Fact]
public void CanCorrectlyGetCommonAncestorForTwoCommits()
{
using (var repo = new Repository(BareTestRepoPath))
{
Commit first = repo.Lookup<Commit>("c47800c7266a2be04c571c04d5a6614691ea99bd");

Commit second = repo.Lookup<Commit>("9fd738e8f7967c078dceed8190330fc8648ee56a");

Commit ancestor = repo.Commits.GetCommonAncestor(first, second);

Assert.NotNull(ancestor);

ancestor.Id.Sha.ShouldEqual("5b5b025afb0b4c913b4c338a42934a3863bf3644");
}
}

[Fact]
public void CanCorrectlyGetCommonAncestorForSeveralCommits()
{
using (var repo = new Repository(BareTestRepoPath))
{
Commit first = repo.Lookup<Commit>("4c062a6361ae6959e06292c1fa5e2822d9c96345");

Commit second = repo.Lookup<Commit>("be3563ae3f795b2b4353bcce3a527ad0a4f7f644");

Commit third = repo.Lookup<Commit>("c47800c7266a2be04c571c04d5a6614691ea99bd");

Commit fourth = repo.Lookup<Commit>("5b5b025afb0b4c913b4c338a42934a3863bf3644");

Commit ancestor = repo.Commits.GetCommonAncestor(first, second, third, fourth);

Assert.NotNull(ancestor);

ancestor.Id.Sha.ShouldEqual("5b5b025afb0b4c913b4c338a42934a3863bf3644");
}
}

[Fact]
public void CanEnumerateCommits()
{
Expand Down
43 changes: 43 additions & 0 deletions LibGit2Sharp/CommitCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ IEnumerator IEnumerable.GetEnumerator()

#endregion


/// <summary>
/// Returns the list of commits of the repository matching the specified <paramref name = "filter" />.
/// </summary>
Expand Down Expand Up @@ -124,6 +125,48 @@ private static bool PointsAtTheHead(string shaOrRefName)
return ("HEAD".Equals(shaOrRefName, StringComparison.Ordinal) || "refs/heads/master".Equals(shaOrRefName, StringComparison.Ordinal));
}

/// <summary>
/// Find as good common ancestors as possible for a merge given second <see cref="Commit"/>.
/// </summary>
/// <param name="first">The first <see cref="Commit"/> for which to find the common ancestor.</param>
/// <param name="second">The second <see cref="Commit"/> for which to find the common ancestor.</param>
/// <returns>The common ancestor or null if none found.</returns>
public Commit GetCommonAncestor(Commit first, Commit second)
{
Ensure.ArgumentNotNull(first, "first");
Ensure.ArgumentNotNull(second, "second");
GitOid ret;
using (var osw1 = new ObjectSafeWrapper(first.Id, this.repo))
using (var osw2 = new ObjectSafeWrapper(second.Id, this.repo))
{
Ensure.Success(NativeMethods.git_merge_base(out ret, this.repo.Handle, osw1.ObjectPtr, osw2.ObjectPtr));
return this.repo.Lookup<Commit>(new ObjectId(ret));
}
}

/// <summary>
/// Find as good common ancestors as possible for a merge given second or more <see cref="Commit"/>.
/// </summary>
/// <param name="commits">The <see cref="Commit"/> for which to find the common ancestor.</param>
/// <returns>The common ancestor or null if none found.</returns>
public Commit GetCommonAncestor(IEnumerable<Commit> commits)
{
Ensure.ArgumentNotNull(commits, "commits");

return commits.Aggregate(this.GetCommonAncestor);
}

/// <summary>
/// Find as good common ancestors as possible for a merge given second or more <see cref="Commit"/>.
/// </summary>
/// <param name="commits">The <see cref="Commit"/> for which to find the common ancestor.</param>
/// <returns>The common ancestor or null if none found.</returns>
public Commit GetCommonAncestor(params Commit[] commits)
{
return this.GetCommonAncestor((IEnumerable<Commit>)commits);
}


/// <summary>
/// Stores the content of the <see cref = "Repository.Index" /> as a new <see cref = "Commit" /> into the repository.
/// The tip of the <see cref = "Repository.Head"/> will be used as the parent of this new Commit.
Expand Down
9 changes: 6 additions & 3 deletions LibGit2Sharp/Core/Ensure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ namespace LibGit2Sharp.Core
/// <summary>
/// Ensure input parameters
/// </summary>
[DebuggerStepThrough]
//[DebuggerStepThrough]
internal static class Ensure
{
private static readonly Utf8Marshaler marshaler = (Utf8Marshaler)Utf8Marshaler.GetInstance(string.Empty);
/// <summary>
/// Checks an argument to ensure it isn't null.
/// </summary>
Expand Down Expand Up @@ -62,10 +63,12 @@ public static void Success(int result, bool allowPositiveResult = false)
return;
}

string errorMessage = NativeMethods.git_lasterror();
GitError error = NativeMethods.giterr_last();

string errorMsg= "";// = (string)marshaler.MarshalNativeToManaged(error.Message);

throw new LibGit2Exception(
String.Format(CultureInfo.InvariantCulture, "An error was raised by libgit2. Error code = {0} ({1}).{2}{3}", Enum.GetName(typeof(GitErrorCode), result), result, Environment.NewLine, errorMessage));
String.Format(CultureInfo.InvariantCulture, "An error was raised by libgit2. Error code = {0} ({1}).{2}{3}", Enum.GetName(typeof(GitErrorCode), result), result, Environment.NewLine, errorMsg));
}

/// <summary>
Expand Down
13 changes: 13 additions & 0 deletions LibGit2Sharp/Core/GitError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Runtime.InteropServices;
using System.Text;

namespace LibGit2Sharp.Core
{
[StructLayout(LayoutKind.Sequential)]
internal struct GitError
{
public IntPtr Message;
public int Klass;
}
}
130 changes: 11 additions & 119 deletions LibGit2Sharp/Core/GitErrorCode.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
namespace LibGit2Sharp.Core
{


internal enum GitErrorCode
{
/// <summary>
Expand All @@ -15,111 +17,11 @@ internal enum GitErrorCode
/// </summary>
GIT_ERROR = -1,

/// <summary>
/// Input was not a properly formatted Git object id.
/// </summary>
GIT_ENOTOID = -2,

/// <summary>
/// Input does not exist in the scope searched.
/// </summary>
GIT_ENOTFOUND = -3,

/// <summary>
/// Not enough space available.
/// </summary>
GIT_ENOMEM = -4,

/// <summary>
/// Consult the OS error information.
/// </summary>
GIT_EOSERR = -5,

/// <summary>
/// The specified object is of invalid type
/// </summary>
GIT_EOBJTYPE = -6,

/// <summary>
/// The specified repository is invalid
/// </summary>
GIT_ENOTAREPO = -7,

/// <summary>
/// The object type is invalid or doesn't match
/// </summary>
GIT_EINVALIDTYPE = -8,

/// <summary>
/// The object cannot be written that because it's missing internal data
/// </summary>
GIT_EMISSINGOBJDATA = -9,

/// <summary>
/// The packfile for the ODB is corrupted
/// </summary>
GIT_EPACKCORRUPTED = -10,

/// <summary>
/// Failed to adquire or release a file lock
/// </summary>
GIT_EFLOCKFAIL = -11,

/// <summary>
/// The Z library failed to inflate/deflate an object's data
/// </summary>
GIT_EZLIB = -12,

/// <summary>
/// The queried object is currently busy
/// </summary>
GIT_EBUSY = -13,

/// <summary>
/// The index file is not backed up by an existing repository
/// </summary>
GIT_EBAREINDEX = -14,

/// <summary>
/// The name of the reference is not valid
/// </summary>
GIT_EINVALIDREFNAME = -15,

/// <summary>
/// The specified reference has its data corrupted
/// </summary>
GIT_EREFCORRUPTED = -16,

/// <summary>
/// The specified symbolic reference is too deeply nested
/// </summary>
GIT_ETOONESTEDSYMREF = -17,

/// <summary>
/// The pack-refs file is either corrupted of its format is not currently supported
/// </summary>
GIT_EPACKEDREFSCORRUPTED = -18,

/// <summary>
/// The path is invalid
/// </summary>
GIT_EINVALIDPATH = -19,

/// <summary>
/// The revision walker is empty; there are no more commits left to iterate
/// </summary>
GIT_EREVWALKOVER = -20,

/// <summary>
/// The state of the reference is not valid
/// </summary>
GIT_EINVALIDREFSTATE = -21,

/// <summary>
/// This feature has not been implemented yet
/// </summary>
GIT_ENOTIMPLEMENTED = -22,

/// <summary>
/// A reference with this name already exists
/// </summary>
Expand All @@ -131,33 +33,23 @@ internal enum GitErrorCode
GIT_EOVERFLOW = -24,

/// <summary>
/// The given literal is not a valid number
/// </summary>
GIT_ENOTNUM = -25,

/// <summary>
/// Streaming error
/// </summary>
GIT_ESTREAM = -26,

/// <summary>
/// invalid arguments to function
/// The given short oid is ambiguous
/// </summary>
GIT_EINVALIDARGS = -27,
GIT_EAMBIGUOUS = -29,

/// <summary>
/// The specified object has its data corrupted
/// Skip and passthrough the given ODB backend
/// </summary>
GIT_EOBJCORRUPTED = -28,

GIT_EPASSTHROUGH = -30,
/// <summary>
/// The given short oid is ambiguous
/// The buffer is too short to satisfy the request
/// </summary>
GIT_EAMBIGUOUSOIDPREFIX = -29,
GIT_ESHORTBUFFER = -32,

/// <summary>
/// Skip and passthrough the given ODB backend
/// The revision walker is empty; there are no more commits left to iterate
/// </summary>
GIT_EPASSTHROUGH = -30,
GIT_EREVWALKOVER = -33,
}
}
10 changes: 8 additions & 2 deletions LibGit2Sharp/Core/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ public static bool RepositoryStateChecker(RepositorySafeHandle repositoryPtr, Fu
return (res == 1);
}

[DllImport(libgit2)]
public static extern GitError giterr_last();

[DllImport(libgit2)]
public static extern int git_blob_create_fromfile(
ref GitOid oid,
Expand Down Expand Up @@ -302,8 +305,11 @@ public static extern int git_index_find(
public static extern int git_index_write(IndexSafeHandle index);

[DllImport(libgit2)]
[return : MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]
public static extern string git_lasterror();
public static extern int git_merge_base(
out GitOid mergeBase,
RepositorySafeHandle repo,
GitObjectSafeHandle one,
GitObjectSafeHandle two);

[DllImport(libgit2)]
public static extern int git_odb_exists(ObjectDatabaseSafeHandle odb, ref GitOid id);
Expand Down
27 changes: 26 additions & 1 deletion LibGit2Sharp/IQueryableCommitCollection.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace LibGit2Sharp
using System.Collections.Generic;

namespace LibGit2Sharp
{
public interface IQueryableCommitCollection : ICommitCollection //TODO: Find a name that's more explicit than IQueryableCommitCollection
{
Expand All @@ -20,5 +22,28 @@ public interface IQueryableCommitCollection : ICommitCollection //TODO: Find a n
/// <param name = "amendPreviousCommit">True to amend the current <see cref = "Commit"/> pointed at by <see cref = "Repository.Head"/>, false otherwise.</param>
/// <returns>The generated <see cref = "Commit" />.</returns>
Commit Create(string message, Signature author, Signature committer, bool amendPreviousCommit);


/// <summary>
/// Find as good common ancestors as possible for a merge given second <see cref="Commit"/>.
/// </summary>
/// <param name="first">The first <see cref="Commit"/> for which to find the common ancestor.</param>
/// <param name="second">The second <see cref="Commit"/> for which to find the common ancestor.</param>
/// <returns>The common ancestor or null if none found.</returns>
Commit GetCommonAncestor(Commit first, Commit second);

/// <summary>
/// Find as good common ancestors as possible for a merge given second or more <see cref="Commit"/>.
/// </summary>
/// <param name="commits">The <see cref="Commit"/> for which to find the common ancestor.</param>
/// <returns>The common ancestor or null if none found.</returns>
Commit GetCommonAncestor(IEnumerable<Commit> commits);

/// <summary>
/// Find as good common ancestors as possible for a merge given second or more <see cref="Commit"/>.
/// </summary>
/// <param name="commits">The <see cref="Commit"/> for which to find the common ancestor.</param>
/// <returns>The common ancestor or null if none found.</returns>
Commit GetCommonAncestor(params Commit[] commits);
}
}
1 change: 1 addition & 0 deletions LibGit2Sharp/LibGit2Sharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
<Compile Include="Core\EnumExtensions.cs" />
<Compile Include="ChangeKind.cs" />
<Compile Include="Core\GitDiff.cs" />
<Compile Include="Core\GitError.cs" />
<Compile Include="Core\GitObjectExtensions.cs" />
<Compile Include="Core\Handles\ObjectDatabaseSafeHandle.cs" />
<Compile Include="Core\Handles\DiffListSafeHandle.cs" />
Expand Down
Loading