@@ -808,6 +808,74 @@ public virtual MergeTreeResult MergeCommits(Commit ours, Commit theirs, MergeTre
808
808
}
809
809
}
810
810
811
+ /// <summary>
812
+ /// Perform a three-way merge of two trees relative to the provided ancestor.
813
+ /// The returned <see cref="MergeTreeResult"/> will contain the results
814
+ /// of the merge and can be examined for conflicts.
815
+ /// </summary>
816
+ /// <param name="ancestor">The ancestor tree</param>
817
+ /// <param name="ours">The first tree</param>
818
+ /// <param name="theirs">The second tree</param>
819
+ /// <param name="options">The <see cref="MergeTreeOptions"/> controlling the merge</param>
820
+ /// <returns>The <see cref="MergeTreeResult"/> containing the merged trees and any conflicts</returns>
821
+ public virtual MergeTreeResult MergeTrees ( Tree ancestor , Tree ours , Tree theirs , MergeTreeOptions options )
822
+ {
823
+ Ensure . ArgumentNotNull ( ancestor , "ancestor" ) ;
824
+ Ensure . ArgumentNotNull ( ours , "ours" ) ;
825
+ Ensure . ArgumentNotNull ( theirs , "theirs" ) ;
826
+
827
+ var modifiedOptions = new MergeTreeOptions ( ) ;
828
+
829
+ // We throw away the index after looking at the conflicts, so we'll never need the REUC
830
+ // entries to be there
831
+ modifiedOptions . SkipReuc = true ;
832
+
833
+ if ( options != null )
834
+ {
835
+ modifiedOptions . FailOnConflict = options . FailOnConflict ;
836
+ modifiedOptions . FindRenames = options . FindRenames ;
837
+ modifiedOptions . IgnoreWhitespaceChange = options . IgnoreWhitespaceChange ;
838
+ modifiedOptions . MergeFileFavor = options . MergeFileFavor ;
839
+ modifiedOptions . RenameThreshold = options . RenameThreshold ;
840
+ modifiedOptions . TargetLimit = options . TargetLimit ;
841
+ }
842
+
843
+ bool earlyStop ;
844
+ using ( var indexHandle = MergeTrees ( ancestor , ours , theirs , modifiedOptions , out earlyStop ) )
845
+ {
846
+ MergeTreeResult mergeResult ;
847
+
848
+ // Stopped due to FailOnConflict so there's no index or conflict list
849
+ if ( earlyStop )
850
+ {
851
+ return new MergeTreeResult ( new Conflict [ ] { } ) ;
852
+ }
853
+
854
+ if ( Proxy . git_index_has_conflicts ( indexHandle ) )
855
+ {
856
+ List < Conflict > conflicts = new List < Conflict > ( ) ;
857
+ Conflict conflict ;
858
+
859
+ using ( ConflictIteratorHandle iterator = Proxy . git_index_conflict_iterator_new ( indexHandle ) )
860
+ {
861
+ while ( ( conflict = Proxy . git_index_conflict_next ( iterator ) ) != null )
862
+ {
863
+ conflicts . Add ( conflict ) ;
864
+ }
865
+ }
866
+
867
+ mergeResult = new MergeTreeResult ( conflicts ) ;
868
+ }
869
+ else
870
+ {
871
+ var treeId = Proxy . git_index_write_tree_to ( indexHandle , repo . Handle ) ;
872
+ mergeResult = new MergeTreeResult ( this . repo . Lookup < Tree > ( treeId ) ) ;
873
+ }
874
+
875
+ return mergeResult ;
876
+ }
877
+ }
878
+
811
879
/// <summary>
812
880
/// Packs all the objects in the <see cref="ObjectDatabase"/> and write a pack (.pack) and index (.idx) files for them.
813
881
/// </summary>
@@ -940,6 +1008,50 @@ private IndexHandle MergeCommits(Commit ours, Commit theirs, MergeTreeOptions op
940
1008
}
941
1009
}
942
1010
1011
+ /// <summary>
1012
+ /// Perform a three-way merge of two trees relative to the provided ancestor.
1013
+ /// The returned <see cref="MergeTreeResult"/> will contain the results
1014
+ /// of the merge and can be examined for conflicts.
1015
+ /// </summary>
1016
+ /// <param name="ancestor">The ancestor tree</param>
1017
+ /// <param name="ours">The first tree</param>
1018
+ /// <param name="theirs">The second tree</param>
1019
+ /// <param name="options">The <see cref="MergeTreeOptions"/> controlling the merge</param>
1020
+ /// <param name="earlyStop">True if the merge stopped early due to conflicts</param>
1021
+ /// <returns>The <see cref="MergeTreeResult"/> containing the merged trees and any conflicts</returns>
1022
+ private IndexHandle MergeTrees ( Tree ancestor , Tree ours , Tree theirs , MergeTreeOptions options , out bool earlyStop )
1023
+ {
1024
+ GitMergeFlag mergeFlags = GitMergeFlag . GIT_MERGE_NORMAL ;
1025
+ if ( options . SkipReuc )
1026
+ {
1027
+ mergeFlags |= GitMergeFlag . GIT_MERGE_SKIP_REUC ;
1028
+ }
1029
+ if ( options . FindRenames )
1030
+ {
1031
+ mergeFlags |= GitMergeFlag . GIT_MERGE_FIND_RENAMES ;
1032
+ }
1033
+ if ( options . FailOnConflict )
1034
+ {
1035
+ mergeFlags |= GitMergeFlag . GIT_MERGE_FAIL_ON_CONFLICT ;
1036
+ }
1037
+
1038
+ var mergeOptions = new GitMergeOpts
1039
+ {
1040
+ Version = 1 ,
1041
+ MergeFileFavorFlags = options . MergeFileFavor ,
1042
+ MergeTreeFlags = mergeFlags ,
1043
+ RenameThreshold = ( uint ) options . RenameThreshold ,
1044
+ TargetLimit = ( uint ) options . TargetLimit ,
1045
+ } ;
1046
+ using ( var ancestorHandle = Proxy . git_object_lookup ( repo . Handle , ancestor . Id , GitObjectType . Tree ) )
1047
+ using ( var oursHandle = Proxy . git_object_lookup ( repo . Handle , ours . Id , GitObjectType . Tree ) )
1048
+ using ( var theirHandle = Proxy . git_object_lookup ( repo . Handle , theirs . Id , GitObjectType . Tree ) )
1049
+ {
1050
+ var indexHandle = Proxy . git_merge_trees ( repo . Handle , ancestorHandle , oursHandle , theirHandle , mergeOptions , out earlyStop ) ;
1051
+ return indexHandle ;
1052
+ }
1053
+ }
1054
+
943
1055
/// <summary>
944
1056
/// Performs a cherry-pick of <paramref name="cherryPickCommit"/> onto <paramref name="cherryPickOnto"/> commit.
945
1057
/// </summary>
0 commit comments