@@ -4,6 +4,7 @@ use crate::{
44 utils:: Classifier ,
55} ;
66use std:: { cmp, fmt} ;
7+ use std:: hash:: Hash ;
78
89#[ cfg( test) ]
910mod tests;
@@ -205,6 +206,34 @@ impl MergeOptions {
205206 self . style ,
206207 )
207208 }
209+
210+ pub fn merge_custom < ' a , T : Eq + Hash > (
211+ & self ,
212+ ancestor : & ' a [ T ] ,
213+ ours : & ' a [ T ] ,
214+ theirs : & ' a [ T ] ,
215+ ) -> Result < Vec < & ' a T > , MergeConflicts > {
216+ let mut classifier = Classifier :: default ( ) ;
217+ let ( ancestor_lines, ancestor_ids) = classifier. classify ( ancestor) ;
218+ let ( our_lines, our_ids) = classifier. classify ( ours) ;
219+ let ( their_lines, their_ids) = classifier. classify ( theirs) ;
220+
221+ let opts = DiffOptions :: default ( ) ;
222+ let our_solution = opts. diff_slice ( & ancestor_ids, & our_ids) ;
223+ let their_solution = opts. diff_slice ( & ancestor_ids, & their_ids) ;
224+
225+ let merged = merge_solutions ( & our_solution, & their_solution) ;
226+ let mut merge = diff3_range_to_merge_range ( & merged) ;
227+
228+ cleanup_conflicts ( & mut merge) ;
229+
230+ output_result_custom (
231+ & ancestor_lines,
232+ & our_lines,
233+ & their_lines,
234+ & merge,
235+ )
236+ }
208237}
209238
210239impl Default for MergeOptions {
@@ -277,6 +306,30 @@ pub fn merge_bytes<'a>(
277306 MergeOptions :: default ( ) . merge_bytes ( ancestor, ours, theirs)
278307}
279308
309+ /// Infos about a merge that went wrong
310+ #[ derive( Debug , Ord , PartialOrd , Eq , PartialEq ) ]
311+ pub struct MergeConflicts {
312+ /// How many conflicts have occurred
313+ pub count : usize
314+ }
315+
316+ impl std:: fmt:: Display for MergeConflicts {
317+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> Result < ( ) , std:: fmt:: Error > {
318+ write ! ( f, "{} merge conflicts" , self . count)
319+ }
320+ }
321+
322+ impl std:: error:: Error for MergeConflicts { }
323+
324+ /// Perform a 3-way merge between any list of values that support it
325+ pub fn merge_custom < ' a , T : Eq + Hash > (
326+ ancestor : & ' a [ T ] ,
327+ ours : & ' a [ T ] ,
328+ theirs : & ' a [ T ] ,
329+ ) -> Result < Vec < & ' a T > , MergeConflicts > {
330+ MergeOptions :: default ( ) . merge_custom ( ancestor, ours, theirs)
331+ }
332+
280333fn merge_solutions < ' ancestor , ' ours , ' theirs , T : ?Sized + SliceLike > (
281334 our_solution : & [ DiffRange < ' ancestor , ' ours , T > ] ,
282335 their_solution : & [ DiffRange < ' ancestor , ' theirs , T > ] ,
@@ -635,3 +688,39 @@ fn add_conflict_marker_bytes(
635688 }
636689 output. push ( b'\n' ) ;
637690}
691+
692+ fn output_result_custom < ' a , T : Eq + Hash > (
693+ ancestor : & [ & ' a T ] ,
694+ ours : & [ & ' a T ] ,
695+ theirs : & [ & ' a T ] ,
696+ merge : & [ MergeRange < [ u64 ] > ] ,
697+ ) -> Result < Vec < & ' a T > , MergeConflicts > {
698+ let mut conflicts = 0 ;
699+ let mut output = Vec :: new ( ) ;
700+
701+ for merge_range in merge {
702+ match merge_range {
703+ MergeRange :: Equal ( range, ..) => {
704+ output. extend ( ancestor[ range. range ( ) ] . iter ( ) . copied ( ) ) ;
705+ }
706+ MergeRange :: Conflict ( _ancestor_range, _ours_range, _theirs_range) => {
707+ conflicts += 1 ;
708+ }
709+ MergeRange :: Ours ( range) => {
710+ output. extend ( ours[ range. range ( ) ] . iter ( ) . copied ( ) ) ;
711+ }
712+ MergeRange :: Theirs ( range) => {
713+ output. extend ( theirs[ range. range ( ) ] . iter ( ) . copied ( ) ) ;
714+ }
715+ MergeRange :: Both ( range, _) => {
716+ output. extend ( ours[ range. range ( ) ] . iter ( ) . copied ( ) ) ;
717+ }
718+ }
719+ }
720+
721+ if conflicts != 0 {
722+ Err ( MergeConflicts { count : conflicts } )
723+ } else {
724+ Ok ( output)
725+ }
726+ }
0 commit comments