1- use libc:: c_uint;
1+ use libc:: { c_uint, c_ushort} ;
2+ use std:: ffi:: CString ;
23use std:: marker;
34use std:: mem;
5+ use std:: ptr;
46use std:: str;
57
68use crate :: call:: Convert ;
79use crate :: util:: Binding ;
10+ use crate :: IntoCString ;
811use crate :: { raw, Commit , FileFavor , Oid } ;
912
1013/// A structure to represent an annotated commit, the input to merge and rebase.
@@ -22,6 +25,19 @@ pub struct MergeOptions {
2225 raw : raw:: git_merge_options ,
2326}
2427
28+ /// Options for merging a file.
29+ pub struct MergeFileOptions {
30+ ancestor_label : Option < CString > ,
31+ our_label : Option < CString > ,
32+ their_label : Option < CString > ,
33+ raw : raw:: git_merge_file_options ,
34+ }
35+
36+ /// Information about file-level merging.
37+ pub struct MergeFileResult {
38+ raw : raw:: git_merge_file_result ,
39+ }
40+
2541impl < ' repo > AnnotatedCommit < ' repo > {
2642 /// Gets the commit ID that the given git_annotated_commit refers to
2743 pub fn id ( & self ) -> Oid {
@@ -192,3 +208,207 @@ impl<'repo> Drop for AnnotatedCommit<'repo> {
192208 unsafe { raw:: git_annotated_commit_free ( self . raw ) }
193209 }
194210}
211+
212+ impl Default for MergeFileOptions {
213+ fn default ( ) -> Self {
214+ Self :: new ( )
215+ }
216+ }
217+
218+ impl MergeFileOptions {
219+ /// Creates a default set of merge file options.
220+ pub fn new ( ) -> MergeFileOptions {
221+ let mut opts = MergeFileOptions {
222+ ancestor_label : None ,
223+ our_label : None ,
224+ their_label : None ,
225+ raw : unsafe { mem:: zeroed ( ) } ,
226+ } ;
227+ assert_eq ! (
228+ unsafe { raw:: git_merge_file_options_init( & mut opts. raw, 1 ) } ,
229+ 0
230+ ) ;
231+ opts
232+ }
233+
234+ /// Label for the ancestor file side of the conflict which will be prepended
235+ /// to labels in diff3-format merge files.
236+ pub fn ancestor_label < T : IntoCString > ( & mut self , t : T ) -> & mut MergeFileOptions {
237+ self . ancestor_label = Some ( t. into_c_string ( ) . unwrap ( ) ) ;
238+
239+ self . raw . ancestor_label = self
240+ . ancestor_label
241+ . as_ref ( )
242+ . map ( |s| s. as_ptr ( ) )
243+ . unwrap_or ( ptr:: null ( ) ) ;
244+
245+ self
246+ }
247+
248+ /// Label for our file side of the conflict which will be prepended to labels
249+ /// in merge files.
250+ pub fn our_label < T : IntoCString > ( & mut self , t : T ) -> & mut MergeFileOptions {
251+ self . our_label = Some ( t. into_c_string ( ) . unwrap ( ) ) ;
252+
253+ self . raw . our_label = self
254+ . our_label
255+ . as_ref ( )
256+ . map ( |s| s. as_ptr ( ) )
257+ . unwrap_or ( ptr:: null ( ) ) ;
258+
259+ self
260+ }
261+
262+ /// Label for their file side of the conflict which will be prepended to labels
263+ /// in merge files.
264+ pub fn their_label < T : IntoCString > ( & mut self , t : T ) -> & mut MergeFileOptions {
265+ self . their_label = Some ( t. into_c_string ( ) . unwrap ( ) ) ;
266+
267+ self . raw . their_label = self
268+ . their_label
269+ . as_ref ( )
270+ . map ( |s| s. as_ptr ( ) )
271+ . unwrap_or ( ptr:: null ( ) ) ;
272+
273+ self
274+ }
275+
276+ /// Specify a side to favor for resolving conflicts
277+ pub fn favor ( & mut self , favor : FileFavor ) -> & mut MergeFileOptions {
278+ self . raw . favor = favor. convert ( ) ;
279+ self
280+ }
281+
282+ fn flag ( & mut self , opt : raw:: git_merge_file_flag_t , val : bool ) -> & mut MergeFileOptions {
283+ if val {
284+ self . raw . flags |= opt as u32 ;
285+ } else {
286+ self . raw . flags &= !opt as u32 ;
287+ }
288+ self
289+ }
290+
291+ /// Create standard conflicted merge files
292+ pub fn style_standard ( & mut self , standard : bool ) -> & mut MergeFileOptions {
293+ self . flag ( raw:: GIT_MERGE_FILE_STYLE_MERGE , standard)
294+ }
295+
296+ /// Create diff3-style file
297+ pub fn style_diff3 ( & mut self , diff3 : bool ) -> & mut MergeFileOptions {
298+ self . flag ( raw:: GIT_MERGE_FILE_STYLE_DIFF3 , diff3)
299+ }
300+
301+ /// Condense non-alphanumeric regions for simplified diff file
302+ pub fn simplify_alnum ( & mut self , simplify : bool ) -> & mut MergeFileOptions {
303+ self . flag ( raw:: GIT_MERGE_FILE_SIMPLIFY_ALNUM , simplify)
304+ }
305+
306+ /// Ignore all whitespace
307+ pub fn ignore_whitespace ( & mut self , ignore : bool ) -> & mut MergeFileOptions {
308+ self . flag ( raw:: GIT_MERGE_FILE_IGNORE_WHITESPACE , ignore)
309+ }
310+
311+ /// Ignore changes in amount of whitespace
312+ pub fn ignore_whitespace_change ( & mut self , ignore : bool ) -> & mut MergeFileOptions {
313+ self . flag ( raw:: GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE , ignore)
314+ }
315+
316+ /// Ignore whitespace at end of line
317+ pub fn ignore_whitespace_eol ( & mut self , ignore : bool ) -> & mut MergeFileOptions {
318+ self . flag ( raw:: GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL , ignore)
319+ }
320+
321+ /// Use the "patience diff" algorithm
322+ pub fn patience ( & mut self , patience : bool ) -> & mut MergeFileOptions {
323+ self . flag ( raw:: GIT_MERGE_FILE_DIFF_PATIENCE , patience)
324+ }
325+
326+ /// Take extra time to find minimal diff
327+ pub fn minimal ( & mut self , minimal : bool ) -> & mut MergeFileOptions {
328+ self . flag ( raw:: GIT_MERGE_FILE_DIFF_MINIMAL , minimal)
329+ }
330+
331+ /// Create zdiff3 ("zealous diff3")-style files
332+ pub fn style_zdiff3 ( & mut self , zdiff3 : bool ) -> & mut MergeFileOptions {
333+ self . flag ( raw:: GIT_MERGE_FILE_STYLE_ZDIFF3 , zdiff3)
334+ }
335+
336+ /// Do not produce file conflicts when common regions have changed
337+ pub fn accept_conflicts ( & mut self , accept : bool ) -> & mut MergeFileOptions {
338+ self . flag ( raw:: GIT_MERGE_FILE_ACCEPT_CONFLICTS , accept)
339+ }
340+
341+ /// The size of conflict markers (eg, "<<<<<<<"). Default is 7.
342+ pub fn marker_size ( & mut self , size : u16 ) -> & mut MergeFileOptions {
343+ self . raw . marker_size = size as c_ushort ;
344+ self
345+ }
346+
347+ /// Acquire a pointer to the underlying raw options.
348+ ///
349+ /// # Safety
350+ /// The pointer used here (or its contents) should not outlive self.
351+ pub ( crate ) unsafe fn raw ( & mut self ) -> * const raw:: git_merge_file_options {
352+ & self . raw
353+ }
354+ }
355+
356+ impl MergeFileResult {
357+ /// True if the output was automerged, false if the output contains
358+ /// conflict markers.
359+ pub fn is_automergeable ( & self ) -> bool {
360+ self . raw . automergeable > 0
361+ }
362+
363+ /// The path that the resultant merge file should use.
364+ ///
365+ /// returns `None` if a filename conflict would occur,
366+ /// or if the path is not valid utf-8
367+ pub fn path ( & self ) -> Option < & str > {
368+ self . path_bytes ( )
369+ . and_then ( |bytes| str:: from_utf8 ( bytes) . ok ( ) )
370+ }
371+
372+ /// Gets the path as a byte slice.
373+ pub fn path_bytes ( & self ) -> Option < & [ u8 ] > {
374+ unsafe { crate :: opt_bytes ( self , self . raw . path ) }
375+ }
376+
377+ /// The mode that the resultant merge file should use.
378+ pub fn mode ( & self ) -> u32 {
379+ self . raw . mode as u32
380+ }
381+
382+ /// The contents of the merge.
383+ pub fn content ( & self ) -> & [ u8 ] {
384+ unsafe { std:: slice:: from_raw_parts ( self . raw . ptr as * const u8 , self . raw . len as usize ) }
385+ }
386+ }
387+
388+ impl Binding for MergeFileResult {
389+ type Raw = raw:: git_merge_file_result ;
390+ unsafe fn from_raw ( raw : raw:: git_merge_file_result ) -> MergeFileResult {
391+ MergeFileResult { raw }
392+ }
393+ fn raw ( & self ) -> raw:: git_merge_file_result {
394+ unimplemented ! ( )
395+ }
396+ }
397+
398+ impl Drop for MergeFileResult {
399+ fn drop ( & mut self ) {
400+ unsafe { raw:: git_merge_file_result_free ( & mut self . raw ) }
401+ }
402+ }
403+
404+ impl std:: fmt:: Debug for MergeFileResult {
405+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
406+ let mut ds = f. debug_struct ( "MergeFileResult" ) ;
407+ if let Some ( path) = & self . path ( ) {
408+ ds. field ( "path" , path) ;
409+ }
410+ ds. field ( "automergeable" , & self . is_automergeable ( ) ) ;
411+ ds. field ( "mode" , & self . mode ( ) ) ;
412+ ds. finish ( )
413+ }
414+ }
0 commit comments