/// <summary> /// Perform a three-way merge of two commits, looking up their /// commit ancestor. The returned index will contain the results /// of the merge and can be examined for conflicts. /// </summary> /// <param name="ours">The first tree</param> /// <param name="theirs">The second tree</param> /// <param name="options">The <see cref="MergeTreeOptions"/> controlling the merge</param> /// <param name="earlyStop">True if the merge stopped early due to conflicts</param> /// <returns>The <see cref="IndexHandle"/> containing the merged trees and any conflicts</returns> private IndexHandle MergeCommits(Commit ours, Commit theirs, MergeTreeOptions options, out bool earlyStop) { GitMergeFlag mergeFlags = GitMergeFlag.GIT_MERGE_NORMAL; if (options.SkipReuc) { mergeFlags |= GitMergeFlag.GIT_MERGE_SKIP_REUC; } if (options.FindRenames) { mergeFlags |= GitMergeFlag.GIT_MERGE_FIND_RENAMES; } if (options.FailOnConflict) { mergeFlags |= GitMergeFlag.GIT_MERGE_FAIL_ON_CONFLICT; } var mergeOptions = new GitMergeOpts { Version = 1, MergeFileFavorFlags = options.MergeFileFavor, MergeTreeFlags = mergeFlags, RenameThreshold = (uint)options.RenameThreshold, TargetLimit = (uint)options.TargetLimit, }; using (var oneHandle = Proxy.git_object_lookup(repo.Handle, ours.Id, GitObjectType.Commit)) using (var twoHandle = Proxy.git_object_lookup(repo.Handle, theirs.Id, GitObjectType.Commit)) { var indexHandle = Proxy.git_merge_commits(repo.Handle, oneHandle, twoHandle, mergeOptions, out earlyStop); return(indexHandle); } }
/// <summary> /// Performs a cherry-pick of <paramref name="cherryPickCommit"/> onto <paramref name="cherryPickOnto"/> commit. /// </summary> /// <param name="cherryPickCommit">The commit to cherry-pick.</param> /// <param name="cherryPickOnto">The commit to cherry-pick onto.</param> /// <param name="mainline">Which commit to consider the parent for the diff when cherry-picking a merge commit.</param> /// <param name="options">The options for the merging in the cherry-pick operation.</param> /// <param name="earlyStop">True if the cherry-pick stopped early due to conflicts</param> /// <returns>The <see cref="IndexHandle"/> containing the cherry-pick result tree and any conflicts</returns> private IndexHandle CherryPickCommit(Commit cherryPickCommit, Commit cherryPickOnto, int mainline, MergeTreeOptions options, out bool earlyStop) { GitMergeFlag mergeFlags = GitMergeFlag.GIT_MERGE_NORMAL; if (options.SkipReuc) { mergeFlags |= GitMergeFlag.GIT_MERGE_SKIP_REUC; } if (options.FindRenames) { mergeFlags |= GitMergeFlag.GIT_MERGE_FIND_RENAMES; } if (options.FailOnConflict) { mergeFlags |= GitMergeFlag.GIT_MERGE_FAIL_ON_CONFLICT; } var mergeOptions = new GitMergeOpts { Version = 1, MergeFileFavorFlags = options.MergeFileFavor, MergeTreeFlags = mergeFlags, RenameThreshold = (uint)options.RenameThreshold, TargetLimit = (uint)options.TargetLimit, }; using (var cherryPickOntoHandle = Proxy.git_object_lookup(repo.Handle, cherryPickOnto.Id, GitObjectType.Commit)) using (var cherryPickCommitHandle = Proxy.git_object_lookup(repo.Handle, cherryPickCommit.Id, GitObjectType.Commit)) { var indexHandle = Proxy.git_cherrypick_commit(repo.Handle, cherryPickCommitHandle, cherryPickOntoHandle, (uint)mainline, mergeOptions, out earlyStop); return(indexHandle); } }
/// <summary> /// Merges the given commit into HEAD as well as performing a Fast Forward if possible. /// </summary> /// <param name="commit">The commit to use as a reference for the changes that should be merged into HEAD.</param> /// <param name="merger">If the merge generates a merge commit (i.e. a non-fast forward merge), the <see cref="Signature"/> of who made the merge.</param> /// <returns>The result of the performed merge <see cref="MergeResult"/>.</returns> public MergeResult Merge(Commit commit, Signature merger) { using (GitMergeHeadHandle mergeHeadHandle = Proxy.git_merge_head_from_oid(Handle, commit.Id.Oid)) { GitMergeOpts opts = new GitMergeOpts() { Version = 1, MergeTreeOpts = { Version = 1 }, CheckoutOpts = { version = 1 }, }; // Perform the merge in libgit2 and get the result back. GitMergeResult gitMergeResult; using (GitMergeResultHandle mergeResultHandle = Proxy.git_merge(Handle, new GitMergeHeadHandle[] { mergeHeadHandle }, opts)) { gitMergeResult = new GitMergeResult(mergeResultHandle); } // Handle the result of the merge performed in libgit2 // and commit the result / update the working directory as necessary. MergeResult mergeResult; switch (gitMergeResult.Status) { case MergeStatus.UpToDate: mergeResult = new MergeResult(MergeStatus.UpToDate); break; case MergeStatus.FastForward: Commit fastForwardCommit = this.Lookup <Commit>(gitMergeResult.FastForwardId); FastForward(fastForwardCommit); mergeResult = new MergeResult(MergeStatus.FastForward, fastForwardCommit); break; case MergeStatus.NonFastForward: { if (Index.IsFullyMerged) { // Commit the merge Commit mergeCommit = this.Commit(Info.Message, author: merger, committer: merger); mergeResult = new MergeResult(MergeStatus.NonFastForward, mergeCommit); } else { mergeResult = new MergeResult(MergeStatus.Conflicts); } } break; default: throw new NotImplementedException(string.Format("Unknown MergeStatus: {0}", gitMergeResult.Status)); } return(mergeResult); } }
/// <summary> /// Perform a three-way merge of two commits, looking up their /// commit ancestor. The returned index will contain the results /// of the merge and can be examined for conflicts. The returned /// index must be disposed. /// </summary> /// <param name="ours">The first tree</param> /// <param name="theirs">The second tree</param> /// <param name="options">The <see cref="MergeTreeOptions"/> controlling the merge</param> /// <returns>The <see cref="Index"/> containing the merged trees and any conflicts</returns> public virtual MergeTreeResult MergeCommits(Commit ours, Commit theirs, MergeTreeOptions options) { Ensure.ArgumentNotNull(ours, "ours"); Ensure.ArgumentNotNull(theirs, "theirs"); options = options ?? new MergeTreeOptions(); var mergeOptions = new GitMergeOpts { Version = 1, MergeFileFavorFlags = options.MergeFileFavor, MergeTreeFlags = options.FindRenames ? GitMergeTreeFlags.GIT_MERGE_TREE_FIND_RENAMES : GitMergeTreeFlags.GIT_MERGE_TREE_NORMAL, RenameThreshold = (uint)options.RenameThreshold, TargetLimit = (uint)options.TargetLimit, }; using (var oneHandle = Proxy.git_object_lookup(repo.Handle, ours.Id, GitObjectType.Commit)) using (var twoHandle = Proxy.git_object_lookup(repo.Handle, theirs.Id, GitObjectType.Commit)) using (var indexHandle = Proxy.git_merge_commits(repo.Handle, oneHandle, twoHandle, mergeOptions)) { MergeTreeResult mergeResult; if (Proxy.git_index_has_conflicts(indexHandle)) { List <Conflict> conflicts = new List <Conflict>(); Conflict conflict; using (ConflictIteratorSafeHandle iterator = Proxy.git_index_conflict_iterator_new(indexHandle)) { while ((conflict = Proxy.git_index_conflict_next(iterator)) != null) { conflicts.Add(conflict); } } mergeResult = new MergeTreeResult(conflicts); } else { var treeId = Proxy.git_index_write_tree_to(indexHandle, repo.Handle); mergeResult = new MergeTreeResult(this.repo.Lookup <Tree>(treeId)); } return(mergeResult); } }
/// <summary> /// Perform a three-way merge of two commits, looking up their /// commit ancestor. The returned index will contain the results /// of the merge and can be examined for conflicts. The returned /// index must be disposed. /// </summary> /// <param name="ours">The first tree</param> /// <param name="theirs">The second tree</param> /// <param name="options">The <see cref="MergeTreeOptions"/> controlling the merge</param> /// <returns>The <see cref="Index"/> containing the merged trees and any conflicts</returns> public virtual MergeTreeResult MergeCommits(Commit ours, Commit theirs, MergeTreeOptions options) { Ensure.ArgumentNotNull(ours, "ours"); Ensure.ArgumentNotNull(theirs, "theirs"); options = options ?? new MergeTreeOptions(); // We throw away the index after looking at the conflicts, so we'll never need the REUC // entries to be there GitMergeFlag mergeFlags = GitMergeFlag.GIT_MERGE_NORMAL | GitMergeFlag.GIT_MERGE_SKIP_REUC; if (options.FindRenames) { mergeFlags |= GitMergeFlag.GIT_MERGE_FIND_RENAMES; } if (options.FailOnConflict) { mergeFlags |= GitMergeFlag.GIT_MERGE_FAIL_ON_CONFLICT; } var mergeOptions = new GitMergeOpts { Version = 1, MergeFileFavorFlags = options.MergeFileFavor, MergeTreeFlags = mergeFlags, RenameThreshold = (uint)options.RenameThreshold, TargetLimit = (uint)options.TargetLimit, }; bool earlyStop; using (var oneHandle = Proxy.git_object_lookup(repo.Handle, ours.Id, GitObjectType.Commit)) using (var twoHandle = Proxy.git_object_lookup(repo.Handle, theirs.Id, GitObjectType.Commit)) using (var indexHandle = Proxy.git_merge_commits(repo.Handle, oneHandle, twoHandle, mergeOptions, out earlyStop)) { MergeTreeResult mergeResult; // Stopped due to FailOnConflict so there's no index or conflict list if (earlyStop) { return(new MergeTreeResult(new Conflict[] { })); } if (Proxy.git_index_has_conflicts(indexHandle)) { List <Conflict> conflicts = new List <Conflict>(); Conflict conflict; using (ConflictIteratorHandle iterator = Proxy.git_index_conflict_iterator_new(indexHandle)) { while ((conflict = Proxy.git_index_conflict_next(iterator)) != null) { conflicts.Add(conflict); } } mergeResult = new MergeTreeResult(conflicts); } else { var treeId = Proxy.git_index_write_tree_to(indexHandle, repo.Handle); mergeResult = new MergeTreeResult(this.repo.Lookup <Tree>(treeId)); } return(mergeResult); } }