Skip to content

Commit f8489b2

Browse files
authored
Merge pull request libgit2#1569 from tyrielv/indexforcherrypicks
Add CherryPickCommitIntoIndex to ObjectDatabase
2 parents 46dd4a4 + c4320ba commit f8489b2

File tree

2 files changed

+133
-22
lines changed

2 files changed

+133
-22
lines changed

LibGit2Sharp.Tests/CherryPickFixture.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,56 @@ public void CherryPickWithConflictsReturnsConflicts()
162162
}
163163
}
164164

165+
[Fact]
166+
public void CanCherryPickCommitIntoIndex()
167+
{
168+
string path = SandboxMergeTestRepo();
169+
using (var repo = new Repository(path))
170+
{
171+
var ours = repo.Head.Tip;
172+
173+
Commit commitToMerge = repo.Branches["fast_forward"].Tip;
174+
175+
using (TransientIndex index = repo.ObjectDatabase.CherryPickCommitIntoIndex(commitToMerge, ours, 0, null))
176+
{
177+
var tree = index.WriteToTree();
178+
Assert.Equal(commitToMerge.Tree.Id, tree.Id);
179+
}
180+
}
181+
}
182+
183+
[Fact]
184+
public void CanCherryPickIntoIndexWithConflicts()
185+
{
186+
const string conflictBranchName = "conflicts";
187+
188+
string path = SandboxMergeTestRepo();
189+
using (var repo = new Repository(path))
190+
{
191+
Branch branch = repo.Branches[conflictBranchName];
192+
Assert.NotNull(branch);
193+
194+
using (TransientIndex index = repo.ObjectDatabase.CherryPickCommitIntoIndex(branch.Tip, repo.Head.Tip, 0, null))
195+
{
196+
Assert.False(index.IsFullyMerged);
197+
198+
var conflict = index.Conflicts.First();
199+
200+
//Resolve the conflict by taking the blob from branch
201+
var blob = repo.Lookup<Blob>(conflict.Theirs.Id);
202+
//Add() does not remove conflict entries for the same path, so they must be explicitly removed first.
203+
index.Remove(conflict.Ours.Path);
204+
index.Add(blob, conflict.Ours.Path, Mode.NonExecutableFile);
205+
206+
Assert.True(index.IsFullyMerged);
207+
var tree = index.WriteToTree();
208+
209+
//Since we took the conflicted blob from the branch, the merged result should be the same as the branch.
210+
Assert.Equal(branch.Tip.Tree.Id, tree.Id);
211+
}
212+
}
213+
}
214+
165215
private Commit AddFileCommitToRepo(IRepository repository, string filename, string content = null)
166216
{
167217
Touch(repository.Info.WorkingDirectory, filename, content);

LibGit2Sharp/ObjectDatabase.cs

Lines changed: 83 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -562,37 +562,26 @@ public virtual HistoryDivergence CalculateHistoryDivergence(Commit one, Commit a
562562
public virtual MergeTreeResult CherryPickCommit(Commit cherryPickCommit, Commit cherryPickOnto, int mainline, MergeTreeOptions options)
563563
{
564564
Ensure.ArgumentNotNull(cherryPickCommit, "cherryPickCommit");
565-
Ensure.ArgumentNotNull(cherryPickOnto, "ours");
565+
Ensure.ArgumentNotNull(cherryPickOnto, "cherryPickOnto");
566566

567-
options = options ?? new MergeTreeOptions();
567+
var modifiedOptions = new MergeTreeOptions();
568568

569569
// We throw away the index after looking at the conflicts, so we'll never need the REUC
570570
// entries to be there
571-
GitMergeFlag mergeFlags = GitMergeFlag.GIT_MERGE_NORMAL | GitMergeFlag.GIT_MERGE_SKIP_REUC;
572-
if (options.FindRenames)
573-
{
574-
mergeFlags |= GitMergeFlag.GIT_MERGE_FIND_RENAMES;
575-
}
576-
if (options.FailOnConflict)
577-
{
578-
mergeFlags |= GitMergeFlag.GIT_MERGE_FAIL_ON_CONFLICT;
579-
}
580-
571+
modifiedOptions.SkipReuc = true;
581572

582-
var opts = new GitMergeOpts
573+
if (options != null)
583574
{
584-
Version = 1,
585-
MergeFileFavorFlags = options.MergeFileFavor,
586-
MergeTreeFlags = mergeFlags,
587-
RenameThreshold = (uint)options.RenameThreshold,
588-
TargetLimit = (uint)options.TargetLimit
589-
};
575+
modifiedOptions.FailOnConflict = options.FailOnConflict;
576+
modifiedOptions.FindRenames = options.FindRenames;
577+
modifiedOptions.MergeFileFavor = options.MergeFileFavor;
578+
modifiedOptions.RenameThreshold = options.RenameThreshold;
579+
modifiedOptions.TargetLimit = options.TargetLimit;
580+
}
590581

591582
bool earlyStop;
592583

593-
using (var cherryPickOntoHandle = Proxy.git_object_lookup(repo.Handle, cherryPickOnto.Id, GitObjectType.Commit))
594-
using (var cherryPickCommitHandle = Proxy.git_object_lookup(repo.Handle, cherryPickCommit.Id, GitObjectType.Commit))
595-
using (var indexHandle = Proxy.git_cherrypick_commit(repo.Handle, cherryPickCommitHandle, cherryPickOntoHandle, (uint)mainline, opts, out earlyStop))
584+
using (var indexHandle = CherryPickCommit(cherryPickCommit, cherryPickOnto, mainline, modifiedOptions, out earlyStop))
596585
{
597586
MergeTreeResult cherryPickResult;
598587

@@ -879,6 +868,36 @@ public virtual TransientIndex MergeCommitsIntoIndex(Commit ours, Commit theirs,
879868
return result;
880869
}
881870

871+
/// <summary>
872+
/// Performs a cherry-pick of <paramref name="cherryPickCommit"/> onto <paramref name="cherryPickOnto"/> commit.
873+
/// </summary>
874+
/// <param name="cherryPickCommit">The commit to cherry-pick.</param>
875+
/// <param name="cherryPickOnto">The commit to cherry-pick onto.</param>
876+
/// <param name="mainline">Which commit to consider the parent for the diff when cherry-picking a merge commit.</param>
877+
/// <param name="options">The options for the merging in the cherry-pick operation.</param>
878+
/// <returns>The <see cref="TransientIndex"/> containing the cherry-pick result tree and any conflicts, or null if the merge stopped early due to conflicts.
879+
/// The index must be disposed by the caller. </returns>
880+
public virtual TransientIndex CherryPickCommitIntoIndex(Commit cherryPickCommit, Commit cherryPickOnto, int mainline, MergeTreeOptions options)
881+
{
882+
Ensure.ArgumentNotNull(cherryPickCommit, "cherryPickCommit");
883+
Ensure.ArgumentNotNull(cherryPickOnto, "cherryPickOnto");
884+
885+
options = options ?? new MergeTreeOptions();
886+
887+
bool earlyStop;
888+
var indexHandle = CherryPickCommit(cherryPickCommit, cherryPickOnto, mainline, options, out earlyStop);
889+
if (earlyStop)
890+
{
891+
if (indexHandle != null)
892+
{
893+
indexHandle.Dispose();
894+
}
895+
return null;
896+
}
897+
var result = new TransientIndex(indexHandle, repo);
898+
return result;
899+
}
900+
882901
/// <summary>
883902
/// Perform a three-way merge of two commits, looking up their
884903
/// commit ancestor. The returned index will contain the results
@@ -921,6 +940,48 @@ private IndexHandle MergeCommits(Commit ours, Commit theirs, MergeTreeOptions op
921940
}
922941
}
923942

943+
/// <summary>
944+
/// Performs a cherry-pick of <paramref name="cherryPickCommit"/> onto <paramref name="cherryPickOnto"/> commit.
945+
/// </summary>
946+
/// <param name="cherryPickCommit">The commit to cherry-pick.</param>
947+
/// <param name="cherryPickOnto">The commit to cherry-pick onto.</param>
948+
/// <param name="mainline">Which commit to consider the parent for the diff when cherry-picking a merge commit.</param>
949+
/// <param name="options">The options for the merging in the cherry-pick operation.</param>
950+
/// <param name="earlyStop">True if the cherry-pick stopped early due to conflicts</param>
951+
/// <returns>The <see cref="IndexHandle"/> containing the cherry-pick result tree and any conflicts</returns>
952+
private IndexHandle CherryPickCommit(Commit cherryPickCommit, Commit cherryPickOnto, int mainline, MergeTreeOptions options, out bool earlyStop)
953+
{
954+
GitMergeFlag mergeFlags = GitMergeFlag.GIT_MERGE_NORMAL;
955+
if (options.SkipReuc)
956+
{
957+
mergeFlags |= GitMergeFlag.GIT_MERGE_SKIP_REUC;
958+
}
959+
if (options.FindRenames)
960+
{
961+
mergeFlags |= GitMergeFlag.GIT_MERGE_FIND_RENAMES;
962+
}
963+
if (options.FailOnConflict)
964+
{
965+
mergeFlags |= GitMergeFlag.GIT_MERGE_FAIL_ON_CONFLICT;
966+
}
967+
968+
var mergeOptions = new GitMergeOpts
969+
{
970+
Version = 1,
971+
MergeFileFavorFlags = options.MergeFileFavor,
972+
MergeTreeFlags = mergeFlags,
973+
RenameThreshold = (uint)options.RenameThreshold,
974+
TargetLimit = (uint)options.TargetLimit,
975+
};
976+
977+
using (var cherryPickOntoHandle = Proxy.git_object_lookup(repo.Handle, cherryPickOnto.Id, GitObjectType.Commit))
978+
using (var cherryPickCommitHandle = Proxy.git_object_lookup(repo.Handle, cherryPickCommit.Id, GitObjectType.Commit))
979+
{
980+
var indexHandle = Proxy.git_cherrypick_commit(repo.Handle, cherryPickCommitHandle, cherryPickOntoHandle, (uint)mainline, mergeOptions, out earlyStop);
981+
return indexHandle;
982+
}
983+
}
984+
924985

925986
/// <summary>
926987
/// Packs objects in the <see cref="ObjectDatabase"/> and write a pack (.pack) and index (.idx) files for them.

0 commit comments

Comments
 (0)