1 use libc::c_uint; 2 use std::marker; 3 use std::mem; 4 use std::str; 5 6 use crate::call::Convert; 7 use crate::util::Binding; 8 use crate::{raw, Commit, FileFavor, Oid}; 9 10 /// A structure to represent an annotated commit, the input to merge and rebase. 11 /// 12 /// An annotated commit contains information about how it was looked up, which 13 /// may be useful for functions like merge or rebase to provide context to the 14 /// operation. 15 pub struct AnnotatedCommit<'repo> { 16 raw: *mut raw::git_annotated_commit, 17 _marker: marker::PhantomData<Commit<'repo>>, 18 } 19 20 /// Options to specify when merging. 21 pub struct MergeOptions { 22 raw: raw::git_merge_options, 23 } 24 25 impl<'repo> AnnotatedCommit<'repo> { 26 /// Gets the commit ID that the given git_annotated_commit refers to id(&self) -> Oid27 pub fn id(&self) -> Oid { 28 unsafe { Binding::from_raw(raw::git_annotated_commit_id(self.raw)) } 29 } 30 31 /// Get the refname that the given git_annotated_commit refers to 32 /// 33 /// Returns None if it is not valid utf8 refname(&self) -> Option<&str>34 pub fn refname(&self) -> Option<&str> { 35 str::from_utf8(self.refname_bytes()).ok() 36 } 37 38 /// Get the refname that the given git_annotated_commit refers to. refname_bytes(&self) -> &[u8]39 pub fn refname_bytes(&self) -> &[u8] { 40 unsafe { crate::opt_bytes(self, raw::git_annotated_commit_ref(&*self.raw)).unwrap() } 41 } 42 } 43 44 impl Default for MergeOptions { default() -> Self45 fn default() -> Self { 46 Self::new() 47 } 48 } 49 50 impl MergeOptions { 51 /// Creates a default set of merge options. new() -> MergeOptions52 pub fn new() -> MergeOptions { 53 let mut opts = MergeOptions { 54 raw: unsafe { mem::zeroed() }, 55 }; 56 assert_eq!(unsafe { raw::git_merge_init_options(&mut opts.raw, 1) }, 0); 57 opts 58 } 59 flag(&mut self, opt: u32, val: bool) -> &mut MergeOptions60 fn flag(&mut self, opt: u32, val: bool) -> &mut MergeOptions { 61 if val { 62 self.raw.flags |= opt; 63 } else { 64 self.raw.flags &= !opt; 65 } 66 self 67 } 68 69 /// Detect file renames find_renames(&mut self, find: bool) -> &mut MergeOptions70 pub fn find_renames(&mut self, find: bool) -> &mut MergeOptions { 71 self.flag(raw::GIT_MERGE_FIND_RENAMES as u32, find) 72 } 73 74 /// If a conflict occurs, exit immediately instead of attempting to continue 75 /// resolving conflicts fail_on_conflict(&mut self, fail: bool) -> &mut MergeOptions76 pub fn fail_on_conflict(&mut self, fail: bool) -> &mut MergeOptions { 77 self.flag(raw::GIT_MERGE_FAIL_ON_CONFLICT as u32, fail) 78 } 79 80 /// Do not write the REUC extension on the generated index skip_reuc(&mut self, skip: bool) -> &mut MergeOptions81 pub fn skip_reuc(&mut self, skip: bool) -> &mut MergeOptions { 82 self.flag(raw::GIT_MERGE_FAIL_ON_CONFLICT as u32, skip) 83 } 84 85 /// If the commits being merged have multiple merge bases, do not build a 86 /// recursive merge base (by merging the multiple merge bases), instead 87 /// simply use the first base. no_recursive(&mut self, disable: bool) -> &mut MergeOptions88 pub fn no_recursive(&mut self, disable: bool) -> &mut MergeOptions { 89 self.flag(raw::GIT_MERGE_NO_RECURSIVE as u32, disable) 90 } 91 92 /// Similarity to consider a file renamed (default 50) rename_threshold(&mut self, thresh: u32) -> &mut MergeOptions93 pub fn rename_threshold(&mut self, thresh: u32) -> &mut MergeOptions { 94 self.raw.rename_threshold = thresh; 95 self 96 } 97 98 /// Maximum similarity sources to examine for renames (default 200). 99 /// If the number of rename candidates (add / delete pairs) is greater 100 /// than this value, inexact rename detection is aborted. This setting 101 /// overrides the `merge.renameLimit` configuration value. target_limit(&mut self, limit: u32) -> &mut MergeOptions102 pub fn target_limit(&mut self, limit: u32) -> &mut MergeOptions { 103 self.raw.target_limit = limit as c_uint; 104 self 105 } 106 107 /// Maximum number of times to merge common ancestors to build a 108 /// virtual merge base when faced with criss-cross merges. When 109 /// this limit is reached, the next ancestor will simply be used 110 /// instead of attempting to merge it. The default is unlimited. recursion_limit(&mut self, limit: u32) -> &mut MergeOptions111 pub fn recursion_limit(&mut self, limit: u32) -> &mut MergeOptions { 112 self.raw.recursion_limit = limit as c_uint; 113 self 114 } 115 116 /// Specify a side to favor for resolving conflicts file_favor(&mut self, favor: FileFavor) -> &mut MergeOptions117 pub fn file_favor(&mut self, favor: FileFavor) -> &mut MergeOptions { 118 self.raw.file_favor = favor.convert(); 119 self 120 } 121 file_flag(&mut self, opt: u32, val: bool) -> &mut MergeOptions122 fn file_flag(&mut self, opt: u32, val: bool) -> &mut MergeOptions { 123 if val { 124 self.raw.file_flags |= opt; 125 } else { 126 self.raw.file_flags &= !opt; 127 } 128 self 129 } 130 131 /// Create standard conflicted merge files standard_style(&mut self, standard: bool) -> &mut MergeOptions132 pub fn standard_style(&mut self, standard: bool) -> &mut MergeOptions { 133 self.file_flag(raw::GIT_MERGE_FILE_STYLE_MERGE as u32, standard) 134 } 135 136 /// Create diff3-style file diff3_style(&mut self, diff3: bool) -> &mut MergeOptions137 pub fn diff3_style(&mut self, diff3: bool) -> &mut MergeOptions { 138 self.file_flag(raw::GIT_MERGE_FILE_STYLE_DIFF3 as u32, diff3) 139 } 140 141 /// Condense non-alphanumeric regions for simplified diff file simplify_alnum(&mut self, simplify: bool) -> &mut MergeOptions142 pub fn simplify_alnum(&mut self, simplify: bool) -> &mut MergeOptions { 143 self.file_flag(raw::GIT_MERGE_FILE_SIMPLIFY_ALNUM as u32, simplify) 144 } 145 146 /// Ignore all whitespace ignore_whitespace(&mut self, ignore: bool) -> &mut MergeOptions147 pub fn ignore_whitespace(&mut self, ignore: bool) -> &mut MergeOptions { 148 self.file_flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE as u32, ignore) 149 } 150 151 /// Ignore changes in amount of whitespace ignore_whitespace_change(&mut self, ignore: bool) -> &mut MergeOptions152 pub fn ignore_whitespace_change(&mut self, ignore: bool) -> &mut MergeOptions { 153 self.file_flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE as u32, ignore) 154 } 155 156 /// Ignore whitespace at end of line ignore_whitespace_eol(&mut self, ignore: bool) -> &mut MergeOptions157 pub fn ignore_whitespace_eol(&mut self, ignore: bool) -> &mut MergeOptions { 158 self.file_flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL as u32, ignore) 159 } 160 161 /// Use the "patience diff" algorithm patience(&mut self, patience: bool) -> &mut MergeOptions162 pub fn patience(&mut self, patience: bool) -> &mut MergeOptions { 163 self.file_flag(raw::GIT_MERGE_FILE_DIFF_PATIENCE as u32, patience) 164 } 165 166 /// Take extra time to find minimal diff minimal(&mut self, minimal: bool) -> &mut MergeOptions167 pub fn minimal(&mut self, minimal: bool) -> &mut MergeOptions { 168 self.file_flag(raw::GIT_MERGE_FILE_DIFF_MINIMAL as u32, minimal) 169 } 170 171 /// Acquire a pointer to the underlying raw options. raw(&self) -> *const raw::git_merge_options172 pub unsafe fn raw(&self) -> *const raw::git_merge_options { 173 &self.raw as *const _ 174 } 175 } 176 177 impl<'repo> Binding for AnnotatedCommit<'repo> { 178 type Raw = *mut raw::git_annotated_commit; from_raw(raw: *mut raw::git_annotated_commit) -> AnnotatedCommit<'repo>179 unsafe fn from_raw(raw: *mut raw::git_annotated_commit) -> AnnotatedCommit<'repo> { 180 AnnotatedCommit { 181 raw: raw, 182 _marker: marker::PhantomData, 183 } 184 } raw(&self) -> *mut raw::git_annotated_commit185 fn raw(&self) -> *mut raw::git_annotated_commit { 186 self.raw 187 } 188 } 189 190 impl<'repo> Drop for AnnotatedCommit<'repo> { drop(&mut self)191 fn drop(&mut self) { 192 unsafe { raw::git_annotated_commit_free(self.raw) } 193 } 194 } 195