diff options
Diffstat (limited to 'extra/git2/src/merge.rs')
-rw-r--r-- | extra/git2/src/merge.rs | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/extra/git2/src/merge.rs b/extra/git2/src/merge.rs new file mode 100644 index 000000000..6bd30c10d --- /dev/null +++ b/extra/git2/src/merge.rs @@ -0,0 +1,194 @@ +use libc::c_uint; +use std::marker; +use std::mem; +use std::str; + +use crate::call::Convert; +use crate::util::Binding; +use crate::{raw, Commit, FileFavor, Oid}; + +/// A structure to represent an annotated commit, the input to merge and rebase. +/// +/// An annotated commit contains information about how it was looked up, which +/// may be useful for functions like merge or rebase to provide context to the +/// operation. +pub struct AnnotatedCommit<'repo> { + raw: *mut raw::git_annotated_commit, + _marker: marker::PhantomData<Commit<'repo>>, +} + +/// Options to specify when merging. +pub struct MergeOptions { + raw: raw::git_merge_options, +} + +impl<'repo> AnnotatedCommit<'repo> { + /// Gets the commit ID that the given git_annotated_commit refers to + pub fn id(&self) -> Oid { + unsafe { Binding::from_raw(raw::git_annotated_commit_id(self.raw)) } + } + + /// Get the refname that the given git_annotated_commit refers to + /// + /// Returns None if it is not valid utf8 + pub fn refname(&self) -> Option<&str> { + str::from_utf8(self.refname_bytes()).ok() + } + + /// Get the refname that the given git_annotated_commit refers to. + pub fn refname_bytes(&self) -> &[u8] { + unsafe { crate::opt_bytes(self, raw::git_annotated_commit_ref(&*self.raw)).unwrap() } + } +} + +impl Default for MergeOptions { + fn default() -> Self { + Self::new() + } +} + +impl MergeOptions { + /// Creates a default set of merge options. + pub fn new() -> MergeOptions { + let mut opts = MergeOptions { + raw: unsafe { mem::zeroed() }, + }; + assert_eq!(unsafe { raw::git_merge_init_options(&mut opts.raw, 1) }, 0); + opts + } + + fn flag(&mut self, opt: u32, val: bool) -> &mut MergeOptions { + if val { + self.raw.flags |= opt; + } else { + self.raw.flags &= !opt; + } + self + } + + /// Detect file renames + pub fn find_renames(&mut self, find: bool) -> &mut MergeOptions { + self.flag(raw::GIT_MERGE_FIND_RENAMES as u32, find) + } + + /// If a conflict occurs, exit immediately instead of attempting to continue + /// resolving conflicts + pub fn fail_on_conflict(&mut self, fail: bool) -> &mut MergeOptions { + self.flag(raw::GIT_MERGE_FAIL_ON_CONFLICT as u32, fail) + } + + /// Do not write the REUC extension on the generated index + pub fn skip_reuc(&mut self, skip: bool) -> &mut MergeOptions { + self.flag(raw::GIT_MERGE_FAIL_ON_CONFLICT as u32, skip) + } + + /// If the commits being merged have multiple merge bases, do not build a + /// recursive merge base (by merging the multiple merge bases), instead + /// simply use the first base. + pub fn no_recursive(&mut self, disable: bool) -> &mut MergeOptions { + self.flag(raw::GIT_MERGE_NO_RECURSIVE as u32, disable) + } + + /// Similarity to consider a file renamed (default 50) + pub fn rename_threshold(&mut self, thresh: u32) -> &mut MergeOptions { + self.raw.rename_threshold = thresh; + self + } + + /// Maximum similarity sources to examine for renames (default 200). + /// If the number of rename candidates (add / delete pairs) is greater + /// than this value, inexact rename detection is aborted. This setting + /// overrides the `merge.renameLimit` configuration value. + pub fn target_limit(&mut self, limit: u32) -> &mut MergeOptions { + self.raw.target_limit = limit as c_uint; + self + } + + /// Maximum number of times to merge common ancestors to build a + /// virtual merge base when faced with criss-cross merges. When + /// this limit is reached, the next ancestor will simply be used + /// instead of attempting to merge it. The default is unlimited. + pub fn recursion_limit(&mut self, limit: u32) -> &mut MergeOptions { + self.raw.recursion_limit = limit as c_uint; + self + } + + /// Specify a side to favor for resolving conflicts + pub fn file_favor(&mut self, favor: FileFavor) -> &mut MergeOptions { + self.raw.file_favor = favor.convert(); + self + } + + fn file_flag(&mut self, opt: u32, val: bool) -> &mut MergeOptions { + if val { + self.raw.file_flags |= opt; + } else { + self.raw.file_flags &= !opt; + } + self + } + + /// Create standard conflicted merge files + pub fn standard_style(&mut self, standard: bool) -> &mut MergeOptions { + self.file_flag(raw::GIT_MERGE_FILE_STYLE_MERGE as u32, standard) + } + + /// Create diff3-style file + pub fn diff3_style(&mut self, diff3: bool) -> &mut MergeOptions { + self.file_flag(raw::GIT_MERGE_FILE_STYLE_DIFF3 as u32, diff3) + } + + /// Condense non-alphanumeric regions for simplified diff file + pub fn simplify_alnum(&mut self, simplify: bool) -> &mut MergeOptions { + self.file_flag(raw::GIT_MERGE_FILE_SIMPLIFY_ALNUM as u32, simplify) + } + + /// Ignore all whitespace + pub fn ignore_whitespace(&mut self, ignore: bool) -> &mut MergeOptions { + self.file_flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE as u32, ignore) + } + + /// Ignore changes in amount of whitespace + pub fn ignore_whitespace_change(&mut self, ignore: bool) -> &mut MergeOptions { + self.file_flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE as u32, ignore) + } + + /// Ignore whitespace at end of line + pub fn ignore_whitespace_eol(&mut self, ignore: bool) -> &mut MergeOptions { + self.file_flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL as u32, ignore) + } + + /// Use the "patience diff" algorithm + pub fn patience(&mut self, patience: bool) -> &mut MergeOptions { + self.file_flag(raw::GIT_MERGE_FILE_DIFF_PATIENCE as u32, patience) + } + + /// Take extra time to find minimal diff + pub fn minimal(&mut self, minimal: bool) -> &mut MergeOptions { + self.file_flag(raw::GIT_MERGE_FILE_DIFF_MINIMAL as u32, minimal) + } + + /// Acquire a pointer to the underlying raw options. + pub unsafe fn raw(&self) -> *const raw::git_merge_options { + &self.raw as *const _ + } +} + +impl<'repo> Binding for AnnotatedCommit<'repo> { + type Raw = *mut raw::git_annotated_commit; + unsafe fn from_raw(raw: *mut raw::git_annotated_commit) -> AnnotatedCommit<'repo> { + AnnotatedCommit { + raw, + _marker: marker::PhantomData, + } + } + fn raw(&self) -> *mut raw::git_annotated_commit { + self.raw + } +} + +impl<'repo> Drop for AnnotatedCommit<'repo> { + fn drop(&mut self) { + unsafe { raw::git_annotated_commit_free(self.raw) } + } +} |