use std::env;
use std::ffi::{CStr, CString, OsStr};
use std::iter::IntoIterator;
use std::mem;
use std::path::Path;
use std::ptr;
use std::str;
use libc::{c_int, c_char, size_t, c_void, c_uint};
use {raw, Revspec, Error, init, Object, RepositoryOpenFlags, RepositoryState, Remote, Buf, StashFlags};
use {ResetType, Signature, Reference, References, Submodule, Blame, BlameOptions};
use {Branches, BranchType, Index, Config, Oid, Blob, BlobWriter, Branch, Commit, Tree};
use {AnnotatedCommit, MergeOptions, SubmoduleIgnore, SubmoduleStatus, MergeAnalysis, MergePreference};
use {ObjectType, Tag, Note, Notes, StatusOptions, Statuses, Status, Revwalk};
use {RevparseMode, RepositoryInitMode, Reflog, IntoCString, Describe};
use {DescribeOptions, TreeBuilder, Diff, DiffOptions, PackBuilder, Odb};
use {Rebase, RebaseOptions};
use build::{RepoBuilder, CheckoutBuilder};
use stash::{StashApplyOptions, StashCbData, stash_cb};
use string_array::StringArray;
use oid_array::OidArray;
use util::{self, Binding};
pub struct Repository {
raw: *mut raw::git_repository,
}
unsafe impl Send for Repository {}
pub struct RepositoryInitOptions {
flags: u32,
mode: u32,
workdir_path: Option<CString>,
description: Option<CString>,
template_path: Option<CString>,
initial_head: Option<CString>,
origin_url: Option<CString>,
}
impl Repository {
pub fn open<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
init();
let path = try!(path.as_ref().into_c_string());
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_repository_open(&mut ret, path));
Ok(Binding::from_raw(ret))
}
}
pub fn open_bare<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
init();
let path = try!(path.as_ref().into_c_string());
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_repository_open_bare(&mut ret, path));
Ok(Binding::from_raw(ret))
}
}
pub fn open_from_env() -> Result<Repository, Error> {
init();
let mut ret = ptr::null_mut();
let flags = raw::GIT_REPOSITORY_OPEN_FROM_ENV;
unsafe {
try_call!(raw::git_repository_open_ext(&mut ret,
ptr::null(),
flags as c_uint,
ptr::null()));
Ok(Binding::from_raw(ret))
}
}
pub fn open_ext<P, O, I>(path: P,
flags: RepositoryOpenFlags,
ceiling_dirs: I)
-> Result<Repository, Error>
where P: AsRef<Path>, O: AsRef<OsStr>, I: IntoIterator<Item=O>
{
init();
let path = try!(path.as_ref().into_c_string());
let ceiling_dirs_os = try!(env::join_paths(ceiling_dirs));
let ceiling_dirs = try!(ceiling_dirs_os.into_c_string());
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_repository_open_ext(&mut ret,
path,
flags.bits() as c_uint,
ceiling_dirs));
Ok(Binding::from_raw(ret))
}
}
pub fn discover<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
init();
let buf = Buf::new();
let path = try!(path.as_ref().into_c_string());
unsafe {
try_call!(raw::git_repository_discover(buf.raw(), path, 1,
ptr::null()));
}
Repository::open(util::bytes2path(&*buf))
}
pub fn init<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
Repository::init_opts(path, &RepositoryInitOptions::new())
}
pub fn init_bare<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
Repository::init_opts(path, RepositoryInitOptions::new().bare(true))
}
pub fn init_opts<P: AsRef<Path>>(path: P, opts: &RepositoryInitOptions)
-> Result<Repository, Error> {
init();
let path = try!(path.as_ref().into_c_string());
let mut ret = ptr::null_mut();
unsafe {
let mut opts = opts.raw();
try_call!(raw::git_repository_init_ext(&mut ret, path, &mut opts));
Ok(Binding::from_raw(ret))
}
}
pub fn clone<P: AsRef<Path>>(url: &str, into: P)
-> Result<Repository, Error> {
::init();
RepoBuilder::new().clone(url, into.as_ref())
}
pub fn clone_recurse<P: AsRef<Path>>(url: &str, into: P)
-> Result<Repository, Error> {
let repo = Repository::clone(url, into)?;
repo.update_submodules()?;
Ok(repo)
}
pub fn from_odb(odb: Odb) -> Result<Repository, Error> {
init();
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_repository_wrap_odb(&mut ret, odb.raw()));
Ok(Binding::from_raw(ret))
}
}
fn update_submodules(&self) -> Result<(), Error> {
fn add_subrepos(repo: &Repository, list: &mut Vec<Repository>)
-> Result<(), Error> {
for mut subm in repo.submodules()? {
subm.update(true, None)?;
list.push(subm.open()?);
}
Ok(())
}
let mut repos = Vec::new();
add_subrepos(self, &mut repos)?;
while let Some(repo) = repos.pop() {
add_subrepos(&repo, &mut repos)?;
}
Ok(())
}
pub fn revparse(&self, spec: &str) -> Result<Revspec, Error> {
let mut raw = raw::git_revspec {
from: ptr::null_mut(),
to: ptr::null_mut(),
flags: 0,
};
let spec = try!(CString::new(spec));
unsafe {
try_call!(raw::git_revparse(&mut raw, self.raw, spec));
let to = Binding::from_raw_opt(raw.to);
let from = Binding::from_raw_opt(raw.from);
let mode = RevparseMode::from_bits_truncate(raw.flags as u32);
Ok(Revspec::from_objects(from, to, mode))
}
}
pub fn revparse_single(&self, spec: &str) -> Result<Object, Error> {
let spec = try!(CString::new(spec));
let mut obj = ptr::null_mut();
unsafe {
try_call!(raw::git_revparse_single(&mut obj, self.raw, spec));
assert!(!obj.is_null());
Ok(Binding::from_raw(obj))
}
}
pub fn revparse_ext(&self, spec: &str)
-> Result<(Object, Option<Reference>), Error> {
let spec = try!(CString::new(spec));
let mut git_obj = ptr::null_mut();
let mut git_ref = ptr::null_mut();
unsafe {
try_call!(raw::git_revparse_ext(&mut git_obj, &mut git_ref,
self.raw, spec));
assert!(!git_obj.is_null());
Ok((Binding::from_raw(git_obj), Binding::from_raw_opt(git_ref)))
}
}
pub fn is_bare(&self) -> bool {
unsafe { raw::git_repository_is_bare(self.raw) == 1 }
}
pub fn is_shallow(&self) -> bool {
unsafe { raw::git_repository_is_shallow(self.raw) == 1 }
}
pub fn is_worktree(&self) -> bool {
unsafe { raw::git_repository_is_worktree(self.raw) == 1 }
}
pub fn is_empty(&self) -> Result<bool, Error> {
let empty = unsafe {
try_call!(raw::git_repository_is_empty(self.raw))
};
Ok(empty == 1)
}
pub fn path(&self) -> &Path {
unsafe {
let ptr = raw::git_repository_path(self.raw);
util::bytes2path(::opt_bytes(self, ptr).unwrap())
}
}
pub fn state(&self) -> RepositoryState {
let state = unsafe { raw::git_repository_state(self.raw) };
macro_rules! check( ($($raw:ident => $real:ident),*) => (
$(if state == raw::$raw as c_int {
super::RepositoryState::$real
}) else *
else {
panic!("unknown repository state: {}", state)
}
) );
check!(
GIT_REPOSITORY_STATE_NONE => Clean,
GIT_REPOSITORY_STATE_MERGE => Merge,
GIT_REPOSITORY_STATE_REVERT => Revert,
GIT_REPOSITORY_STATE_REVERT_SEQUENCE => RevertSequence,
GIT_REPOSITORY_STATE_CHERRYPICK => CherryPick,
GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE => CherryPickSequence,
GIT_REPOSITORY_STATE_BISECT => Bisect,
GIT_REPOSITORY_STATE_REBASE => Rebase,
GIT_REPOSITORY_STATE_REBASE_INTERACTIVE => RebaseInteractive,
GIT_REPOSITORY_STATE_REBASE_MERGE => RebaseMerge,
GIT_REPOSITORY_STATE_APPLY_MAILBOX => ApplyMailbox,
GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE => ApplyMailboxOrRebase
)
}
pub fn workdir(&self) -> Option<&Path> {
unsafe {
let ptr = raw::git_repository_workdir(self.raw);
if ptr.is_null() {
None
} else {
Some(util::bytes2path(CStr::from_ptr(ptr).to_bytes()))
}
}
}
pub fn set_workdir(&self, path: &Path, update_gitlink: bool)
-> Result<(), Error> {
let path = try!(path.into_c_string());
unsafe {
try_call!(raw::git_repository_set_workdir(self.raw(), path,
update_gitlink));
}
Ok(())
}
pub fn namespace(&self) -> Option<&str> {
self.namespace_bytes().and_then(|s| str::from_utf8(s).ok())
}
pub fn namespace_bytes(&self) -> Option<&[u8]> {
unsafe { ::opt_bytes(self, raw::git_repository_get_namespace(self.raw)) }
}
pub fn set_namespace(&self, namespace: &str) -> Result<(), Error> {
self.set_namespace_bytes(namespace.as_bytes())
}
pub fn set_namespace_bytes(&self, namespace: &[u8]) -> Result<(), Error> {
unsafe {
let namespace = try!(CString::new(namespace));
try_call!(raw::git_repository_set_namespace(self.raw,
namespace));
Ok(())
}
}
pub fn remove_namespace(&self) -> Result<(), Error> {
unsafe {
try_call!(raw::git_repository_set_namespace(self.raw,
ptr::null()));
Ok(())
}
}
pub fn message(&self) -> Result<String, Error> {
unsafe {
let buf = Buf::new();
try_call!(raw::git_repository_message(buf.raw(), self.raw));
Ok(str::from_utf8(&buf).unwrap().to_string())
}
}
pub fn remove_message(&self) -> Result<(), Error> {
unsafe {
try_call!(raw::git_repository_message_remove(self.raw));
Ok(())
}
}
pub fn remotes(&self) -> Result<StringArray, Error> {
let mut arr = raw::git_strarray {
strings: 0 as *mut *mut c_char,
count: 0,
};
unsafe {
try_call!(raw::git_remote_list(&mut arr, self.raw));
Ok(Binding::from_raw(arr))
}
}
pub fn find_remote(&self, name: &str) -> Result<Remote, Error> {
let mut ret = ptr::null_mut();
let name = try!(CString::new(name));
unsafe {
try_call!(raw::git_remote_lookup(&mut ret, self.raw, name));
Ok(Binding::from_raw(ret))
}
}
pub fn remote(&self, name: &str, url: &str) -> Result<Remote, Error> {
let mut ret = ptr::null_mut();
let name = try!(CString::new(name));
let url = try!(CString::new(url));
unsafe {
try_call!(raw::git_remote_create(&mut ret, self.raw, name, url));
Ok(Binding::from_raw(ret))
}
}
pub fn remote_anonymous(&self, url: &str) -> Result<Remote, Error> {
let mut ret = ptr::null_mut();
let url = try!(CString::new(url));
unsafe {
try_call!(raw::git_remote_create_anonymous(&mut ret, self.raw, url));
Ok(Binding::from_raw(ret))
}
}
pub fn remote_rename(&self, name: &str,
new_name: &str) -> Result<StringArray, Error> {
let name = try!(CString::new(name));
let new_name = try!(CString::new(new_name));
let mut problems = raw::git_strarray {
count: 0,
strings: 0 as *mut *mut c_char,
};
unsafe {
try_call!(raw::git_remote_rename(&mut problems, self.raw, name,
new_name));
Ok(Binding::from_raw(problems))
}
}
pub fn remote_delete(&self, name: &str) -> Result<(), Error> {
let name = try!(CString::new(name));
unsafe { try_call!(raw::git_remote_delete(self.raw, name)); }
Ok(())
}
pub fn remote_add_fetch(&self, name: &str, spec: &str)
-> Result<(), Error> {
let name = try!(CString::new(name));
let spec = try!(CString::new(spec));
unsafe {
try_call!(raw::git_remote_add_fetch(self.raw, name, spec));
}
Ok(())
}
pub fn remote_add_push(&self, name: &str, spec: &str)
-> Result<(), Error> {
let name = try!(CString::new(name));
let spec = try!(CString::new(spec));
unsafe {
try_call!(raw::git_remote_add_push(self.raw, name, spec));
}
Ok(())
}
pub fn remote_set_url(&self, name: &str, url: &str) -> Result<(), Error> {
let name = try!(CString::new(name));
let url = try!(CString::new(url));
unsafe { try_call!(raw::git_remote_set_url(self.raw, name, url)); }
Ok(())
}
pub fn remote_set_pushurl(&self, name: &str, pushurl: Option<&str>)
-> Result<(), Error> {
let name = try!(CString::new(name));
let pushurl = try!(::opt_cstr(pushurl));
unsafe {
try_call!(raw::git_remote_set_pushurl(self.raw, name, pushurl));
}
Ok(())
}
pub fn reset(&self,
target: &Object,
kind: ResetType,
checkout: Option<&mut CheckoutBuilder>)
-> Result<(), Error> {
unsafe {
let mut opts: raw::git_checkout_options = mem::zeroed();
try_call!(raw::git_checkout_init_options(&mut opts,
raw::GIT_CHECKOUT_OPTIONS_VERSION));
let opts = checkout.map(|c| {
c.configure(&mut opts); &mut opts
});
try_call!(raw::git_reset(self.raw, target.raw(), kind, opts));
}
Ok(())
}
pub fn reset_default<T, I>(&self,
target: Option<&Object>,
paths: I) -> Result<(), Error>
where T: IntoCString, I: IntoIterator<Item=T>,
{
let (_a, _b, mut arr) = try!(::util::iter2cstrs(paths));
let target = target.map(|t| t.raw());
unsafe {
try_call!(raw::git_reset_default(self.raw, target, &mut arr));
}
Ok(())
}
pub fn head(&self) -> Result<Reference, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_repository_head(&mut ret, self.raw));
Ok(Binding::from_raw(ret))
}
}
pub fn set_head(&self, refname: &str) -> Result<(), Error> {
let refname = try!(CString::new(refname));
unsafe {
try_call!(raw::git_repository_set_head(self.raw, refname));
}
Ok(())
}
pub fn head_detached(&self) -> Result<bool, Error> {
unsafe {
let value = raw::git_repository_head_detached(self.raw);
match value {
0 => Ok(false),
1 => Ok(true),
_ => Err(Error::last_error(value).unwrap())
}
}
}
pub fn set_head_detached(&self, commitish: Oid) -> Result<(), Error> {
unsafe {
try_call!(raw::git_repository_set_head_detached(self.raw,
commitish.raw()));
}
Ok(())
}
pub fn references(&self) -> Result<References, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_reference_iterator_new(&mut ret, self.raw));
Ok(Binding::from_raw(ret))
}
}
pub fn references_glob(&self, glob: &str) -> Result<References, Error> {
let mut ret = ptr::null_mut();
let glob = try!(CString::new(glob));
unsafe {
try_call!(raw::git_reference_iterator_glob_new(&mut ret, self.raw,
glob));
Ok(Binding::from_raw(ret))
}
}
pub fn submodules(&self) -> Result<Vec<Submodule>, Error> {
struct Data<'a, 'b:'a> {
repo: &'b Repository,
ret: &'a mut Vec<Submodule<'b>>,
}
let mut ret = Vec::new();
unsafe {
let mut data = Data {
repo: self,
ret: &mut ret,
};
try_call!(raw::git_submodule_foreach(self.raw, append,
&mut data as *mut _
as *mut c_void));
}
return Ok(ret);
extern fn append(_repo: *mut raw::git_submodule,
name: *const c_char,
data: *mut c_void) -> c_int {
unsafe {
let data = &mut *(data as *mut Data);
let mut raw = ptr::null_mut();
let rc = raw::git_submodule_lookup(&mut raw, data.repo.raw(),
name);
assert_eq!(rc, 0);
data.ret.push(Binding::from_raw(raw));
}
0
}
}
pub fn statuses(&self, options: Option<&mut StatusOptions>)
-> Result<Statuses, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_status_list_new(&mut ret, self.raw,
options.map(|s| s.raw())
.unwrap_or(ptr::null())));
Ok(Binding::from_raw(ret))
}
}
pub fn status_should_ignore(&self, path: &Path) -> Result<bool, Error> {
let mut ret = 0 as c_int;
let path = try!(path.into_c_string());
unsafe {
try_call!(raw::git_status_should_ignore(&mut ret, self.raw,
path));
}
Ok(ret != 0)
}
pub fn status_file(&self, path: &Path) -> Result<Status, Error> {
let mut ret = 0 as c_uint;
let path = if cfg!(windows) {
try!(::std::ffi::CString::new(path.to_string_lossy().replace('\\', "/")))
} else {
try!(path.into_c_string())
};
unsafe {
try_call!(raw::git_status_file(&mut ret, self.raw,
path));
}
Ok(Status::from_bits_truncate(ret as u32))
}
pub fn branches(&self, filter: Option<BranchType>)
-> Result<Branches, Error> {
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_branch_iterator_new(&mut raw, self.raw(), filter));
Ok(Branches::from_raw(raw))
}
}
pub fn index(&self) -> Result<Index, Error> {
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_repository_index(&mut raw, self.raw()));
Ok(Binding::from_raw(raw))
}
}
pub fn set_index(&self, index: &mut Index) {
unsafe {
raw::git_repository_set_index(self.raw(), index.raw());
}
}
pub fn config(&self) -> Result<Config, Error> {
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_repository_config(&mut raw, self.raw()));
Ok(Binding::from_raw(raw))
}
}
pub fn blob(&self, data: &[u8]) -> Result<Oid, Error> {
let mut raw = raw::git_oid { id: [0; raw::GIT_OID_RAWSZ] };
unsafe {
let ptr = data.as_ptr() as *const c_void;
let len = data.len() as size_t;
try_call!(raw::git_blob_create_frombuffer(&mut raw, self.raw(),
ptr, len));
Ok(Binding::from_raw(&raw as *const _))
}
}
pub fn blob_path(&self, path: &Path) -> Result<Oid, Error> {
let path = try!(path.into_c_string());
let mut raw = raw::git_oid { id: [0; raw::GIT_OID_RAWSZ] };
unsafe {
try_call!(raw::git_blob_create_fromdisk(&mut raw, self.raw(),
path));
Ok(Binding::from_raw(&raw as *const _))
}
}
pub fn blob_writer(&self, hintpath: Option<&Path>) -> Result<BlobWriter, Error> {
let path_str = match hintpath {
Some(path) => Some(try!(path.into_c_string())),
None => None,
};
let path = match path_str {
Some(ref path) => path.as_ptr(),
None => ptr::null(),
};
let mut out = ptr::null_mut();
unsafe {
try_call!(raw::git_blob_create_fromstream(&mut out, self.raw(), path));
Ok(BlobWriter::from_raw(out))
}
}
pub fn find_blob(&self, oid: Oid) -> Result<Blob, Error> {
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_blob_lookup(&mut raw, self.raw(), oid.raw()));
Ok(Binding::from_raw(raw))
}
}
pub fn odb(&self) -> Result<Odb, Error> {
let mut odb = ptr::null_mut();
unsafe {
try_call!(raw::git_repository_odb(&mut odb, self.raw()));
Ok(Odb::from_raw(odb))
}
}
pub fn branch(&self,
branch_name: &str,
target: &Commit,
force: bool) -> Result<Branch, Error> {
let branch_name = try!(CString::new(branch_name));
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_branch_create(&mut raw,
self.raw(),
branch_name,
target.raw(),
force));
Ok(Branch::wrap(Binding::from_raw(raw)))
}
}
pub fn find_branch(&self, name: &str, branch_type: BranchType)
-> Result<Branch, Error> {
let name = try!(CString::new(name));
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_branch_lookup(&mut ret, self.raw(), name,
branch_type));
Ok(Branch::wrap(Binding::from_raw(ret)))
}
}
pub fn commit(&self,
update_ref: Option<&str>,
author: &Signature,
committer: &Signature,
message: &str,
tree: &Tree,
parents: &[&Commit]) -> Result<Oid, Error> {
let update_ref = try!(::opt_cstr(update_ref));
let mut parent_ptrs = parents.iter().map(|p| {
p.raw() as *const raw::git_commit
}).collect::<Vec<_>>();
let message = try!(CString::new(message));
let mut raw = raw::git_oid { id: [0; raw::GIT_OID_RAWSZ] };
unsafe {
try_call!(raw::git_commit_create(&mut raw,
self.raw(),
update_ref,
author.raw(),
committer.raw(),
ptr::null(),
message,
tree.raw(),
parents.len() as size_t,
parent_ptrs.as_mut_ptr()));
Ok(Binding::from_raw(&raw as *const _))
}
}
pub fn commit_signed(&self,
commit_content: &str,
signature: &str,
signature_field: Option<&str>) -> Result<Oid, Error> {
let commit_content = try!(CString::new(commit_content));
let signature = try!(CString::new(signature));
let signature_field = try!(::opt_cstr(signature_field));
let mut raw = raw::git_oid { id: [0; raw::GIT_OID_RAWSZ] };
unsafe {
try_call!(raw::git_commit_create_with_signature(&mut raw,
self.raw(),
commit_content,
signature,
signature_field));
Ok(Binding::from_raw(&raw as *const _))
}
}
pub fn extract_signature(&self,
commit_id: &Oid,
signature_field: Option<&str>)
-> Result<(Buf, Buf), Error> {
let signature_field = try!(::opt_cstr(signature_field));
let signature = Buf::new();
let content = Buf::new();
unsafe {
try_call!(raw::git_commit_extract_signature(signature.raw(),
content.raw(),
self.raw(),
commit_id.raw() as *mut _,
signature_field));
Ok((signature, content))
}
}
pub fn find_commit(&self, oid: Oid) -> Result<Commit, Error> {
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_commit_lookup(&mut raw, self.raw(), oid.raw()));
Ok(Binding::from_raw(raw))
}
}
pub fn find_annotated_commit(&self, id: Oid) -> Result<AnnotatedCommit, Error> {
unsafe {
let mut raw = 0 as *mut raw::git_annotated_commit;
try_call!(raw::git_annotated_commit_lookup(&mut raw, self.raw(), id.raw()));
Ok(Binding::from_raw(raw))
}
}
pub fn find_object(&self, oid: Oid,
kind: Option<ObjectType>) -> Result<Object, Error> {
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_object_lookup(&mut raw, self.raw(), oid.raw(),
kind));
Ok(Binding::from_raw(raw))
}
}
pub fn reference(&self, name: &str, id: Oid, force: bool,
log_message: &str) -> Result<Reference, Error> {
let name = try!(CString::new(name));
let log_message = try!(CString::new(log_message));
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_reference_create(&mut raw, self.raw(), name,
id.raw(), force,
log_message));
Ok(Binding::from_raw(raw))
}
}
pub fn reference_matching(&self,
name: &str,
id: Oid,
force: bool,
current_id: Oid,
log_message: &str) -> Result<Reference, Error> {
let name = try!(CString::new(name));
let log_message = try!(CString::new(log_message));
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_reference_create_matching(&mut raw,
self.raw(),
name,
id.raw(),
force,
current_id.raw(),
log_message));
Ok(Binding::from_raw(raw))
}
}
pub fn reference_symbolic(&self, name: &str, target: &str,
force: bool,
log_message: &str)
-> Result<Reference, Error> {
let name = try!(CString::new(name));
let target = try!(CString::new(target));
let log_message = try!(CString::new(log_message));
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_reference_symbolic_create(&mut raw, self.raw(),
name, target, force,
log_message));
Ok(Binding::from_raw(raw))
}
}
pub fn reference_symbolic_matching(&self,
name: &str,
target: &str,
force: bool,
current_value: &str,
log_message: &str)
-> Result<Reference, Error> {
let name = try!(CString::new(name));
let target = try!(CString::new(target));
let current_value = try!(CString::new(current_value));
let log_message = try!(CString::new(log_message));
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_reference_symbolic_create_matching(&mut raw,
self.raw(),
name,
target,
force,
current_value,
log_message));
Ok(Binding::from_raw(raw))
}
}
pub fn find_reference(&self, name: &str) -> Result<Reference, Error> {
let name = try!(CString::new(name));
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_reference_lookup(&mut raw, self.raw(), name));
Ok(Binding::from_raw(raw))
}
}
pub fn refname_to_id(&self, name: &str) -> Result<Oid, Error> {
let name = try!(CString::new(name));
let mut ret = raw::git_oid { id: [0; raw::GIT_OID_RAWSZ] };
unsafe {
try_call!(raw::git_reference_name_to_id(&mut ret, self.raw(), name));
Ok(Binding::from_raw(&ret as *const _))
}
}
pub fn reference_to_annotated_commit(&self, reference: &Reference)
-> Result<AnnotatedCommit, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_annotated_commit_from_ref(&mut ret,
self.raw(),
reference.raw()));
Ok(AnnotatedCommit::from_raw(ret))
}
}
pub fn signature(&self) -> Result<Signature<'static>, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_signature_default(&mut ret, self.raw()));
Ok(Binding::from_raw(ret))
}
}
pub fn submodule(&self, url: &str, path: &Path,
use_gitlink: bool) -> Result<Submodule, Error> {
let url = try!(CString::new(url));
let path = try!(path.into_c_string());
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_submodule_add_setup(&mut raw, self.raw(),
url, path, use_gitlink));
Ok(Binding::from_raw(raw))
}
}
pub fn find_submodule(&self, name: &str) -> Result<Submodule, Error> {
let name = try!(CString::new(name));
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_submodule_lookup(&mut raw, self.raw(), name));
Ok(Binding::from_raw(raw))
}
}
pub fn submodule_status(&self, name: &str, ignore: SubmoduleIgnore)
-> Result<SubmoduleStatus, Error> {
let mut ret = 0;
let name = try!(CString::new(name));
unsafe {
try_call!(raw::git_submodule_status(&mut ret, self.raw, name,
ignore));
}
Ok(SubmoduleStatus::from_bits_truncate(ret as u32))
}
pub fn find_tree(&self, oid: Oid) -> Result<Tree, Error> {
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_tree_lookup(&mut raw, self.raw(), oid.raw()));
Ok(Binding::from_raw(raw))
}
}
pub fn treebuilder(&self, tree: Option<&Tree>) -> Result<TreeBuilder, Error> {
unsafe {
let mut ret = ptr::null_mut();
let tree = match tree {
Some(tree) => tree.raw(),
None => ptr::null_mut(),
};
try_call!(raw::git_treebuilder_new(&mut ret, self.raw, tree));
Ok(Binding::from_raw(ret))
}
}
pub fn tag(&self, name: &str, target: &Object,
tagger: &Signature, message: &str,
force: bool) -> Result<Oid, Error> {
let name = try!(CString::new(name));
let message = try!(CString::new(message));
let mut raw = raw::git_oid { id: [0; raw::GIT_OID_RAWSZ] };
unsafe {
try_call!(raw::git_tag_create(&mut raw, self.raw, name,
target.raw(), tagger.raw(),
message, force));
Ok(Binding::from_raw(&raw as *const _))
}
}
pub fn tag_lightweight(&self,
name: &str,
target: &Object,
force: bool) -> Result<Oid, Error> {
let name = try!(CString::new(name));
let mut raw = raw::git_oid { id: [0; raw::GIT_OID_RAWSZ] };
unsafe {
try_call!(raw::git_tag_create_lightweight(&mut raw, self.raw, name,
target.raw(), force));
Ok(Binding::from_raw(&raw as *const _))
}
}
pub fn find_tag(&self, id: Oid) -> Result<Tag, Error> {
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_tag_lookup(&mut raw, self.raw, id.raw()));
Ok(Binding::from_raw(raw))
}
}
pub fn tag_delete(&self, name: &str) -> Result<(), Error> {
let name = try!(CString::new(name));
unsafe {
try_call!(raw::git_tag_delete(self.raw, name));
Ok(())
}
}
pub fn tag_names(&self, pattern: Option<&str>) -> Result<StringArray, Error> {
let mut arr = raw::git_strarray {
strings: 0 as *mut *mut c_char,
count: 0,
};
unsafe {
match pattern {
Some(s) => {
let s = try!(CString::new(s));
try_call!(raw::git_tag_list_match(&mut arr, s, self.raw));
}
None => { try_call!(raw::git_tag_list(&mut arr, self.raw)); }
}
Ok(Binding::from_raw(arr))
}
}
pub fn checkout_head(&self, opts: Option<&mut CheckoutBuilder>)
-> Result<(), Error> {
unsafe {
let mut raw_opts = mem::zeroed();
try_call!(raw::git_checkout_init_options(&mut raw_opts,
raw::GIT_CHECKOUT_OPTIONS_VERSION));
if let Some(c) = opts {
c.configure(&mut raw_opts);
}
try_call!(raw::git_checkout_head(self.raw, &raw_opts));
}
Ok(())
}
pub fn checkout_index(&self,
index: Option<&mut Index>,
opts: Option<&mut CheckoutBuilder>) -> Result<(), Error> {
unsafe {
let mut raw_opts = mem::zeroed();
try_call!(raw::git_checkout_init_options(&mut raw_opts,
raw::GIT_CHECKOUT_OPTIONS_VERSION));
if let Some(c) = opts {
c.configure(&mut raw_opts);
}
try_call!(raw::git_checkout_index(self.raw,
index.map(|i| &mut *i.raw()),
&raw_opts));
}
Ok(())
}
pub fn checkout_tree(&self,
treeish: &Object,
opts: Option<&mut CheckoutBuilder>) -> Result<(), Error> {
unsafe {
let mut raw_opts = mem::zeroed();
try_call!(raw::git_checkout_init_options(&mut raw_opts,
raw::GIT_CHECKOUT_OPTIONS_VERSION));
if let Some(c) = opts {
c.configure(&mut raw_opts);
}
try_call!(raw::git_checkout_tree(self.raw, &*treeish.raw(),
&raw_opts));
}
Ok(())
}
pub fn merge(&self,
annotated_commits: &[&AnnotatedCommit],
merge_opts: Option<&mut MergeOptions>,
checkout_opts: Option<&mut CheckoutBuilder>)
-> Result<(), Error>
{
unsafe {
let mut raw_checkout_opts = mem::zeroed();
try_call!(raw::git_checkout_init_options(&mut raw_checkout_opts,
raw::GIT_CHECKOUT_OPTIONS_VERSION));
if let Some(c) = checkout_opts {
c.configure(&mut raw_checkout_opts);
}
let mut commit_ptrs = annotated_commits.iter().map(|c| {
c.raw() as *const raw::git_annotated_commit
}).collect::<Vec<_>>();
try_call!(raw::git_merge(self.raw,
commit_ptrs.as_mut_ptr(),
annotated_commits.len() as size_t,
merge_opts.map(|o| o.raw())
.unwrap_or(ptr::null()),
&raw_checkout_opts));
}
Ok(())
}
pub fn merge_commits(&self, our_commit: &Commit, their_commit: &Commit,
opts: Option<&MergeOptions>) -> Result<Index, Error> {
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_merge_commits(&mut raw, self.raw,
our_commit.raw(),
their_commit.raw(),
opts.map(|o| o.raw())));
Ok(Binding::from_raw(raw))
}
}
pub fn merge_trees(&self, ancestor_tree: &Tree, our_tree: &Tree,
their_tree: &Tree, opts: Option<&MergeOptions>) -> Result<Index, Error> {
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_merge_trees(&mut raw, self.raw, ancestor_tree.raw(),
our_tree.raw(), their_tree.raw(),
opts.map(|o| o.raw())));
Ok(Binding::from_raw(raw))
}
}
pub fn cleanup_state(&self) -> Result<(), Error> {
unsafe {
try_call!(raw::git_repository_state_cleanup(self.raw));
}
Ok(())
}
pub fn merge_analysis(&self,
their_heads: &[&AnnotatedCommit])
-> Result<(MergeAnalysis, MergePreference), Error> {
unsafe {
let mut raw_merge_analysis = 0 as raw::git_merge_analysis_t;
let mut raw_merge_preference = 0 as raw::git_merge_preference_t;
let mut their_heads = their_heads
.iter()
.map(|v| v.raw() as *const _)
.collect::<Vec<_>>();
try_call!(raw::git_merge_analysis(&mut raw_merge_analysis,
&mut raw_merge_preference,
self.raw,
their_heads.as_mut_ptr() as *mut _,
their_heads.len()));
Ok((MergeAnalysis::from_bits_truncate(raw_merge_analysis as u32), MergePreference::from_bits_truncate(raw_merge_preference as u32)))
}
}
pub fn rebase(&self,
branch: Option<&AnnotatedCommit>,
upstream: Option<&AnnotatedCommit>,
onto: Option<&AnnotatedCommit>,
opts: Option<&mut RebaseOptions>) -> Result<Rebase, Error> {
let mut rebase: *mut raw::git_rebase = ptr::null_mut();
unsafe {
try_call!(raw::git_rebase_init(
&mut rebase,
self.raw(),
branch.map(|c| c.raw()),
upstream.map(|c| c.raw()),
onto.map(|c| c.raw()),
opts.map(|o| o.raw()).unwrap_or(ptr::null())));
Ok(Rebase::from_raw(rebase))
}
}
pub fn open_rebase(&self, opts: Option<&mut RebaseOptions>) -> Result<Rebase, Error> {
let mut rebase: *mut raw::git_rebase = ptr::null_mut();
unsafe {
try_call!(raw::git_rebase_open(&mut rebase, self.raw(), opts.map(|o| o.raw()).unwrap_or(ptr::null())));
Ok(Rebase::from_raw(rebase))
}
}
pub fn note(&self,
author: &Signature,
committer: &Signature,
notes_ref: Option<&str>,
oid: Oid,
note: &str,
force: bool) -> Result<Oid, Error> {
let notes_ref = try!(::opt_cstr(notes_ref));
let note = try!(CString::new(note));
let mut ret = raw::git_oid { id: [0; raw::GIT_OID_RAWSZ] };
unsafe {
try_call!(raw::git_note_create(&mut ret,
self.raw,
notes_ref,
author.raw(),
committer.raw(),
oid.raw(),
note,
force));
Ok(Binding::from_raw(&ret as *const _))
}
}
pub fn note_default_ref(&self) -> Result<String, Error> {
let ret = Buf::new();
unsafe {
try_call!(raw::git_note_default_ref(ret.raw(), self.raw));
}
Ok(str::from_utf8(&ret).unwrap().to_string())
}
pub fn notes(&self, notes_ref: Option<&str>) -> Result<Notes, Error> {
let notes_ref = try!(::opt_cstr(notes_ref));
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_note_iterator_new(&mut ret, self.raw, notes_ref));
Ok(Binding::from_raw(ret))
}
}
pub fn find_note(&self, notes_ref: Option<&str>, id: Oid)
-> Result<Note, Error> {
let notes_ref = try!(::opt_cstr(notes_ref));
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_note_read(&mut ret, self.raw, notes_ref,
id.raw()));
Ok(Binding::from_raw(ret))
}
}
pub fn note_delete(&self,
id: Oid,
notes_ref: Option<&str>,
author: &Signature,
committer: &Signature) -> Result<(), Error> {
let notes_ref = try!(::opt_cstr(notes_ref));
unsafe {
try_call!(raw::git_note_remove(self.raw, notes_ref, author.raw(),
committer.raw(), id.raw()));
Ok(())
}
}
pub fn revwalk(&self) -> Result<Revwalk, Error> {
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_revwalk_new(&mut raw, self.raw()));
Ok(Binding::from_raw(raw))
}
}
pub fn blame_file(&self, path: &Path, opts: Option<&mut BlameOptions>)
-> Result<Blame, Error> {
let path = try!(path.into_c_string());
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_blame_file(&mut raw,
self.raw(),
path,
opts.map(|s| s.raw())));
Ok(Binding::from_raw(raw))
}
}
pub fn merge_base(&self, one: Oid, two: Oid) -> Result<Oid, Error> {
let mut raw = raw::git_oid { id: [0; raw::GIT_OID_RAWSZ] };
unsafe {
try_call!(raw::git_merge_base(&mut raw, self.raw,
one.raw(), two.raw()));
Ok(Binding::from_raw(&raw as *const _))
}
}
pub fn merge_bases(&self, one: Oid, two: Oid) -> Result<OidArray, Error> {
let mut arr = raw::git_oidarray {
ids: ptr::null_mut(),
count: 0,
};
unsafe {
try_call!(raw::git_merge_bases(&mut arr, self.raw,
one.raw(), two.raw()));
Ok(Binding::from_raw(arr))
}
}
pub fn graph_ahead_behind(&self, local: Oid, upstream: Oid)
-> Result<(usize, usize), Error> {
unsafe {
let mut ahead: size_t = 0;
let mut behind: size_t = 0;
try_call!(raw::git_graph_ahead_behind(&mut ahead, &mut behind,
self.raw(), local.raw(),
upstream.raw()));
Ok((ahead as usize, behind as usize))
}
}
pub fn graph_descendant_of(&self, commit: Oid, ancestor: Oid)
-> Result<bool, Error> {
unsafe {
let rv = try_call!(raw::git_graph_descendant_of(self.raw(),
commit.raw(),
ancestor.raw()));
Ok(rv != 0)
}
}
pub fn reflog(&self, name: &str) -> Result<Reflog, Error> {
let name = try!(CString::new(name));
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_reflog_read(&mut ret, self.raw, name));
Ok(Binding::from_raw(ret))
}
}
pub fn reflog_delete(&self, name: &str) -> Result<(), Error> {
let name = try!(CString::new(name));
unsafe { try_call!(raw::git_reflog_delete(self.raw, name)); }
Ok(())
}
pub fn reflog_rename(&self, old_name: &str, new_name: &str)
-> Result<(), Error> {
let old_name = try!(CString::new(old_name));
let new_name = try!(CString::new(new_name));
unsafe {
try_call!(raw::git_reflog_rename(self.raw, old_name, new_name));
}
Ok(())
}
pub fn reference_has_log(&self, name: &str) -> Result<bool, Error> {
let name = try!(CString::new(name));
let ret = unsafe {
try_call!(raw::git_reference_has_log(self.raw, name))
};
Ok(ret != 0)
}
pub fn reference_ensure_log(&self, name: &str) -> Result<(), Error> {
let name = try!(CString::new(name));
unsafe {
try_call!(raw::git_reference_ensure_log(self.raw, name));
}
Ok(())
}
pub fn describe(&self, opts: &DescribeOptions) -> Result<Describe, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_describe_workdir(&mut ret, self.raw, opts.raw()));
Ok(Binding::from_raw(ret))
}
}
pub fn diff_tree_to_tree(&self,
old_tree: Option<&Tree>,
new_tree: Option<&Tree>,
opts: Option<&mut DiffOptions>)
-> Result<Diff, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_diff_tree_to_tree(&mut ret,
self.raw(),
old_tree.map(|s| s.raw()),
new_tree.map(|s| s.raw()),
opts.map(|s| s.raw())));
Ok(Binding::from_raw(ret))
}
}
pub fn diff_tree_to_index(&self,
old_tree: Option<&Tree>,
index: Option<&Index>,
opts: Option<&mut DiffOptions>)
-> Result<Diff, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_diff_tree_to_index(&mut ret,
self.raw(),
old_tree.map(|s| s.raw()),
index.map(|s| s.raw()),
opts.map(|s| s.raw())));
Ok(Binding::from_raw(ret))
}
}
pub fn diff_index_to_index(&self,
old_index: &Index,
new_index: &Index,
opts: Option<&mut DiffOptions>)
-> Result<Diff, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_diff_index_to_index(&mut ret,
self.raw(),
old_index.raw(),
new_index.raw(),
opts.map(|s| s.raw())));
Ok(Binding::from_raw(ret))
}
}
pub fn diff_index_to_workdir(&self,
index: Option<&Index>,
opts: Option<&mut DiffOptions>)
-> Result<Diff, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_diff_index_to_workdir(&mut ret,
self.raw(),
index.map(|s| s.raw()),
opts.map(|s| s.raw())));
Ok(Binding::from_raw(ret))
}
}
pub fn diff_tree_to_workdir(&self,
old_tree: Option<&Tree>,
opts: Option<&mut DiffOptions>)
-> Result<Diff, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_diff_tree_to_workdir(&mut ret,
self.raw(),
old_tree.map(|s| s.raw()),
opts.map(|s| s.raw())));
Ok(Binding::from_raw(ret))
}
}
pub fn diff_tree_to_workdir_with_index(&self,
old_tree: Option<&Tree>,
opts: Option<&mut DiffOptions>)
-> Result<Diff, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_diff_tree_to_workdir_with_index(&mut ret,
self.raw(), old_tree.map(|s| s.raw()), opts.map(|s| s.raw())));
Ok(Binding::from_raw(ret))
}
}
pub fn packbuilder(&self) -> Result<PackBuilder, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_packbuilder_new(&mut ret, self.raw()));
Ok(Binding::from_raw(ret))
}
}
pub fn stash_save(&mut self,
stasher: &Signature,
message: &str,
flags: Option<StashFlags>)
-> Result<Oid, Error> {
unsafe {
let mut raw_oid = raw::git_oid { id: [0; raw::GIT_OID_RAWSZ] };
let message = try!(CString::new(message));
let flags = flags.unwrap_or_else(StashFlags::empty);
try_call!(raw::git_stash_save(&mut raw_oid,
self.raw(),
stasher.raw(),
message,
flags.bits() as c_uint));
Ok(Binding::from_raw(&raw_oid as *const _))
}
}
pub fn stash_apply(&mut self,
index: usize,
opts: Option<&mut StashApplyOptions>)
-> Result<(), Error> {
unsafe {
let opts = opts.map(|opts| opts.raw());
try_call!(raw::git_stash_apply(self.raw(), index, opts));
Ok(())
}
}
pub fn stash_foreach<C>(&mut self, mut callback: C) -> Result<(), Error>
where C: FnMut(usize, &str, &Oid) -> bool
{
unsafe {
let mut data = StashCbData { callback: &mut callback };
try_call!(raw::git_stash_foreach(self.raw(),
stash_cb,
&mut data as *mut _ as *mut _));
Ok(())
}
}
pub fn stash_drop(&mut self, index: usize) -> Result<(), Error> {
unsafe {
try_call!(raw::git_stash_drop(self.raw(), index));
Ok(())
}
}
pub fn stash_pop(&mut self,
index: usize,
opts: Option<&mut StashApplyOptions>)
-> Result<(), Error> {
unsafe {
let opts = opts.map(|opts| opts.raw());
try_call!(raw::git_stash_pop(self.raw(), index, opts));
Ok(())
}
}
pub fn add_ignore_rule(&self, rules: &str) -> Result<(), Error> {
let rules = CString::new(rules)?;
unsafe {
try_call!(raw::git_ignore_add_rule(self.raw, rules));
}
Ok(())
}
pub fn clear_ignore_rules(&self) -> Result<(), Error> {
unsafe {
try_call!(raw::git_ignore_clear_internal_rules(self.raw));
}
Ok(())
}
pub fn is_path_ignored<P: AsRef<Path>>(&self, path: P) -> Result<bool, Error> {
let path = if cfg!(windows) {
try!(::std::ffi::CString::new(path.as_ref().to_string_lossy().replace('\\', "/")))
} else {
try!(path.as_ref().into_c_string())
};
let mut ignored: c_int = 0;
unsafe {
try_call!(raw::git_ignore_path_is_ignored(&mut ignored, self.raw, path));
}
Ok(ignored == 1)
}
}
impl Binding for Repository {
type Raw = *mut raw::git_repository;
unsafe fn from_raw(ptr: *mut raw::git_repository) -> Repository {
Repository { raw: ptr }
}
fn raw(&self) -> *mut raw::git_repository { self.raw }
}
impl Drop for Repository {
fn drop(&mut self) {
unsafe { raw::git_repository_free(self.raw) }
}
}
impl RepositoryInitOptions {
pub fn new() -> RepositoryInitOptions {
RepositoryInitOptions {
flags: raw::GIT_REPOSITORY_INIT_MKDIR as u32 |
raw::GIT_REPOSITORY_INIT_MKPATH as u32 |
raw::GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE as u32,
mode: 0,
workdir_path: None,
description: None,
template_path: None,
initial_head: None,
origin_url: None,
}
}
pub fn bare(&mut self, bare: bool) -> &mut RepositoryInitOptions {
self.flag(raw::GIT_REPOSITORY_INIT_BARE, bare)
}
pub fn no_reinit(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
self.flag(raw::GIT_REPOSITORY_INIT_NO_REINIT, enabled)
}
pub fn no_dotgit_dir(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
self.flag(raw::GIT_REPOSITORY_INIT_NO_DOTGIT_DIR, enabled)
}
pub fn mkdir(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
self.flag(raw::GIT_REPOSITORY_INIT_MKDIR, enabled)
}
pub fn mkpath(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
self.flag(raw::GIT_REPOSITORY_INIT_MKPATH, enabled)
}
pub fn mode(&mut self, mode: RepositoryInitMode)
-> &mut RepositoryInitOptions {
self.mode = mode.bits();
self
}
pub fn external_template(&mut self, enabled: bool)
-> &mut RepositoryInitOptions {
self.flag(raw::GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE, enabled)
}
fn flag(&mut self, flag: raw::git_repository_init_flag_t, on: bool)
-> &mut RepositoryInitOptions {
if on {
self.flags |= flag as u32;
} else {
self.flags &= !(flag as u32);
}
self
}
pub fn workdir_path(&mut self, path: &Path) -> &mut RepositoryInitOptions {
self.workdir_path = Some(path.into_c_string().unwrap());
self
}
pub fn description(&mut self, desc: &str) -> &mut RepositoryInitOptions {
self.description = Some(CString::new(desc).unwrap());
self
}
pub fn template_path(&mut self, path: &Path) -> &mut RepositoryInitOptions {
self.template_path = Some(path.into_c_string().unwrap());
self
}
pub fn initial_head(&mut self, head: &str) -> &mut RepositoryInitOptions {
self.initial_head = Some(CString::new(head).unwrap());
self
}
pub fn origin_url(&mut self, url: &str) -> &mut RepositoryInitOptions {
self.origin_url = Some(CString::new(url).unwrap());
self
}
pub unsafe fn raw(&self) -> raw::git_repository_init_options {
let mut opts = mem::zeroed();
assert_eq!(raw::git_repository_init_init_options(&mut opts,
raw::GIT_REPOSITORY_INIT_OPTIONS_VERSION), 0);
opts.flags = self.flags;
opts.mode = self.mode;
opts.workdir_path = ::call::convert(&self.workdir_path);
opts.description = ::call::convert(&self.description);
opts.template_path = ::call::convert(&self.template_path);
opts.initial_head = ::call::convert(&self.initial_head);
opts.origin_url = ::call::convert(&self.origin_url);
opts
}
}
#[cfg(test)]
mod tests {
use std::ffi::OsStr;
use std::fs;
use std::path::Path;
use tempdir::TempDir;
use {Repository, Oid, ObjectType, ResetType};
use build::CheckoutBuilder;
#[test]
fn smoke_init() {
let td = TempDir::new("test").unwrap();
let path = td.path();
let repo = Repository::init(path).unwrap();
assert!(!repo.is_bare());
}
#[test]
fn smoke_init_bare() {
let td = TempDir::new("test").unwrap();
let path = td.path();
let repo = Repository::init_bare(path).unwrap();
assert!(repo.is_bare());
assert!(repo.namespace().is_none());
}
#[test]
fn smoke_open() {
let td = TempDir::new("test").unwrap();
let path = td.path();
Repository::init(td.path()).unwrap();
let repo = Repository::open(path).unwrap();
assert!(!repo.is_bare());
assert!(!repo.is_shallow());
assert!(repo.is_empty().unwrap());
assert_eq!(::test::realpath(&repo.path()).unwrap(),
::test::realpath(&td.path().join(".git/")).unwrap());
assert_eq!(repo.state(), ::RepositoryState::Clean);
}
#[test]
fn smoke_open_bare() {
let td = TempDir::new("test").unwrap();
let path = td.path();
Repository::init_bare(td.path()).unwrap();
let repo = Repository::open(path).unwrap();
assert!(repo.is_bare());
assert_eq!(::test::realpath(&repo.path()).unwrap(),
::test::realpath(&td.path().join("")).unwrap());
}
#[test]
fn smoke_checkout() {
let (_td, repo) = ::test::repo_init();
repo.checkout_head(None).unwrap();
}
#[test]
fn smoke_revparse() {
let (_td, repo) = ::test::repo_init();
let rev = repo.revparse("HEAD").unwrap();
assert!(rev.to().is_none());
let from = rev.from().unwrap();
assert!(rev.from().is_some());
assert_eq!(repo.revparse_single("HEAD").unwrap().id(), from.id());
let obj = repo.find_object(from.id(), None).unwrap().clone();
obj.peel(ObjectType::Any).unwrap();
obj.short_id().unwrap();
repo.reset(&obj, ResetType::Hard, None).unwrap();
let mut opts = CheckoutBuilder::new();
t!(repo.reset(&obj, ResetType::Soft, Some(&mut opts)));
}
#[test]
fn makes_dirs() {
let td = TempDir::new("foo").unwrap();
Repository::init(&td.path().join("a/b/c/d")).unwrap();
}
#[test]
fn smoke_discover() {
let td = TempDir::new("test").unwrap();
let subdir = td.path().join("subdi");
fs::create_dir(&subdir).unwrap();
Repository::init_bare(td.path()).unwrap();
let repo = Repository::discover(&subdir).unwrap();
assert_eq!(::test::realpath(&repo.path()).unwrap(),
::test::realpath(&td.path().join("")).unwrap());
}
#[test]
fn smoke_open_ext() {
let td = TempDir::new("test").unwrap();
let subdir = td.path().join("subdir");
fs::create_dir(&subdir).unwrap();
Repository::init(td.path()).unwrap();
let repo = Repository::open_ext(&subdir, ::RepositoryOpenFlags::empty(), &[] as &[&OsStr]).unwrap();
assert!(!repo.is_bare());
assert_eq!(::test::realpath(&repo.path()).unwrap(),
::test::realpath(&td.path().join(".git")).unwrap());
let repo = Repository::open_ext(&subdir, ::RepositoryOpenFlags::BARE, &[] as &[&OsStr]).unwrap();
assert!(repo.is_bare());
assert_eq!(::test::realpath(&repo.path()).unwrap(),
::test::realpath(&td.path().join(".git")).unwrap());
let err = Repository::open_ext(&subdir, ::RepositoryOpenFlags::NO_SEARCH, &[] as &[&OsStr]).err().unwrap();
assert_eq!(err.code(), ::ErrorCode::NotFound);
assert!(Repository::open_ext(&subdir,
::RepositoryOpenFlags::empty(),
&[&subdir]).is_ok());
}
fn graph_repo_init() -> (TempDir, Repository) {
let (_td, repo) = ::test::repo_init();
{
let head = repo.head().unwrap().target().unwrap();
let head = repo.find_commit(head).unwrap();
let mut index = repo.index().unwrap();
let id = index.write_tree().unwrap();
let tree = repo.find_tree(id).unwrap();
let sig = repo.signature().unwrap();
repo.commit(Some("HEAD"), &sig, &sig, "second",
&tree, &[&head]).unwrap();
}
(_td, repo)
}
#[test]
fn smoke_graph_ahead_behind() {
let (_td, repo) = graph_repo_init();
let head = repo.head().unwrap().target().unwrap();
let head = repo.find_commit(head).unwrap();
let head_id = head.id();
let head_parent_id = head.parent(0).unwrap().id();
let (ahead, behind) = repo.graph_ahead_behind(head_id,
head_parent_id).unwrap();
assert_eq!(ahead, 1);
assert_eq!(behind, 0);
let (ahead, behind) = repo.graph_ahead_behind(head_parent_id,
head_id).unwrap();
assert_eq!(ahead, 0);
assert_eq!(behind, 1);
}
#[test]
fn smoke_graph_descendant_of() {
let (_td, repo) = graph_repo_init();
let head = repo.head().unwrap().target().unwrap();
let head = repo.find_commit(head).unwrap();
let head_id = head.id();
let head_parent_id = head.parent(0).unwrap().id();
assert!(repo.graph_descendant_of(head_id, head_parent_id).unwrap());
assert!(!repo.graph_descendant_of(head_parent_id, head_id).unwrap());
}
#[test]
fn smoke_reference_has_log_ensure_log() {
let (_td, repo) = ::test::repo_init();
assert_eq!(repo.reference_has_log("HEAD").unwrap(), true);
assert_eq!(repo.reference_has_log("refs/heads/master").unwrap(), true);
assert_eq!(repo.reference_has_log("NOT_HEAD").unwrap(), false);
let master_oid = repo.revparse_single("master").unwrap().id();
assert!(repo.reference("NOT_HEAD", master_oid, false, "creating a new branch").is_ok());
assert_eq!(repo.reference_has_log("NOT_HEAD").unwrap(), false);
assert!(repo.reference_ensure_log("NOT_HEAD").is_ok());
assert_eq!(repo.reference_has_log("NOT_HEAD").unwrap(), true);
}
#[test]
fn smoke_set_head() {
let (_td, repo) = ::test::repo_init();
assert!(repo.set_head("refs/heads/does-not-exist").is_ok());
assert!(repo.head().is_err());
assert!(repo.set_head("refs/heads/master").is_ok());
assert!(repo.head().is_ok());
assert!(repo.set_head("*").is_err());
}
#[test]
fn smoke_set_head_detached() {
let (_td, repo) = ::test::repo_init();
let void_oid = Oid::from_bytes(b"00000000000000000000").unwrap();
assert!(repo.set_head_detached(void_oid).is_err());
let master_oid = repo.revparse_single("master").unwrap().id();
assert!(repo.set_head_detached(master_oid).is_ok());
assert_eq!(repo.head().unwrap().target().unwrap(), master_oid);
}
#[test]
fn smoke_merge_bases() {
let (_td, repo) = graph_repo_init();
let sig = repo.signature().unwrap();
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();
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);
t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
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);
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();
println!("created oid4 {:?}", oid4);
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();
println!("created oid5 {:?}", oid5);
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();
{
let short_refname = "master";
let expected_refname = "refs/heads/master";
let (obj, reference) = repo.revparse_ext(short_refname).unwrap();
let expected_obj = repo.revparse_single(expected_refname).unwrap();
assert_eq!(obj.id(), expected_obj.id());
assert_eq!(reference.unwrap().name().unwrap(), expected_refname);
}
{
let missing_refname = "refs/heads/does-not-exist";
assert!(repo.revparse_ext(missing_refname).is_err());
}
{
let (_obj, reference) = repo.revparse_ext("HEAD^").unwrap();
assert!(reference.is_none());
}
}
#[test]
fn smoke_is_path_ignored() {
let (_td, repo) = graph_repo_init();
assert!(!repo.is_path_ignored(Path::new("/foo")).unwrap());
let _ = repo.add_ignore_rule("/foo");
assert!(repo.is_path_ignored(Path::new("/foo")).unwrap());
if cfg!(windows){
assert!(repo.is_path_ignored(Path::new("\\foo\\thing")).unwrap());
}
let _ = repo.clear_ignore_rules();
assert!(!repo.is_path_ignored(Path::new("/foo")).unwrap());
if cfg!(windows){
assert!(!repo.is_path_ignored(Path::new("\\foo\\thing")).unwrap());
}
}
}