use libc::{c_char, c_int, c_uint, c_void, size_t};
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 crate::build::{CheckoutBuilder, RepoBuilder};
use crate::diff::{
binary_cb_c, file_cb_c, hunk_cb_c, line_cb_c, BinaryCb, DiffCallbacks, FileCb, HunkCb, LineCb,
};
use crate::oid_array::OidArray;
use crate::stash::{stash_cb, StashApplyOptions, StashCbData};
use crate::string_array::StringArray;
use crate::util::{self, path_to_repo_path, Binding};
use crate::CherrypickOptions;
use crate::{
init, raw, AttrCheckFlags, Buf, Error, Object, Remote, RepositoryOpenFlags, RepositoryState,
Revspec, StashFlags,
};
use crate::{
AnnotatedCommit, MergeAnalysis, MergeOptions, MergePreference, SubmoduleIgnore, SubmoduleStatus,
};
use crate::{ApplyLocation, ApplyOptions, Rebase, RebaseOptions};
use crate::{Blame, BlameOptions, Reference, References, ResetType, Signature, Submodule};
use crate::{Blob, BlobWriter, Branch, BranchType, Branches, Commit, Config, Index, Oid, Tree};
use crate::{Describe, IntoCString, Reflog, RepositoryInitMode, RevparseMode};
use crate::{DescribeOptions, Diff, DiffOptions, Odb, PackBuilder, TreeBuilder};
use crate::{Note, Notes, ObjectType, Revwalk, Status, StatusOptions, Statuses, Tag};
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 = 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 = 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 = path.as_ref().into_c_string()?;
let ceiling_dirs_os = env::join_paths(ceiling_dirs)?;
let ceiling_dirs = 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 = 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 = 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> {
crate::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 = 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 = 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 = 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(crate::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 = 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 { crate::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 = 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: ptr::null_mut(),
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 = 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 = CString::new(name)?;
let url = CString::new(url)?;
unsafe {
try_call!(raw::git_remote_create(&mut ret, self.raw, name, url));
Ok(Binding::from_raw(ret))
}
}
pub fn remote_with_fetch(
&self,
name: &str,
url: &str,
fetch: &str,
) -> Result<Remote<'_>, Error> {
let mut ret = ptr::null_mut();
let name = CString::new(name)?;
let url = CString::new(url)?;
let fetch = CString::new(fetch)?;
unsafe {
try_call!(raw::git_remote_create_with_fetchspec(
&mut ret, self.raw, name, url, fetch
));
Ok(Binding::from_raw(ret))
}
}
pub fn remote_anonymous(&self, url: &str) -> Result<Remote<'_>, Error> {
let mut ret = ptr::null_mut();
let url = 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 = CString::new(name)?;
let new_name = CString::new(new_name)?;
let mut problems = raw::git_strarray {
count: 0,
strings: ptr::null_mut(),
};
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 = 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 = CString::new(name)?;
let spec = 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 = CString::new(name)?;
let spec = 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 = CString::new(name)?;
let url = 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 = CString::new(name)?;
let pushurl = crate::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) = crate::util::iter2cstrs_paths(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 = 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 set_head_detached_from_annotated(
&self,
commitish: AnnotatedCommit<'_>,
) -> Result<(), Error> {
unsafe {
try_call!(raw::git_repository_set_head_detached_from_annotated(
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 = 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> {
repo: &'b Repository,
ret: &'a mut Vec<Submodule<'b>>,
}
let mut ret = Vec::new();
unsafe {
let mut data = Data {
repo: self,
ret: &mut ret,
};
let cb: raw::git_submodule_cb = Some(append);
try_call!(raw::git_submodule_foreach(
self.raw,
cb,
&mut data as *mut _ as *mut c_void
));
}
return Ok(ret);
extern "C" 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 = util::cstring_to_repo_path(path)?;
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 = path_to_repo_path(path)?;
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) -> Result<(), Error> {
unsafe {
try_call!(raw::git_repository_set_index(self.raw(), index.raw()));
}
Ok(())
}
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 get_attr(
&self,
path: &Path,
name: &str,
flags: AttrCheckFlags,
) -> Result<Option<&str>, Error> {
Ok(self
.get_attr_bytes(path, name, flags)?
.and_then(|a| str::from_utf8(a).ok()))
}
pub fn get_attr_bytes(
&self,
path: &Path,
name: &str,
flags: AttrCheckFlags,
) -> Result<Option<&[u8]>, Error> {
let mut ret = ptr::null();
let path = util::cstring_to_repo_path(path)?;
let name = CString::new(name)?;
unsafe {
try_call!(raw::git_attr_get(
&mut ret,
self.raw(),
flags.bits(),
path,
name
));
Ok(crate::opt_bytes(self, ret))
}
}
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 = 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(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 = 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 branch_from_annotated_commit(
&self,
branch_name: &str,
target: &AnnotatedCommit<'_>,
force: bool,
) -> Result<Branch<'_>, Error> {
let branch_name = CString::new(branch_name)?;
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_branch_create_from_annotated(
&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 = 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 = crate::opt_cstr(update_ref)?;
let mut parent_ptrs = parents
.iter()
.map(|p| p.raw() as *const raw::git_commit)
.collect::<Vec<_>>();
let message = 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_create_buffer(
&self,
author: &Signature<'_>,
committer: &Signature<'_>,
message: &str,
tree: &Tree<'_>,
parents: &[&Commit<'_>],
) -> Result<Buf, Error> {
let mut parent_ptrs = parents
.iter()
.map(|p| p.raw() as *const raw::git_commit)
.collect::<Vec<_>>();
let message = CString::new(message)?;
let buf = Buf::new();
unsafe {
try_call!(raw::git_commit_create_buffer(
buf.raw(),
self.raw(),
author.raw(),
committer.raw(),
ptr::null(),
message,
tree.raw(),
parents.len() as size_t,
parent_ptrs.as_mut_ptr()
));
Ok(buf)
}
}
pub fn commit_signed(
&self,
commit_content: &str,
signature: &str,
signature_field: Option<&str>,
) -> Result<Oid, Error> {
let commit_content = CString::new(commit_content)?;
let signature = CString::new(signature)?;
let signature_field = crate::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 = crate::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 = ptr::null_mut();
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 = CString::new(name)?;
let log_message = 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 = CString::new(name)?;
let log_message = 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 = CString::new(name)?;
let target = CString::new(target)?;
let log_message = 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 = CString::new(name)?;
let target = CString::new(target)?;
let current_value = CString::new(current_value)?;
let log_message = 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 = 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 resolve_reference_from_short_name(&self, refname: &str) -> Result<Reference<'_>, Error> {
let refname = CString::new(refname)?;
let mut raw = ptr::null_mut();
unsafe {
try_call!(raw::git_reference_dwim(&mut raw, self.raw(), refname));
Ok(Binding::from_raw(raw))
}
}
pub fn refname_to_id(&self, name: &str) -> Result<Oid, Error> {
let name = 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 annotated_commit_from_fetchhead(
&self,
branch_name: &str,
remote_url: &str,
id: &Oid,
) -> Result<AnnotatedCommit<'_>, Error> {
let branch_name = CString::new(branch_name)?;
let remote_url = CString::new(remote_url)?;
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_annotated_commit_from_fetchhead(
&mut ret,
self.raw(),
branch_name,
remote_url,
id.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 = CString::new(url)?;
let path = path_to_repo_path(path)?;
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 = 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 = 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 = CString::new(name)?;
let message = 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 = 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 = 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: ptr::null_mut(),
count: 0,
};
unsafe {
match pattern {
Some(s) => {
let s = 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 = crate::opt_cstr(notes_ref)?;
let note = 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 = crate::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 = crate::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 = crate::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 = path_to_repo_path(path)?;
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_base_many(&self, oids: &[Oid]) -> Result<Oid, Error> {
let mut raw = raw::git_oid {
id: [0; raw::GIT_OID_RAWSZ],
};
unsafe {
try_call!(raw::git_merge_base_many(
&mut raw,
self.raw,
oids.len() as size_t,
oids.as_ptr() as *const raw::git_oid
));
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 merge_bases_many(&self, oids: &[Oid]) -> Result<OidArray, Error> {
let mut arr = raw::git_oidarray {
ids: ptr::null_mut(),
count: 0,
};
unsafe {
try_call!(raw::git_merge_bases_many(
&mut arr,
self.raw,
oids.len() as size_t,
oids.as_ptr() as *const raw::git_oid
));
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 = 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 = 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 = CString::new(old_name)?;
let new_name = 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 = 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 = 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_blobs(
&self,
old_blob: Option<&Blob<'_>>,
old_as_path: Option<&str>,
new_blob: Option<&Blob<'_>>,
new_as_path: Option<&str>,
opts: Option<&mut DiffOptions>,
file_cb: Option<&mut FileCb<'_>>,
binary_cb: Option<&mut BinaryCb<'_>>,
hunk_cb: Option<&mut HunkCb<'_>>,
line_cb: Option<&mut LineCb<'_>>,
) -> Result<(), Error> {
let old_as_path = crate::opt_cstr(old_as_path)?;
let new_as_path = crate::opt_cstr(new_as_path)?;
let mut cbs = DiffCallbacks {
file: file_cb,
binary: binary_cb,
hunk: hunk_cb,
line: line_cb,
};
let ptr = &mut cbs as *mut _;
unsafe {
let file_cb_c: raw::git_diff_file_cb = if cbs.file.is_some() {
Some(file_cb_c)
} else {
None
};
let binary_cb_c: raw::git_diff_binary_cb = if cbs.binary.is_some() {
Some(binary_cb_c)
} else {
None
};
let hunk_cb_c: raw::git_diff_hunk_cb = if cbs.hunk.is_some() {
Some(hunk_cb_c)
} else {
None
};
let line_cb_c: raw::git_diff_line_cb = if cbs.line.is_some() {
Some(line_cb_c)
} else {
None
};
try_call!(raw::git_diff_blobs(
old_blob.map(|s| s.raw()),
old_as_path,
new_blob.map(|s| s.raw()),
new_as_path,
opts.map(|s| s.raw()),
file_cb_c,
binary_cb_c,
hunk_cb_c,
line_cb_c,
ptr as *mut _
));
Ok(())
}
}
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 = 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,
};
let cb: raw::git_stash_cb = Some(stash_cb);
try_call!(raw::git_stash_foreach(
self.raw(),
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 = util::cstring_to_repo_path(path.as_ref())?;
let mut ignored: c_int = 0;
unsafe {
try_call!(raw::git_ignore_path_is_ignored(
&mut ignored,
self.raw,
path
));
}
Ok(ignored == 1)
}
pub fn cherrypick(
&self,
commit: &Commit<'_>,
options: Option<&mut CherrypickOptions<'_>>,
) -> Result<(), Error> {
let raw_opts = options.map(|o| o.raw());
let ptr_raw_opts = match raw_opts.as_ref() {
Some(v) => v,
None => 0 as *const _,
};
unsafe {
try_call!(raw::git_cherrypick(self.raw(), commit.raw(), ptr_raw_opts));
Ok(())
}
}
pub fn cherrypick_commit(
&self,
cherrypick_commit: &Commit<'_>,
our_commit: &Commit<'_>,
mainline: u32,
options: Option<&MergeOptions>,
) -> Result<Index, Error> {
let mut ret = ptr::null_mut();
unsafe {
try_call!(raw::git_cherrypick_commit(
&mut ret,
self.raw(),
cherrypick_commit.raw(),
our_commit.raw(),
mainline,
options.map(|o| o.raw())
));
Ok(Binding::from_raw(ret))
}
}
pub fn branch_upstream_name(&self, refname: &str) -> Result<Buf, Error> {
let refname = CString::new(refname)?;
unsafe {
let buf = Buf::new();
try_call!(raw::git_branch_upstream_name(buf.raw(), self.raw, refname));
Ok(buf)
}
}
pub fn branch_upstream_remote(&self, refname: &str) -> Result<Buf, Error> {
let refname = CString::new(refname)?;
unsafe {
let buf = Buf::new();
try_call!(raw::git_branch_upstream_remote(
buf.raw(),
self.raw,
refname
));
Ok(buf)
}
}
pub fn apply(
&self,
diff: &Diff<'_>,
location: ApplyLocation,
options: Option<&mut ApplyOptions<'_>>,
) -> Result<(), Error> {
unsafe {
try_call!(raw::git_apply(
self.raw,
diff.raw(),
location.raw(),
options.map(|s| s.raw()).unwrap_or(ptr::null())
));
Ok(())
}
}
}
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 = crate::call::convert(&self.workdir_path);
opts.description = crate::call::convert(&self.description);
opts.template_path = crate::call::convert(&self.template_path);
opts.initial_head = crate::call::convert(&self.initial_head);
opts.origin_url = crate::call::convert(&self.origin_url);
opts
}
}
#[cfg(test)]
mod tests {
use crate::build::CheckoutBuilder;
use crate::CherrypickOptions;
use crate::{ObjectType, Oid, Repository, ResetType};
use std::ffi::OsStr;
use std::fs;
use std::path::Path;
use tempfile::TempDir;
#[test]
fn smoke_init() {
let td = TempDir::new().unwrap();
let path = td.path();
let repo = Repository::init(path).unwrap();
assert!(!repo.is_bare());
}
#[test]
fn smoke_init_bare() {
let td = TempDir::new().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().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!(
crate::test::realpath(&repo.path()).unwrap(),
crate::test::realpath(&td.path().join(".git/")).unwrap()
);
assert_eq!(repo.state(), crate::RepositoryState::Clean);
}
#[test]
fn smoke_open_bare() {
let td = TempDir::new().unwrap();
let path = td.path();
Repository::init_bare(td.path()).unwrap();
let repo = Repository::open(path).unwrap();
assert!(repo.is_bare());
assert_eq!(
crate::test::realpath(&repo.path()).unwrap(),
crate::test::realpath(&td.path().join("")).unwrap()
);
}
#[test]
fn smoke_checkout() {
let (_td, repo) = crate::test::repo_init();
repo.checkout_head(None).unwrap();
}
#[test]
fn smoke_revparse() {
let (_td, repo) = crate::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().unwrap();
Repository::init(&td.path().join("a/b/c/d")).unwrap();
}
#[test]
fn smoke_discover() {
let td = TempDir::new().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!(
crate::test::realpath(&repo.path()).unwrap(),
crate::test::realpath(&td.path().join("")).unwrap()
);
}
#[test]
fn smoke_open_ext() {
let td = TempDir::new().unwrap();
let subdir = td.path().join("subdir");
fs::create_dir(&subdir).unwrap();
Repository::init(td.path()).unwrap();
let repo = Repository::open_ext(
&subdir,
crate::RepositoryOpenFlags::empty(),
&[] as &[&OsStr],
)
.unwrap();
assert!(!repo.is_bare());
assert_eq!(
crate::test::realpath(&repo.path()).unwrap(),
crate::test::realpath(&td.path().join(".git")).unwrap()
);
let repo =
Repository::open_ext(&subdir, crate::RepositoryOpenFlags::BARE, &[] as &[&OsStr])
.unwrap();
assert!(repo.is_bare());
assert_eq!(
crate::test::realpath(&repo.path()).unwrap(),
crate::test::realpath(&td.path().join(".git")).unwrap()
);
let err = Repository::open_ext(
&subdir,
crate::RepositoryOpenFlags::NO_SEARCH,
&[] as &[&OsStr],
)
.err()
.unwrap();
assert_eq!(err.code(), crate::ErrorCode::NotFound);
assert!(
Repository::open_ext(&subdir, crate::RepositoryOpenFlags::empty(), &[&subdir]).is_ok()
);
}
fn graph_repo_init() -> (TempDir, Repository) {
let (_td, repo) = crate::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) = crate::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) = crate::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) = crate::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_base() {
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();
repo.branch("branch_c", &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();
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();
repo.find_commit(oid3).unwrap();
println!("created oid3 {:?}", oid3);
t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
let mut index = repo.index().unwrap();
let p = Path::new(repo.workdir().unwrap()).join("file_c");
fs::File::create(&p).unwrap();
index.add_path(Path::new("file_c")).unwrap();
let id_c = index.write_tree().unwrap();
let tree_c = repo.find_tree(id_c).unwrap();
let oid4 = repo
.commit(
Some("refs/heads/branch_c"),
&sig,
&sig,
"commit 3",
&tree_c,
&[&commit1],
)
.unwrap();
repo.find_commit(oid4).unwrap();
println!("created oid4 {:?}", oid4);
let merge_base = repo.merge_base(oid2, oid3).unwrap();
assert_eq!(merge_base, oid1);
let merge_base = repo.merge_base_many(&[oid2, oid3, oid4]).unwrap();
assert_eq!(merge_base, oid1);
}
#[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);
let merge_bases = repo.merge_bases_many(&[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());
}
}
#[test]
fn smoke_cherrypick() {
let (_td, repo) = crate::test::repo_init();
let sig = repo.signature().unwrap();
let oid1 = repo.head().unwrap().target().unwrap();
let commit1 = repo.find_commit(oid1).unwrap();
repo.branch("branch_a", &commit1, true).unwrap();
let mut index = repo.index().unwrap();
let p1 = Path::new(repo.workdir().unwrap()).join("file_c");
fs::File::create(&p1).unwrap();
index.add_path(Path::new("file_c")).unwrap();
let id = index.write_tree().unwrap();
let tree_c = repo.find_tree(id).unwrap();
let oid2 = repo
.commit(
Some("refs/heads/branch_a"),
&sig,
&sig,
"commit 2",
&tree_c,
&[&commit1],
)
.unwrap();
let commit2 = repo.find_commit(oid2).unwrap();
println!("created oid2 {:?}", oid2);
assert!(p1.exists());
let mut index = repo.index().unwrap();
let p2 = Path::new(repo.workdir().unwrap()).join("file_d");
fs::File::create(&p2).unwrap();
index.add_path(Path::new("file_d")).unwrap();
let id = index.write_tree().unwrap();
let tree_d = repo.find_tree(id).unwrap();
let oid3 = repo
.commit(
Some("refs/heads/branch_a"),
&sig,
&sig,
"commit 3",
&tree_d,
&[&commit2],
)
.unwrap();
let commit3 = repo.find_commit(oid3).unwrap();
println!("created oid3 {:?}", oid3);
assert!(p1.exists());
assert!(p2.exists());
repo.reset(commit1.as_object(), ResetType::Hard, None)
.unwrap();
let mut cherrypick_opts = CherrypickOptions::new();
repo.cherrypick(&commit3, Some(&mut cherrypick_opts))
.unwrap();
let id = repo.index().unwrap().write_tree().unwrap();
let tree_d = repo.find_tree(id).unwrap();
let oid4 = repo
.commit(Some("HEAD"), &sig, &sig, "commit 4", &tree_d, &[&commit1])
.unwrap();
let commit4 = repo.find_commit(oid4).unwrap();
assert_eq!(commit4.parent(0).unwrap().id(), commit1.id());
assert!(!p1.exists());
assert!(p2.exists());
}
}