Skip to content

Commit

Permalink
Merge 4e2fdda into 2c0aa03
Browse files Browse the repository at this point in the history
  • Loading branch information
cmsd2 committed Sep 22, 2015
2 parents 2c0aa03 + 4e2fdda commit a328c8c
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 0 deletions.
18 changes: 18 additions & 0 deletions libgit2-sys/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ impl Clone for git_strarray {
fn clone(&self) -> git_strarray { *self }
}

#[repr(C)]
#[derive(Copy)]
pub struct git_oidarray {
pub ids: *mut git_oid,
pub count: size_t,
}
impl Clone for git_oidarray {
fn clone(&self) -> git_oidarray { *self }
}

#[repr(C)]
pub struct git_signature {
pub name: *mut c_char,
Expand Down Expand Up @@ -1478,6 +1488,9 @@ extern {
// strarray
pub fn git_strarray_free(array: *mut git_strarray);

// oidarray
pub fn git_oidarray_free(array: *mut git_oidarray);

// signature
pub fn git_signature_default(out: *mut *mut git_signature,
repo: *mut git_repository) -> c_int;
Expand Down Expand Up @@ -2128,6 +2141,11 @@ extern {
one: *const git_oid,
two: *const git_oid) -> c_int;

pub fn git_merge_bases(out: *mut git_oidarray,
repo: *mut git_repository,
one: *const git_oid,
two: *const git_oid) -> c_int;

// pathspec
pub fn git_pathspec_free(ps: *mut git_pathspec);
pub fn git_pathspec_match_diff(out: *mut *mut git_pathspec_match_list,
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ mod util;
pub mod build;
pub mod cert;
pub mod string_array;
pub mod oid_array;
pub mod transport;

mod blame;
Expand Down
110 changes: 110 additions & 0 deletions src/oid_array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//! Bindings to libgit2's raw git_strarray type

use std::ops::Range;

use oid::Oid;
use raw;
use util::Binding;

/// An oid array structure used by libgit2
///
/// Some apis return arrays of oids which originate from libgit2. This
/// wrapper type behaves a little like `Vec<&Oid>` but does so without copying
/// the underlying Oids until necessary.
pub struct OidArray {
raw: raw::git_oidarray,
}

/// A forward iterator over the Oids of an array, casted to `&Oid`.
pub struct Iter<'a> {
range: Range<usize>,
arr: &'a OidArray,
}

/// A forward iterator over the bytes of n array.
pub struct IterBytes<'a> {
range: Range<usize>,
arr: &'a OidArray,
}

impl OidArray {
/// Returns None if i is out of bounds.
pub fn get(&self, i: usize) -> Option<Oid> {
if i < self.raw.count as usize {
unsafe {
let ptr = self.raw.ids.offset(i as isize);
Some(Oid::from_raw(ptr))
}
} else {
None
}
}

/// Returns None if `i` is out of bounds.
pub fn get_bytes(&self, i: usize) -> Option<&[u8]> {
if i < self.raw.count as usize {
unsafe {
let ptr = self.raw.ids.offset(i as isize) as *const _;
Some(::opt_bytes(self, ptr).unwrap())
}
} else {
None
}
}

/// Returns an iterator over the Oids contained within this array.
pub fn iter(&self) -> Iter {
Iter { range: 0..self.len(), arr: self }
}

/// Returns an iterator over the strings contained within this array,
/// yielding byte slices.
pub fn iter_bytes(&self) -> IterBytes {
IterBytes { range: 0..self.len(), arr: self }
}

/// Returns the number of strings in this array.
pub fn len(&self) -> usize { self.raw.count as usize }
}

impl Binding for OidArray {
type Raw = raw::git_oidarray;
unsafe fn from_raw(raw: raw::git_oidarray) -> OidArray {
OidArray { raw: raw }
}
fn raw(&self) -> raw::git_oidarray { self.raw }
}

impl<'a> Iterator for Iter<'a> {
type Item = Oid;
fn next(&mut self) -> Option<Oid> {
self.range.next().and_then(|i| self.arr.get(i))
}
fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
}
impl<'a> DoubleEndedIterator for Iter<'a> {
fn next_back(&mut self) -> Option<Oid> {
self.range.next_back().and_then(|i| self.arr.get(i))
}
}
impl<'a> ExactSizeIterator for Iter<'a> {}

impl<'a> Iterator for IterBytes<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<&'a [u8]> {
self.range.next().and_then(|i| self.arr.get_bytes(i))
}
fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
}
impl<'a> DoubleEndedIterator for IterBytes<'a> {
fn next_back(&mut self) -> Option<&'a [u8]> {
self.range.next_back().and_then(|i| self.arr.get_bytes(i))
}
}
impl<'a> ExactSizeIterator for IterBytes<'a> {}

impl Drop for OidArray {
fn drop(&mut self) {
unsafe { raw::git_oidarray_free(&mut self.raw) }
}
}
92 changes: 92 additions & 0 deletions src/repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use {ObjectType, Tag, Note, Notes, StatusOptions, Statuses, Status, Revwalk};
use {RevparseMode, RepositoryInitMode, Reflog, IntoCString};
use build::{RepoBuilder, CheckoutBuilder};
use string_array::StringArray;
use oid_array::OidArray;
use util::{self, Binding};

/// An owned git repository, representing all state associated with the
Expand Down Expand Up @@ -1261,6 +1262,20 @@ impl Repository {
}
}

/// Find all merge bases between two commits
pub fn merge_bases(&self, one: Oid, two: Oid) -> Result<OidArray, Error> {
let mut arr = raw::git_oidarray {
ids: 0 as *mut raw::git_oid,
count: 0,
};
unsafe {
try_call!(raw::git_merge_bases(&mut arr, self.raw,
one.raw(), two.raw()));
Ok(Binding::from_raw(arr))
}
}


/// Count the number of unique commits between two commit objects
///
/// There is no need for branches containing the commits to have any
Expand Down Expand Up @@ -1493,6 +1508,7 @@ impl RepositoryInitOptions {
#[cfg(test)]
mod tests {
use std::fs;
use std::path::Path;
use tempdir::TempDir;
use {Repository, Oid, ObjectType, ResetType};
use build::CheckoutBuilder;
Expand Down Expand Up @@ -1652,6 +1668,82 @@ mod tests {
assert_eq!(repo.head().unwrap().target().unwrap(), master_oid);
}

/// create an octopus:
/// /---o2-o4
/// o1 X
/// \---o3-o5
/// and checks that the merge bases of (o4,o5) are (o2,o3)
#[test]
fn smoke_merge_bases() {
let (_td, repo) = graph_repo_init();
let sig = repo.signature().unwrap();

// let oid1 = head
let oid1 = repo.head().unwrap().target().unwrap();
let commit1 = repo.find_commit(oid1).unwrap();
println!("created oid1 {:?}", oid1);

repo.branch("branch_a", &commit1, true).unwrap();
repo.branch("branch_b", &commit1, true).unwrap();

// create commit oid2 on branchA
let mut index = repo.index().unwrap();
let p = Path::new(repo.workdir().unwrap()).join("file_a");
println!("using path {:?}", p);
fs::File::create(&p).unwrap();
index.add_path(Path::new("file_a")).unwrap();
let id_a = index.write_tree().unwrap();
let tree_a = repo.find_tree(id_a).unwrap();
let oid2 = repo.commit(Some("refs/heads/branch_a"), &sig, &sig, "commit 2", &tree_a, &[&commit1]).unwrap();
let commit2 = repo.find_commit(oid2).unwrap();
println!("created oid2 {:?}", oid2);

// create commit oid3 on branchB
let mut index = repo.index().unwrap();
let p = Path::new(repo.workdir().unwrap()).join("file_b");
fs::File::create(&p).unwrap();
index.add_path(Path::new("file_b")).unwrap();
let id_b = index.write_tree().unwrap();
let tree_b = repo.find_tree(id_b).unwrap();
let oid3 = repo.commit(Some("refs/heads/branch_b"), &sig, &sig, "commit 3", &tree_b, &[&commit1]).unwrap();
let commit3 = repo.find_commit(oid3).unwrap();
println!("created oid3 {:?}", oid3);

// create merge commit oid4 on branchA with parents oid2 and oid3
//let mut index4 = repo.merge_commits(&commit2, &commit3, None).unwrap();
repo.set_head("refs/heads/branch_a").unwrap();
repo.checkout_head(None).unwrap();
let oid4 = repo.commit(Some("refs/heads/branch_a"), &sig, &sig, "commit 4", &tree_a, &[&commit2, &commit3]).unwrap();
//index4.write_tree_to(&repo).unwrap();
println!("created oid4 {:?}", oid4);

// create merge commit oid5 on branchB with parents oid2 and oid3
//let mut index5 = repo.merge_commits(&commit3, &commit2, None).unwrap();
repo.set_head("refs/heads/branch_b").unwrap();
repo.checkout_head(None).unwrap();
let oid5 = repo.commit(Some("refs/heads/branch_b"), &sig, &sig, "commit 5", &tree_a, &[&commit3, &commit2]).unwrap();
//index5.write_tree_to(&repo).unwrap();
println!("created oid5 {:?}", oid5);

// merge bases of (oid4,oid5) should be (oid2,oid3)
let merge_bases = repo.merge_bases(oid4, oid5).unwrap();
let mut found_oid2 = false;
let mut found_oid3 = false;
for mg in merge_bases.iter() {
println!("found merge base {:?}", mg);
if mg == oid2 {
found_oid2 = true;
} else if mg == oid3 {
found_oid3 = true;
} else {
assert!(false);
}
}
assert!(found_oid2);
assert!(found_oid3);
assert_eq!(merge_bases.len(), 2);
}

#[test]
fn smoke_revparse_ext() {
let (_td, repo) = graph_repo_init();
Expand Down

0 comments on commit a328c8c

Please sign in to comment.