Skip to content

Introduce Repository.MergeCommits #990

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

Merged
merged 3 commits into from
Apr 9, 2015
Merged
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
100 changes: 100 additions & 0 deletions LibGit2Sharp.Tests/MergeFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,106 @@ public void CanMergeIntoOrphanedBranch()
}
}


[Fact]
public void CanMergeTreeIntoSameTree()
{
string path = SandboxMergeTestRepo();
using (var repo = new Repository(path))
{
var master = repo.Branches["master"].Tip;

var result = repo.ObjectDatabase.MergeCommits(master, master, null);
Assert.Equal(MergeTreeStatus.Succeeded, result.Status);
Copy link
Member

Choose a reason for hiding this comment

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

Assert.NotNull(MergeTreeStatus.Tree);
Assert.Equal(0, MergeTreeStatus.Conflicts.Count());

Assert.Equal(0, result.Conflicts.Count());
}
}

[Fact]
public void CanMergeTreeIntoTreeFromUnbornBranch()
{
string path = SandboxMergeTestRepo();
using (var repo = new Repository(path))
{
repo.Refs.UpdateTarget("HEAD", "refs/heads/unborn");

Touch(repo.Info.WorkingDirectory, "README", "Yeah!\n");
repo.Index.Clear();
repo.Stage("README");

repo.Commit("A new world, free of the burden of the history", Constants.Signature, Constants.Signature);

var master = repo.Branches["master"].Tip;
var branch = repo.Branches["unborn"].Tip;

var result = repo.ObjectDatabase.MergeCommits(master, branch, null);
Assert.Equal(MergeTreeStatus.Succeeded, result.Status);
Copy link
Member

Choose a reason for hiding this comment

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

Assert.NotNull(MergeTreeStatus.Tree);
Assert.Equal(0, MergeTreeStatus.Conflicts.Count());

Assert.NotNull(result.Tree);
Assert.Equal(0, result.Conflicts.Count());
}
}

[Fact]
public void CanMergeCommitsAndDetectConflicts()
{
string path = SandboxMergeTestRepo();
using (var repo = new Repository(path))
{
repo.Refs.UpdateTarget("HEAD", "refs/heads/unborn");

repo.Index.Replace(repo.Lookup<Commit>("conflicts"));

repo.Commit("A conflicting world, free of the burden of the history", Constants.Signature, Constants.Signature);

var master = repo.Branches["master"].Tip;
var branch = repo.Branches["unborn"].Tip;

var result = repo.ObjectDatabase.MergeCommits(master, branch, null);
Assert.Equal(MergeTreeStatus.Conflicts, result.Status);
Copy link
Member

Choose a reason for hiding this comment

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

Assert.Null(MergeTreeStatus.Tree);
Assert.NotEqual(0, MergeTreeStatus.Conflicts.Count());

Assert.Null(result.Tree);
Assert.NotEqual(0, result.Conflicts.Count());
}
}

[Fact]
public void CanMergeFastForwardTreeWithoutConflicts()
{
string path = SandboxMergeTestRepo();
using (var repo = new Repository(path))
{
var master = repo.Lookup<Commit>("master");
var branch = repo.Lookup<Commit>("fast_forward");

var result = repo.ObjectDatabase.MergeCommits(master, branch, null);
Assert.Equal(MergeTreeStatus.Succeeded, result.Status);
Copy link
Member

Choose a reason for hiding this comment

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

Assert.NotNull(MergeTreeStatus.Tree);
Assert.Equal(0, MergeTreeStatus.Conflicts.Count());

Assert.NotNull(result.Tree);
Assert.Equal(0, result.Conflicts.Count());
}
}

[Fact]
public void CanIdentifyConflictsInMergeCommits()
{
string path = SandboxMergeTestRepo();
using (var repo = new Repository(path))
{
var master = repo.Lookup<Commit>("master");
var branch = repo.Lookup<Commit>("conflicts");

var result = repo.ObjectDatabase.MergeCommits(master, branch, null);

Assert.Equal(MergeTreeStatus.Conflicts, result.Status);
Copy link
Member

Choose a reason for hiding this comment

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

Assert.Null(MergeTreeStatus.Tree);


Assert.Null(result.Tree);
Assert.Equal(1, result.Conflicts.Count());

var conflict = result.Conflicts.First();
Assert.Equal(new ObjectId("8e9daea300fbfef6c0da9744c6214f546d55b279"), conflict.Ancestor.Id);
Assert.Equal(new ObjectId("610b16886ca829cebd2767d9196f3c4378fe60b5"), conflict.Ours.Id);
Assert.Equal(new ObjectId("3dd9738af654bbf1c363f6c3bbc323bacdefa179"), conflict.Theirs.Id);
}
}

private Commit AddFileCommitToRepo(IRepository repository, string filename, string content = null)
{
Touch(repository.Info.WorkingDirectory, filename, content);
Expand Down
90 changes: 4 additions & 86 deletions LibGit2Sharp/CherryPickOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,108 +6,26 @@ namespace LibGit2Sharp
/// <summary>
/// Options controlling CherryPick behavior.
/// </summary>
public sealed class CherryPickOptions : IConvertableToGitCheckoutOpts
public sealed class CherryPickOptions : MergeAndCheckoutOptionsBase
{
/// <summary>
/// Initializes a new instance of the <see cref="CherryPickOptions"/> class.
/// By default the cherry pick will be committed if there are no conflicts.
/// </summary>
public CherryPickOptions()
{
CommitOnSuccess = true;

FindRenames = true;

// TODO: libgit2 should provide reasonable defaults for these
// values, but it currently does not.
RenameThreshold = 50;
TargetLimit = 200;
}

/// <summary>
/// The Flags specifying what conditions are
/// reported through the OnCheckoutNotify delegate.
/// </summary>
public CheckoutNotifyFlags CheckoutNotifyFlags { get; set; }

/// <summary>
/// Delegate that checkout progress will be reported through.
/// </summary>
public CheckoutProgressHandler OnCheckoutProgress { get; set; }

/// <summary>
/// Delegate that checkout will notify callers of
/// certain conditions. The conditions that are reported is
/// controlled with the CheckoutNotifyFlags property.
/// </summary>
public CheckoutNotifyHandler OnCheckoutNotify { get; set; }

/// <summary>
/// Commit the cherry pick if the cherry pick is successful.
/// </summary>
public bool CommitOnSuccess { get; set; }

/// <summary>
/// When cherry picking a merge commit, the parent number to consider as
/// mainline, starting from offset 1.
/// <para>
/// As a merge commit has multiple parents, cherry picking a merge commit
/// will reverse all the changes brought in by the merge except for
/// one parent's line of commits. The parent to preserve is called the
/// mainline, and must be specified by its number (i.e. offset).
/// will take only the changes relative to the given parent. The parent
/// to consider changes based on is called the mainline, and must be
/// specified by its number (i.e. offset).
/// </para>
/// </summary>
public int Mainline { get; set; }

/// <summary>
/// How to handle conflicts encountered during a merge.
/// </summary>
public MergeFileFavor MergeFileFavor { get; set; }

/// <summary>
/// How Checkout should handle writing out conflicting index entries.
/// </summary>
public CheckoutFileConflictStrategy FileConflictStrategy { get; set; }

/// <summary>
/// Find renames. Default is true.
/// </summary>
public bool FindRenames { get; set; }

/// <summary>
/// Similarity to consider a file renamed (default 50). If
/// `FindRenames` is enabled, added files will be compared
/// with deleted files to determine their similarity. Files that are
/// more similar than the rename threshold (percentage-wise) will be
/// treated as a rename.
/// </summary>
public int RenameThreshold;

/// <summary>
/// Maximum similarity sources to examine for renames (default 200).
/// If the number of rename candidates (add / delete pairs) is greater
/// than this value, inexact rename detection is aborted.
///
/// This setting overrides the `merge.renameLimit` configuration value.
/// </summary>
public int TargetLimit;

#region IConvertableToGitCheckoutOpts

CheckoutCallbacks IConvertableToGitCheckoutOpts.GenerateCallbacks()
{
return CheckoutCallbacks.From(OnCheckoutProgress, OnCheckoutNotify);
}

CheckoutStrategy IConvertableToGitCheckoutOpts.CheckoutStrategy
{
get
{
return CheckoutStrategy.GIT_CHECKOUT_SAFE |
GitCheckoutOptsWrapper.CheckoutStrategyFromFileConflictStrategy(FileConflictStrategy);
}
}

#endregion IConvertableToGitCheckoutOpts
}
}
14 changes: 7 additions & 7 deletions LibGit2Sharp/ConflictCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ namespace LibGit2Sharp
/// </summary>
public class ConflictCollection : IEnumerable<Conflict>
{
private readonly Repository repo;
private readonly Index index;

/// <summary>
/// Needed for mocking purposes.
/// </summary>
protected ConflictCollection()
{ }

internal ConflictCollection(Repository repo)
internal ConflictCollection(Index index)
{
this.repo = repo;
this.index = index;
}

/// <summary>
Expand All @@ -36,7 +36,7 @@ public virtual Conflict this[string path]
{
get
{
return Proxy.git_index_conflict_get(repo.Index.Handle, repo, path);
return Proxy.git_index_conflict_get(index.Handle, path);
}
}

Expand All @@ -48,7 +48,7 @@ public virtual IndexReucEntryCollection ResolvedConflicts
{
get
{
return new IndexReucEntryCollection(repo);
return new IndexReucEntryCollection(index);
}
}

Expand All @@ -60,7 +60,7 @@ public virtual IndexNameEntryCollection Names
{
get
{
return new IndexNameEntryCollection(repo);
return new IndexNameEntryCollection(index);
}
}

Expand All @@ -72,7 +72,7 @@ private List<Conflict> AllConflicts()
IndexEntry ancestor = null, ours = null, theirs = null;
string currentPath = null;

foreach (IndexEntry entry in repo.Index)
foreach (IndexEntry entry in index)
{
if (entry.StageLevel == StageLevel.Staged)
{
Expand Down
11 changes: 11 additions & 0 deletions LibGit2Sharp/Core/Handles/ConflictIteratorSafeHandle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace LibGit2Sharp.Core.Handles
{
internal class ConflictIteratorSafeHandle : SafeHandleBase
{
protected override bool ReleaseHandleImpl()
{
Proxy.git_index_conflict_iterator_free(handle);
return true;
}
}
}
26 changes: 22 additions & 4 deletions LibGit2Sharp/Core/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,22 @@ internal static extern int git_index_conflict_get(
IndexSafeHandle index,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path);

[DllImport(libgit2)]
internal static extern int git_index_conflict_iterator_new(
out ConflictIteratorSafeHandle iterator,
IndexSafeHandle index);

[DllImport(libgit2)]
internal static extern int git_index_conflict_next(
out IndexEntrySafeHandle ancestor,
out IndexEntrySafeHandle ours,
out IndexEntrySafeHandle theirs,
ConflictIteratorSafeHandle iterator);

[DllImport(libgit2)]
internal static extern void git_index_conflict_iterator_free(
IntPtr iterator);

[DllImport(libgit2)]
internal static extern UIntPtr git_index_entrycount(IndexSafeHandle index);

Expand Down Expand Up @@ -602,6 +618,9 @@ internal static extern IndexReucEntrySafeHandle git_index_reuc_get_bypath(
[DllImport(libgit2)]
internal static extern int git_index_write_tree(out GitOid treeOid, IndexSafeHandle index);

[DllImport(libgit2)]
internal static extern int git_index_write_tree_to(out GitOid treeOid, IndexSafeHandle index, RepositorySafeHandle repo);

[DllImport(libgit2)]
internal static extern int git_index_read_tree(IndexSafeHandle index, GitObjectSafeHandle tree);

Expand Down Expand Up @@ -661,12 +680,11 @@ internal static extern int git_merge(
ref GitCheckoutOpts checkout_opts);

[DllImport(libgit2)]
internal static extern int git_merge_trees(
internal static extern int git_merge_commits(
out IndexSafeHandle index,
RepositorySafeHandle repo,
GitObjectSafeHandle ancestor_tree,
GitObjectSafeHandle our_tree,
GitObjectSafeHandle their_tree,
GitObjectSafeHandle our_commit,
GitObjectSafeHandle their_commit,
ref GitMergeOpts merge_opts);

[DllImport(libgit2)]
Expand Down
Loading