Skip to content
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
26 changes: 24 additions & 2 deletions LibGit2Sharp.Tests/CommitAncestorFixture.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using LibGit2Sharp.Tests.TestHelpers;
using Xunit;
using Xunit.Extensions;
Expand All @@ -8,6 +10,8 @@ namespace LibGit2Sharp.Tests
{
public class CommitAncestorFixture : BaseFixture
{
private static int orphans;

/*
* BareTestRepoPath structure
*
Expand All @@ -27,7 +31,7 @@ public class CommitAncestorFixture : BaseFixture
* |
* * commit 8496071c1b46c854b31185ea97743be6a877447
*
*/
*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could you please revert this? ^^

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I made it align better; was it intentionally misaligned?

Or would you just rather not have that change in this pull request?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Was it intentionally misaligned?

I don't think it was 😉

Or would you just rather not have that change in this pull request?

We tend to prefer beautification changes to live in their own commits. If you're ok with extracting it in a different commit, it'd be glorious.


[Theory]
[InlineData("5b5b025afb0b4c913b4c338a42934a3863bf3644", "c47800c", "9fd738e")]
Expand Down Expand Up @@ -85,6 +89,24 @@ public void FindCommonAncestorForCommitsAsEnumerable(string result, string[] sha
}
}

[Theory]
[InlineData(new[] { "0350717031bebea6b2600b89a33159935541cee1", "ebc932c47f8800bffdd150c560e111d87bb74f4f" }, new[] { "152325a", "c9a2051" })]
[InlineData(new[] { "0350717031bebea6b2600b89a33159935541cee1", "ebc932c47f8800bffdd150c560e111d87bb74f4f" }, new[] { "152325a", "c9a2051", "-" })]
[InlineData(new string[0], new[] { "-", "-" })]
public void FindCommonAncestorsForCommitsAsEnumerable(string[] results, string[] shas)
{
string path = SandboxCrossHistoryRepo();
using (var repo = new Repository(path))
{
var commits = shas.Select(sha => sha == "-" ? CreateOrphanedCommit(repo) : repo.Lookup<Commit>(sha)).ToArray();

var ancestors = repo.ObjectDatabase.FindMergeBases(commits).ToArray();

Assert.NotNull(ancestors);
Assert.Equal(results, ancestors.Select(a => a.Id.Sha));
}
}

[Theory]
[InlineData("4c062a6", "0000000")]
[InlineData("0000000", "4c062a6")]
Expand Down Expand Up @@ -123,7 +145,7 @@ private static Commit CreateOrphanedCommit(IRepository repo)
Commit orphanedCommit = repo.ObjectDatabase.CreateCommit(
random.Author,
random.Committer,
"This is a test commit created by 'CommitFixture.CannotFindCommonAncestorForCommmitsWithoutCommonAncestor'",
string.Format("This is a test commit (#{0}) created by 'CommitFixture.CannotFindCommonAncestorForCommmitsWithoutCommonAncestor'", Interlocked.Increment(ref orphans)),
random.Tree,
Enumerable.Empty<Commit>(),
false);
Expand Down
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions LibGit2Sharp.Tests/Resources/crosshistory_wd/dot_git/HEAD
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ref: refs/heads/a
8 changes: 8 additions & 0 deletions LibGit2Sharp.Tests/Resources/crosshistory_wd/dot_git/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
symlinks = false
ignorecase = true
hideDotFiles = dotGitOnly
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
x��K
�@ @]�)rK�c�] �b&�Z�R#��W�n�Ń'��z1!�lQ� � Q� �X� i$ն�0�ʔ���E'�B��^26Q�TU�g������z��<l�8��Nj�K'��V�������"��"�1#:���ӺCkڠt�4��,G8
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
152325a8a96b610627aaae41a00391c3644079e4
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
c9a20513b0648832b41b0f875ab16c92e4b7dae7
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a69878c70d9cce9a1fbec1712d1fcd4f609e689b
7 changes: 7 additions & 0 deletions LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ static BaseFixture()
}

public static string BareTestRepoPath { get; private set; }
public static string CrossHistoryRepoWorkingDirPath { get; private set; }
public static string StandardTestRepoWorkingDirPath { get; private set; }
public static string StandardTestRepoPath { get; private set; }
public static string ShallowTestRepoPath { get; private set; }
Expand Down Expand Up @@ -63,6 +64,7 @@ private static void SetUpTestEnvironment()

// Setup standard paths to our test repositories
BareTestRepoPath = Path.Combine(sourceRelativePath, "testrepo.git");
CrossHistoryRepoWorkingDirPath = Path.Combine(sourceRelativePath, "crosshistory_wd");
StandardTestRepoWorkingDirPath = Path.Combine(sourceRelativePath, "testrepo_wd");
StandardTestRepoPath = Path.Combine(StandardTestRepoWorkingDirPath, "dot_git");
ShallowTestRepoPath = Path.Combine(sourceRelativePath, "shallow.git");
Expand Down Expand Up @@ -131,6 +133,11 @@ protected string SandboxBareTestRepo()
return Sandbox(BareTestRepoPath);
}

protected string SandboxCrossHistoryRepo()
{
return Sandbox(CrossHistoryRepoWorkingDirPath);
}

protected string SandboxStandardTestRepo()
{
return Sandbox(StandardTestRepoWorkingDirPath);
Expand Down
28 changes: 28 additions & 0 deletions LibGit2Sharp/Core/GitOidArray.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Runtime.InteropServices;

namespace LibGit2Sharp.Core
{
[StructLayout(LayoutKind.Sequential)]
internal struct GitOidArray
{
/// <summary>
/// A pointer to an array of ids.
/// </summary>
public IntPtr Ids;

/// <summary>
/// The number of ids in the array.
/// </summary>
public UIntPtr Length;

/// <summary>
/// Resets the GitOidArray to default values.
/// </summary>
public void Reset()
{
Ids = IntPtr.Zero;
Length = UIntPtr.Zero;
}
}
}
43 changes: 43 additions & 0 deletions LibGit2Sharp/Core/GitOidArrayNative.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Runtime.InteropServices;

namespace LibGit2Sharp.Core
{
/// <summary>
/// A git_oidarray where the id array and ids themselves were allocated
/// with libgit2's allocator. Only libgit2 can free this git_oidarray.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal class GitOidArrayNative : IDisposable
{
public GitOidArray Array;

/// <summary>
/// Reads each GitOid from the array.
/// </summary>
public GitOid[] ReadOids()
{
var count = checked((int)Array.Length.ToUInt32());

GitOid[] toReturn = new GitOid[count];

for (int i = 0; i < count; i++)
{
toReturn[i] = (Array.Ids + i * Marshal.SizeOf(typeof(GitOid))).MarshalAs<GitOid>();
}

return toReturn;
}

public void Dispose()
{
if (Array.Ids != IntPtr.Zero)
{
NativeMethods.git_oidarray_free(ref Array);
}

// Now that we've freed the memory, zero out the structure.
Array.Reset();
}
}
}
11 changes: 11 additions & 0 deletions LibGit2Sharp/Core/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -714,13 +714,24 @@ internal static extern int git_merge_base_many(
int length,
[In] GitOid[] input_array);

[DllImport(libgit2)]
internal static extern int git_merge_bases_many(
out GitOidArray mergeBases,
RepositorySafeHandle repo,
int length,
[In] GitOid[] input_array);

[DllImport(libgit2)]
internal static extern int git_merge_base_octopus(
out GitOid mergeBase,
RepositorySafeHandle repo,
int length,
[In] GitOid[] input_array);

[DllImport(libgit2)]
internal static extern void git_oidarray_free(
ref GitOidArray array);

[DllImport(libgit2)]
internal static extern int git_annotated_commit_from_ref(
out GitAnnotatedCommitHandle annotatedCommit,
Expand Down
23 changes: 23 additions & 0 deletions LibGit2Sharp/Core/Proxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,29 @@ public static ObjectId git_merge_base_many(RepositorySafeHandle repo, GitOid[] c
return ret;
}

public static ObjectId[] git_merge_bases_many(RepositorySafeHandle repo, GitOid[] commitIds)
{
var array = new GitOidArrayNative();

try

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How about switch to a more plain using (var array = new GitOidArrayNative())?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I was following the example of various methods already in Proxy.cs.

For example: https://github.com/libgit2/libgit2sharp/blob/vNext/LibGit2Sharp/Core/Proxy.cs#L2028

Now, I don't disagree with your recommendation, but I would suggest that this pull request should strive to match the style of the surrounding code, which it has done.

If the surrounding code gets cleaned up before this pull request is merged in, I'd be happy to update it. If instead this code review gets pulled in first, I would be happy to submit another that cleans everything up.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If instead this code review gets pulled in first, I would be happy to submit another that cleans everything up.

Let's do this then. Thanks for the proposal!

{
int res = NativeMethods.git_merge_bases_many(out array.Array, repo, commitIds.Length, commitIds);

if (res == (int)GitErrorCode.NotFound)
{
return new ObjectId[0];
}

Ensure.ZeroResult(res);

return Array.ConvertAll(array.ReadOids(), id => (ObjectId)id);
}
finally
{
array.Dispose();
}
}

public static ObjectId git_merge_base_octopus(RepositorySafeHandle repo, GitOid[] commitIds)
{
GitOid ret;
Expand Down
2 changes: 2 additions & 0 deletions LibGit2Sharp/LibGit2Sharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
<Compile Include="CompareOptions.cs" />
<Compile Include="Core\FileHistory.cs" />
<Compile Include="Core\GitFetchOptions.cs" />
<Compile Include="Core\GitOidArray.cs" />
<Compile Include="Core\GitOidArrayNative.cs" />
<Compile Include="Core\GitPushUpdate.cs" />
<Compile Include="Core\GitSubmoduleIgnore.cs" />
<Compile Include="Core\Platform.cs" />
Expand Down
32 changes: 32 additions & 0 deletions LibGit2Sharp/ObjectDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,38 @@ public virtual Commit FindMergeBase(IEnumerable<Commit> commits, MergeBaseFindin
return id == null ? null : repo.Lookup<Commit>(id);
}

/// <summary>
/// Find all best possible merge bases given two or more <see cref="Commit"/>.
/// </summary>
/// <param name="commits">The <see cref="Commit"/>s for which to find the merge bases.</param>
/// <returns>An enumerable collection containing all best possible merge bases.</returns>
public virtual IEnumerable<Commit> FindMergeBases(IEnumerable<Commit> commits)
{
Ensure.ArgumentNotNull(commits, "commits");

List<GitOid> ids = new List<GitOid>(8);
int count = 0;

foreach (var commit in commits)
{
if (commit == null)
{
throw new ArgumentException("Enumerable contains null at position: " + count.ToString(CultureInfo.InvariantCulture), "commits");
}
ids.Add(commit.Id.Oid);
count++;
}

if (count < 2)
{
throw new ArgumentException("The enumerable must contains at least two commits.", "commits");
}

var baseIds = Proxy.git_merge_bases_many(repo.Handle, ids.ToArray());

return baseIds.Select(id => repo.Lookup<Commit>(id)).ToList();
}

/// <summary>
/// Perform a three-way merge of two commits, looking up their
/// commit ancestor. The returned index will contain the results
Expand Down