/// <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> /// Perform a three-way merge of two commits, looking up their /// commit ancestor. The returned <see cref="MergeTreeResult"/> will contain the results /// of the merge and can be examined for conflicts. /// </summary> /// <param name="ours">The first commit</param> /// <param name="theirs">The second commit</param> /// <param name="options">The <see cref="MergeTreeOptions"/> controlling the merge</param> /// <returns>The <see cref="MergeTreeResult"/> 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"); var modifiedOptions = new MergeTreeOptions(); // We throw away the index after looking at the conflicts, so we'll never need the REUC // entries to be there modifiedOptions.SkipReuc = true; if (options != null) { modifiedOptions.FailOnConflict = options.FailOnConflict; modifiedOptions.FindRenames = options.FindRenames; modifiedOptions.IgnoreWhitespaceChange = options.IgnoreWhitespaceChange; modifiedOptions.MergeFileFavor = options.MergeFileFavor; modifiedOptions.RenameThreshold = options.RenameThreshold; modifiedOptions.TargetLimit = options.TargetLimit; } bool earlyStop; using (var indexHandle = MergeCommits(ours, theirs, modifiedOptions, 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); } }
/// <summary> /// Returns whether merging <paramref name="one"/> into <paramref name="another"/> /// would result in merge conflicts. /// </summary> /// <param name="one">The commit wrapping the base tree to merge into.</param> /// <param name="another">The commit wrapping the tree to merge into <paramref name="one"/>.</param> /// <returns>True if the merge does not result in a conflict, false otherwise.</returns> public virtual bool CanMergeWithoutConflict(Commit one, Commit another) { Ensure.ArgumentNotNull(one, "one"); Ensure.ArgumentNotNull(another, "another"); var opts = new MergeTreeOptions() { SkipReuc = true, FailOnConflict = true, }; var result = repo.ObjectDatabase.MergeCommits(one, another, opts); return(result.Status == MergeTreeStatus.Succeeded); }
/// <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> /// 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> /// <returns>The <see cref="TransientIndex"/> containing the cherry-pick result tree and any conflicts, or null if the merge stopped early due to conflicts. /// The index must be disposed by the caller. </returns> public virtual TransientIndex CherryPickCommitIntoIndex(Commit cherryPickCommit, Commit cherryPickOnto, int mainline, MergeTreeOptions options) { Ensure.ArgumentNotNull(cherryPickCommit, "cherryPickCommit"); Ensure.ArgumentNotNull(cherryPickOnto, "cherryPickOnto"); options = options ?? new MergeTreeOptions(); bool earlyStop; var indexHandle = CherryPickCommit(cherryPickCommit, cherryPickOnto, mainline, options, out earlyStop); if (earlyStop) { if (indexHandle != null) { indexHandle.Dispose(); } return(null); } var result = new TransientIndex(indexHandle, repo); return(result); }
/// <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> /// <returns>The <see cref="TransientIndex"/> containing the merged trees and any conflicts, or null if the merge stopped early due to conflicts. /// The index must be disposed by the caller.</returns> public virtual TransientIndex MergeCommitsIntoIndex(Commit ours, Commit theirs, MergeTreeOptions options) { Ensure.ArgumentNotNull(ours, "ours"); Ensure.ArgumentNotNull(theirs, "theirs"); options = options ?? new MergeTreeOptions(); bool earlyStop; var indexHandle = MergeCommits(ours, theirs, options, out earlyStop); if (earlyStop) { if (indexHandle != null) { indexHandle.Dispose(); } return(null); } var result = new TransientIndex(indexHandle, repo); return(result); }
/// <summary> /// Performs a revert of <paramref name="revertCommit"/> onto <paramref name="revertOnto"/> commit. /// </summary> /// <param name="revertCommit">The commit to revert.</param> /// <param name="revertOnto">The commit to revert onto.</param> /// <param name="mainline">Which commit to consider the parent for the diff when reverting a merge commit.</param> /// <param name="options">The options for the merging in the revert operation.</param> /// <returns>A result containing a <see cref="Tree"/> if the revert was successful and a list of <see cref="Conflict"/>s if it is not.</returns> public virtual MergeTreeResult RevertCommit(Commit revertCommit, Commit revertOnto, int mainline, MergeTreeOptions options) { Ensure.ArgumentNotNull(revertCommit, "revertCommit"); Ensure.ArgumentNotNull(revertOnto, "revertOnto"); 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 opts = new GitMergeOpts { Version = 1, MergeFileFavorFlags = options.MergeFileFavor, MergeTreeFlags = mergeFlags, RenameThreshold = (uint)options.RenameThreshold, TargetLimit = (uint)options.TargetLimit }; bool earlyStop; using (var revertOntoHandle = Proxy.git_object_lookup(repo.Handle, revertOnto.Id, GitObjectType.Commit)) using (var revertCommitHandle = Proxy.git_object_lookup(repo.Handle, revertCommit.Id, GitObjectType.Commit)) using (var indexHandle = Proxy.git_revert_commit(repo.Handle, revertCommitHandle, revertOntoHandle, (uint)mainline, opts, out earlyStop)) { MergeTreeResult revertTreeResult; // 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); } } revertTreeResult = new MergeTreeResult(conflicts); } else { var treeId = Proxy.git_index_write_tree_to(indexHandle, repo.Handle); revertTreeResult = new MergeTreeResult(this.repo.Lookup <Tree>(treeId)); } return(revertTreeResult); } }