Skip to content

Commit 33f0a71

Browse files
authored
Merge pull request #1210 from chalharu/master
Add function: merge_file
2 parents 6ee438e + 37f9070 commit 33f0a71

3 files changed

Lines changed: 167 additions & 4 deletions

File tree

libgit2-sys/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,6 +1560,15 @@ pub struct git_merge_file_options {
15601560
pub marker_size: c_ushort,
15611561
}
15621562

1563+
#[repr(C)]
1564+
pub struct git_merge_file_input {
1565+
pub version: c_uint,
1566+
pub ptr: *const c_char,
1567+
pub size: size_t,
1568+
pub path: *const c_char,
1569+
pub mode: c_uint,
1570+
}
1571+
15631572
#[repr(C)]
15641573
pub struct git_merge_file_result {
15651574
pub automergeable: c_uint,
@@ -3674,6 +3683,7 @@ extern "C" {
36743683
their_tree: *const git_tree,
36753684
opts: *const git_merge_options,
36763685
) -> c_int;
3686+
pub fn git_merge_file_input_init(opts: *mut git_merge_file_input, version: c_uint) -> c_int;
36773687
pub fn git_merge_file_options_init(opts: *mut git_merge_file_options, version: c_uint)
36783688
-> c_int;
36793689
pub fn git_repository_state_cleanup(repo: *mut git_repository) -> c_int;
@@ -3833,6 +3843,14 @@ extern "C" {
38333843
opts: *const git_merge_file_options,
38343844
) -> c_int;
38353845

3846+
pub fn git_merge_file(
3847+
out: *mut git_merge_file_result,
3848+
ancestor: *const git_merge_file_input,
3849+
ours: *const git_merge_file_input,
3850+
theirs: *const git_merge_file_input,
3851+
opts: *const git_merge_file_options,
3852+
) -> c_int;
3853+
38363854
pub fn git_merge_file_result_free(file_result: *mut git_merge_file_result);
38373855

38383856
// pathspec

src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,9 @@ pub use crate::index::{
103103
pub use crate::indexer::{Indexer, IndexerProgress, Progress};
104104
pub use crate::mailmap::Mailmap;
105105
pub use crate::mempack::Mempack;
106-
pub use crate::merge::{AnnotatedCommit, MergeFileOptions, MergeFileResult, MergeOptions};
106+
pub use crate::merge::{
107+
merge_file, AnnotatedCommit, MergeFileInput, MergeFileOptions, MergeFileResult, MergeOptions,
108+
};
107109
pub use crate::message::{
108110
message_prettify, message_trailers_bytes, message_trailers_strs, MessageTrailersBytes,
109111
MessageTrailersBytesIterator, MessageTrailersStrs, MessageTrailersStrsIterator,

src/merge.rs

Lines changed: 146 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use libc::{c_uint, c_ushort};
1+
use libc::{c_char, c_uint, c_ushort, size_t};
22
use std::ffi::CString;
33
use std::marker;
44
use std::mem;
@@ -7,8 +7,7 @@ use std::str;
77

88
use crate::call::Convert;
99
use crate::util::Binding;
10-
use crate::IntoCString;
11-
use crate::{raw, Commit, FileFavor, Oid};
10+
use crate::{raw, Commit, Error, FileFavor, FileMode, IntoCString, Oid};
1211

1312
/// A structure to represent an annotated commit, the input to merge and rebase.
1413
///
@@ -412,3 +411,147 @@ impl std::fmt::Debug for MergeFileResult {
412411
ds.finish()
413412
}
414413
}
414+
415+
/// Input structure for merging a file.
416+
pub struct MergeFileInput<'a> {
417+
raw: raw::git_merge_file_input,
418+
path: Option<CString>,
419+
content: Option<&'a [u8]>,
420+
}
421+
422+
impl Default for MergeFileInput<'_> {
423+
fn default() -> Self {
424+
Self::new()
425+
}
426+
}
427+
428+
impl Binding for MergeFileInput<'_> {
429+
type Raw = *const raw::git_merge_file_input;
430+
431+
unsafe fn from_raw(_raw: *const raw::git_merge_file_input) -> MergeFileInput<'static> {
432+
panic!("unimplemened")
433+
}
434+
435+
fn raw(&self) -> *const raw::git_merge_file_input {
436+
&self.raw as *const _
437+
}
438+
}
439+
440+
impl<'a> MergeFileInput<'a> {
441+
/// Creates a new, empty merge file input.
442+
pub fn new() -> MergeFileInput<'a> {
443+
let mut input = MergeFileInput {
444+
raw: unsafe { mem::zeroed() },
445+
path: None,
446+
content: None,
447+
};
448+
assert_eq!(
449+
unsafe { raw::git_merge_file_input_init(&mut input.raw, 1) },
450+
0
451+
);
452+
input
453+
}
454+
455+
/// Sets the content of the file.
456+
pub fn content(&mut self, content: &'a [u8]) -> &mut MergeFileInput<'a> {
457+
self.content = Some(content);
458+
459+
self.raw.ptr = content.as_ptr() as *const c_char;
460+
self.raw.size = content.len() as size_t;
461+
462+
self
463+
}
464+
465+
/// Sets the path of the file.
466+
pub fn path<T: IntoCString>(&mut self, t: T) -> &mut MergeFileInput<'a> {
467+
self.path = Some(t.into_c_string().unwrap());
468+
469+
self.raw.path = self
470+
.path
471+
.as_ref()
472+
.map(|s| s.as_ptr())
473+
.unwrap_or(ptr::null());
474+
475+
self
476+
}
477+
478+
/// Sets the mode of the file.
479+
pub fn mode(&mut self, mode: Option<FileMode>) -> &mut MergeFileInput<'a> {
480+
self.raw.mode = mode.map_or(0, u32::from);
481+
self
482+
}
483+
}
484+
485+
/// Merges three versions of a file: an ancestor version, our version, and their version.
486+
pub fn merge_file(
487+
ancestor: &MergeFileInput<'_>,
488+
ours: &MergeFileInput<'_>,
489+
theirs: &MergeFileInput<'_>,
490+
opts: Option<&mut MergeFileOptions>,
491+
) -> Result<MergeFileResult, Error> {
492+
unsafe {
493+
let ancestor = ancestor.raw();
494+
let ours = ours.raw();
495+
let theirs = theirs.raw();
496+
497+
let mut ret = mem::zeroed();
498+
try_call!(raw::git_merge_file(
499+
&mut ret,
500+
ancestor,
501+
ours,
502+
theirs,
503+
opts.map(|o| o.raw()).unwrap_or(ptr::null())
504+
));
505+
Ok(Binding::from_raw(ret))
506+
}
507+
}
508+
509+
#[cfg(test)]
510+
mod tests {
511+
use crate::{MergeFileInput, MergeFileOptions};
512+
513+
use std::path::Path;
514+
515+
#[test]
516+
fn smoke_merge_file() {
517+
let file_path = Path::new("file");
518+
let base = {
519+
let mut input = MergeFileInput::new();
520+
input.content(b"base").path(file_path);
521+
input
522+
};
523+
524+
let ours = {
525+
let mut input = MergeFileInput::new();
526+
input.content(b"foo").path(file_path);
527+
input
528+
};
529+
530+
let theirs = {
531+
let mut input = MergeFileInput::new();
532+
input.content(b"bar").path(file_path);
533+
input
534+
};
535+
536+
let mut opts = MergeFileOptions::new();
537+
opts.ancestor_label("ancestor");
538+
opts.our_label("ours");
539+
opts.their_label("theirs");
540+
opts.style_diff3(true);
541+
let merge_file_result = crate::merge_file(&base, &ours, &theirs, Some(&mut opts)).unwrap();
542+
543+
assert!(!merge_file_result.is_automergeable());
544+
assert_eq!(merge_file_result.path(), Some("file"));
545+
assert_eq!(
546+
String::from_utf8_lossy(merge_file_result.content()).to_string(),
547+
r"<<<<<<< ours
548+
foo
549+
||||||| ancestor
550+
base
551+
=======
552+
bar
553+
>>>>>>> theirs
554+
",
555+
);
556+
}
557+
}

0 commit comments

Comments
 (0)