-
Notifications
You must be signed in to change notification settings - Fork 401
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add function: git_merge_file_from_index #1062
base: master
Are you sure you want to change the base?
Changes from all commits
21b23cb
e889d31
2eb8c84
e3e023a
384276a
79ca67a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,13 @@ | ||
use libc::c_uint; | ||
use libc::{c_uint, c_ushort}; | ||
use std::ffi::CString; | ||
use std::marker; | ||
use std::mem; | ||
use std::ptr; | ||
use std::str; | ||
|
||
use crate::call::Convert; | ||
use crate::util::Binding; | ||
use crate::IntoCString; | ||
use crate::{raw, Commit, FileFavor, Oid}; | ||
|
||
/// A structure to represent an annotated commit, the input to merge and rebase. | ||
|
@@ -22,6 +25,20 @@ pub struct MergeOptions { | |
raw: raw::git_merge_options, | ||
} | ||
|
||
/// Options for merging a file. | ||
pub struct MergeFileOptions { | ||
ancestor_label: Option<CString>, | ||
our_label: Option<CString>, | ||
their_label: Option<CString>, | ||
raw: raw::git_merge_file_options, | ||
} | ||
|
||
/// Information about file-level merging. | ||
pub struct MergeFileResult<'repo> { | ||
raw: raw::git_merge_file_result, | ||
_marker: marker::PhantomData<&'repo str>, | ||
} | ||
|
||
impl<'repo> AnnotatedCommit<'repo> { | ||
/// Gets the commit ID that the given git_annotated_commit refers to | ||
pub fn id(&self) -> Oid { | ||
|
@@ -192,3 +209,207 @@ impl<'repo> Drop for AnnotatedCommit<'repo> { | |
unsafe { raw::git_annotated_commit_free(self.raw) } | ||
} | ||
} | ||
|
||
impl Default for MergeFileOptions { | ||
fn default() -> Self { | ||
Self::new() | ||
} | ||
} | ||
|
||
impl MergeFileOptions { | ||
/// Creates a default set of merge file options. | ||
pub fn new() -> MergeFileOptions { | ||
let mut opts = MergeFileOptions { | ||
ancestor_label: None, | ||
our_label: None, | ||
their_label: None, | ||
raw: unsafe { mem::zeroed() }, | ||
}; | ||
assert_eq!( | ||
unsafe { raw::git_merge_file_options_init(&mut opts.raw, 1) }, | ||
0 | ||
); | ||
opts | ||
} | ||
|
||
/// Label for the ancestor file side of the conflict which will be prepended | ||
/// to labels in diff3-format merge files. | ||
pub fn ancestor_label<T: IntoCString>(&mut self, t: T) -> &mut MergeFileOptions { | ||
self.ancestor_label = Some(t.into_c_string().unwrap()); | ||
|
||
self.raw.ancestor_label = self | ||
.ancestor_label | ||
.as_ref() | ||
.map(|s| s.as_ptr()) | ||
.unwrap_or(ptr::null()); | ||
|
||
self | ||
} | ||
|
||
/// Label for our file side of the conflict which will be prepended to labels | ||
/// in merge files. | ||
pub fn our_label<T: IntoCString>(&mut self, t: T) -> &mut MergeFileOptions { | ||
self.our_label = Some(t.into_c_string().unwrap()); | ||
|
||
self.raw.our_label = self | ||
.our_label | ||
.as_ref() | ||
.map(|s| s.as_ptr()) | ||
.unwrap_or(ptr::null()); | ||
|
||
self | ||
} | ||
|
||
/// Label for their file side of the conflict which will be prepended to labels | ||
/// in merge files. | ||
pub fn their_label<T: IntoCString>(&mut self, t: T) -> &mut MergeFileOptions { | ||
self.their_label = Some(t.into_c_string().unwrap()); | ||
|
||
self.raw.their_label = self | ||
.their_label | ||
.as_ref() | ||
.map(|s| s.as_ptr()) | ||
.unwrap_or(ptr::null()); | ||
|
||
self | ||
} | ||
|
||
/// Specify a side to favor for resolving conflicts | ||
pub fn favor(&mut self, favor: FileFavor) -> &mut MergeFileOptions { | ||
self.raw.favor = favor.convert(); | ||
self | ||
} | ||
|
||
fn flag(&mut self, opt: raw::git_merge_file_flag_t, val: bool) -> &mut MergeFileOptions { | ||
if val { | ||
self.raw.flags |= opt as u32; | ||
} else { | ||
self.raw.flags &= !opt as u32; | ||
} | ||
self | ||
} | ||
|
||
/// Create standard conflicted merge files | ||
pub fn style_standard(&mut self, standard: bool) -> &mut MergeFileOptions { | ||
self.flag(raw::GIT_MERGE_FILE_STYLE_MERGE, standard) | ||
} | ||
|
||
/// Create diff3-style file | ||
pub fn style_diff3(&mut self, diff3: bool) -> &mut MergeFileOptions { | ||
self.flag(raw::GIT_MERGE_FILE_STYLE_DIFF3, diff3) | ||
} | ||
|
||
/// Condense non-alphanumeric regions for simplified diff file | ||
pub fn simplify_alnum(&mut self, simplify: bool) -> &mut MergeFileOptions { | ||
self.flag(raw::GIT_MERGE_FILE_SIMPLIFY_ALNUM, simplify) | ||
} | ||
|
||
/// Ignore all whitespace | ||
pub fn ignore_whitespace(&mut self, ignore: bool) -> &mut MergeFileOptions { | ||
self.flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE, ignore) | ||
} | ||
|
||
/// Ignore changes in amount of whitespace | ||
pub fn ignore_whitespace_change(&mut self, ignore: bool) -> &mut MergeFileOptions { | ||
self.flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE, ignore) | ||
} | ||
|
||
/// Ignore whitespace at end of line | ||
pub fn ignore_whitespace_eol(&mut self, ignore: bool) -> &mut MergeFileOptions { | ||
self.flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL, ignore) | ||
} | ||
|
||
/// Use the "patience diff" algorithm | ||
pub fn patience(&mut self, patience: bool) -> &mut MergeFileOptions { | ||
self.flag(raw::GIT_MERGE_FILE_DIFF_PATIENCE, patience) | ||
} | ||
|
||
/// Take extra time to find minimal diff | ||
pub fn minimal(&mut self, minimal: bool) -> &mut MergeFileOptions { | ||
self.flag(raw::GIT_MERGE_FILE_DIFF_MINIMAL, minimal) | ||
} | ||
|
||
/// Create zdiff3 ("zealous diff3")-style files | ||
pub fn style_zdiff3(&mut self, zdiff3: bool) -> &mut MergeFileOptions { | ||
self.flag(raw::GIT_MERGE_FILE_STYLE_ZDIFF3, zdiff3) | ||
} | ||
|
||
/// Do not produce file conflicts when common regions have changed | ||
pub fn accept_conflicts(&mut self, accept: bool) -> &mut MergeFileOptions { | ||
self.flag(raw::GIT_MERGE_FILE_ACCEPT_CONFLICTS, accept) | ||
} | ||
|
||
/// The size of conflict markers (eg, "<<<<<<<"). Default is 7. | ||
pub fn marker_size(&mut self, size: u16) -> &mut MergeFileOptions { | ||
self.raw.marker_size = size as c_ushort; | ||
self | ||
} | ||
|
||
/// Acquire a pointer to the underlying raw options. | ||
pub unsafe fn raw(&mut self) -> *const raw::git_merge_file_options { | ||
&self.raw as *const _ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this need to be pub? Also, it seems like this could be done as a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, there could well be cargo cult issues going on here - I believe I copied this from More generally, I think the reasoning in my head was that structs like |
||
} | ||
} | ||
|
||
impl<'repo> MergeFileResult<'repo> { | ||
/// True if the output was automerged, false if the output contains | ||
/// conflict markers. | ||
pub fn is_automergeable(&self) -> bool { | ||
self.raw.automergeable > 0 | ||
} | ||
|
||
/// The path that the resultant merge file should use. | ||
/// | ||
/// returns `None` if a filename conflict would occur, | ||
/// or if the path is not valid utf-8 | ||
pub fn path(&self) -> Option<&str> { | ||
self.path_bytes() | ||
.and_then(|bytes| str::from_utf8(bytes).ok()) | ||
} | ||
|
||
/// Gets the path as a byte slice. | ||
pub fn path_bytes(&self) -> Option<&[u8]> { | ||
unsafe { crate::opt_bytes(self, self.raw.path) } | ||
} | ||
|
||
/// The mode that the resultant merge file should use. | ||
pub fn mode(&self) -> u32 { | ||
self.raw.mode as u32 | ||
} | ||
|
||
/// The contents of the merge. | ||
pub fn content(&self) -> &'repo [u8] { | ||
unsafe { std::slice::from_raw_parts(self.raw.ptr as *const u8, self.raw.len as usize) } | ||
} | ||
} | ||
|
||
impl<'repo> Binding for MergeFileResult<'repo> { | ||
type Raw = raw::git_merge_file_result; | ||
unsafe fn from_raw(raw: raw::git_merge_file_result) -> MergeFileResult<'repo> { | ||
MergeFileResult { | ||
raw, | ||
_marker: marker::PhantomData, | ||
} | ||
} | ||
fn raw(&self) -> raw::git_merge_file_result { | ||
self.raw | ||
} | ||
} | ||
Comment on lines
+394
to
+396
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This generally doesn't look correct to me, or at least looks concerning. I don't think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
|
||
impl<'repo> Drop for MergeFileResult<'repo> { | ||
fn drop(&mut self) { | ||
unsafe { raw::git_merge_file_result_free(&mut self.raw) } | ||
} | ||
} | ||
|
||
impl<'repo> std::fmt::Debug for MergeFileResult<'repo> { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
let mut ds = f.debug_struct("MergeFileResult"); | ||
if let Some(path) = &self.path() { | ||
ds.field("path", path); | ||
} | ||
ds.field("automergeable", &self.is_automergeable()); | ||
ds.field("mode", &self.mode()); | ||
ds.finish() | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this have a
PhantomData
?From what I can tell, the struct owns its own pointers (which are freed with
git_merge_file_result_free
).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be unnecessary, see comment below.