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