1- use libc:: { c_uint, c_ushort} ;
1+ use libc:: { c_char , c_uint, c_ushort, size_t } ;
22use std:: ffi:: CString ;
33use std:: marker;
44use std:: mem;
@@ -7,8 +7,7 @@ use std::str;
77
88use crate :: call:: Convert ;
99use crate :: util:: Binding ;
10- use crate :: IntoCString ;
11- use crate :: { raw, Commit , FileFavor , Oid } ;
10+ use crate :: { raw, Commit , Error , FileFavor , FileMode , IntoCString , Oid } ;
1211
1312/// A structure to represent an annotated commit, the input to merge and rebase.
1413///
@@ -412,3 +411,147 @@ impl std::fmt::Debug for MergeFileResult {
412411 ds. finish ( )
413412 }
414413}
414+
415+ /// Input structure for merging a file.
416+ pub struct MergeFileInput < ' a > {
417+ raw : raw:: git_merge_file_input ,
418+ path : Option < CString > ,
419+ content : Option < & ' a [ u8 ] > ,
420+ }
421+
422+ impl Default for MergeFileInput < ' _ > {
423+ fn default ( ) -> Self {
424+ Self :: new ( )
425+ }
426+ }
427+
428+ impl Binding for MergeFileInput < ' _ > {
429+ type Raw = * const raw:: git_merge_file_input ;
430+
431+ unsafe fn from_raw ( _raw : * const raw:: git_merge_file_input ) -> MergeFileInput < ' static > {
432+ panic ! ( "unimplemened" )
433+ }
434+
435+ fn raw ( & self ) -> * const raw:: git_merge_file_input {
436+ & self . raw as * const _
437+ }
438+ }
439+
440+ impl < ' a > MergeFileInput < ' a > {
441+ /// Creates a new, empty merge file input.
442+ pub fn new ( ) -> MergeFileInput < ' a > {
443+ let mut input = MergeFileInput {
444+ raw : unsafe { mem:: zeroed ( ) } ,
445+ path : None ,
446+ content : None ,
447+ } ;
448+ assert_eq ! (
449+ unsafe { raw:: git_merge_file_input_init( & mut input. raw, 1 ) } ,
450+ 0
451+ ) ;
452+ input
453+ }
454+
455+ /// Sets the content of the file.
456+ pub fn content ( & mut self , content : & ' a [ u8 ] ) -> & mut MergeFileInput < ' a > {
457+ self . content = Some ( content) ;
458+
459+ self . raw . ptr = content. as_ptr ( ) as * const c_char ;
460+ self . raw . size = content. len ( ) as size_t ;
461+
462+ self
463+ }
464+
465+ /// Sets the path of the file.
466+ pub fn path < T : IntoCString > ( & mut self , t : T ) -> & mut MergeFileInput < ' a > {
467+ self . path = Some ( t. into_c_string ( ) . unwrap ( ) ) ;
468+
469+ self . raw . path = self
470+ . path
471+ . as_ref ( )
472+ . map ( |s| s. as_ptr ( ) )
473+ . unwrap_or ( ptr:: null ( ) ) ;
474+
475+ self
476+ }
477+
478+ /// Sets the mode of the file.
479+ pub fn mode ( & mut self , mode : Option < FileMode > ) -> & mut MergeFileInput < ' a > {
480+ self . raw . mode = mode. map_or ( 0 , u32:: from) ;
481+ self
482+ }
483+ }
484+
485+ /// Merges three versions of a file: an ancestor version, our version, and their version.
486+ pub fn merge_file (
487+ ancestor : & MergeFileInput < ' _ > ,
488+ ours : & MergeFileInput < ' _ > ,
489+ theirs : & MergeFileInput < ' _ > ,
490+ opts : Option < & mut MergeFileOptions > ,
491+ ) -> Result < MergeFileResult , Error > {
492+ unsafe {
493+ let ancestor = ancestor. raw ( ) ;
494+ let ours = ours. raw ( ) ;
495+ let theirs = theirs. raw ( ) ;
496+
497+ let mut ret = mem:: zeroed ( ) ;
498+ try_call ! ( raw:: git_merge_file(
499+ & mut ret,
500+ ancestor,
501+ ours,
502+ theirs,
503+ opts. map( |o| o. raw( ) ) . unwrap_or( ptr:: null( ) )
504+ ) ) ;
505+ Ok ( Binding :: from_raw ( ret) )
506+ }
507+ }
508+
509+ #[ cfg( test) ]
510+ mod tests {
511+ use crate :: { MergeFileInput , MergeFileOptions } ;
512+
513+ use std:: path:: Path ;
514+
515+ #[ test]
516+ fn smoke_merge_file ( ) {
517+ let file_path = Path :: new ( "file" ) ;
518+ let base = {
519+ let mut input = MergeFileInput :: new ( ) ;
520+ input. content ( b"base" ) . path ( file_path) ;
521+ input
522+ } ;
523+
524+ let ours = {
525+ let mut input = MergeFileInput :: new ( ) ;
526+ input. content ( b"foo" ) . path ( file_path) ;
527+ input
528+ } ;
529+
530+ let theirs = {
531+ let mut input = MergeFileInput :: new ( ) ;
532+ input. content ( b"bar" ) . path ( file_path) ;
533+ input
534+ } ;
535+
536+ let mut opts = MergeFileOptions :: new ( ) ;
537+ opts. ancestor_label ( "ancestor" ) ;
538+ opts. our_label ( "ours" ) ;
539+ opts. their_label ( "theirs" ) ;
540+ opts. style_diff3 ( true ) ;
541+ let merge_file_result = crate :: merge_file ( & base, & ours, & theirs, Some ( & mut opts) ) . unwrap ( ) ;
542+
543+ assert ! ( !merge_file_result. is_automergeable( ) ) ;
544+ assert_eq ! ( merge_file_result. path( ) , Some ( "file" ) ) ;
545+ assert_eq ! (
546+ String :: from_utf8_lossy( merge_file_result. content( ) ) . to_string( ) ,
547+ r"<<<<<<< ours
548+ foo
549+ ||||||| ancestor
550+ base
551+ =======
552+ bar
553+ >>>>>>> theirs
554+ " ,
555+ ) ;
556+ }
557+ }
0 commit comments