1use libc::{c_char, c_int, c_uint, c_void, size_t};
2use std::env;
3use std::ffi::{CStr, CString, OsStr};
4use std::mem;
5use std::path::{Path, PathBuf};
6use std::ptr;
7use std::str;
8
9use crate::build::{CheckoutBuilder, RepoBuilder};
10use crate::diff::{
11 binary_cb_c, file_cb_c, hunk_cb_c, line_cb_c, BinaryCb, DiffCallbacks, FileCb, HunkCb, LineCb,
12};
13use crate::oid_array::OidArray;
14use crate::stash::{stash_cb, StashApplyOptions, StashCbData, StashSaveOptions};
15use crate::string_array::StringArray;
16use crate::tagforeach::{tag_foreach_cb, TagForeachCB, TagForeachData};
17use crate::util::{self, path_to_repo_path, Binding};
18use crate::worktree::{Worktree, WorktreeAddOptions};
19use crate::CherrypickOptions;
20use crate::RevertOptions;
21use crate::{mailmap::Mailmap, panic};
22use crate::{
23 raw, AttrCheckFlags, Buf, Error, Object, Remote, RepositoryOpenFlags, RepositoryState, Revspec,
24 StashFlags,
25};
26use crate::{
27 AnnotatedCommit, MergeAnalysis, MergeOptions, MergePreference, SubmoduleIgnore,
28 SubmoduleStatus, SubmoduleUpdate,
29};
30use crate::{ApplyLocation, ApplyOptions, Rebase, RebaseOptions};
31use crate::{Blame, BlameOptions, Reference, References, ResetType, Signature, Submodule};
32use crate::{Blob, BlobWriter, Branch, BranchType, Branches, Commit, Config, Index, Oid, Tree};
33use crate::{Describe, IntoCString, Reflog, RepositoryInitMode, RevparseMode};
34use crate::{DescribeOptions, Diff, DiffOptions, Odb, PackBuilder, TreeBuilder};
35use crate::{Note, Notes, ObjectType, Revwalk, Status, StatusOptions, Statuses, Tag, Transaction};
36
37type MergeheadForeachCb<'a> = dyn FnMut(&Oid) -> bool + 'a;
38type FetchheadForeachCb<'a> = dyn FnMut(&str, &[u8], &Oid, bool) -> bool + 'a;
39
40struct FetchheadForeachCbData<'a> {
41 callback: &'a mut FetchheadForeachCb<'a>,
42}
43
44struct MergeheadForeachCbData<'a> {
45 callback: &'a mut MergeheadForeachCb<'a>,
46}
47
48extern "C" fn mergehead_foreach_cb(oid: *const raw::git_oid, payload: *mut c_void) -> c_int {
49 panic::wrap(|| unsafe {
50 let data = &mut *(payload as *mut MergeheadForeachCbData<'_>);
51 let res = {
52 let callback = &mut data.callback;
53 callback(&Binding::from_raw(oid))
54 };
55
56 if res {
57 0
58 } else {
59 1
60 }
61 })
62 .unwrap_or(1)
63}
64
65extern "C" fn fetchhead_foreach_cb(
66 ref_name: *const c_char,
67 remote_url: *const c_char,
68 oid: *const raw::git_oid,
69 is_merge: c_uint,
70 payload: *mut c_void,
71) -> c_int {
72 panic::wrap(|| unsafe {
73 let data = &mut *(payload as *mut FetchheadForeachCbData<'_>);
74 let res = {
75 let callback = &mut data.callback;
76
77 assert!(!ref_name.is_null());
78 assert!(!remote_url.is_null());
79 assert!(!oid.is_null());
80
81 let ref_name = str::from_utf8(CStr::from_ptr(ref_name).to_bytes()).unwrap();
82 let remote_url = CStr::from_ptr(remote_url).to_bytes();
83 let oid = Binding::from_raw(oid);
84 let is_merge = is_merge == 1;
85
86 callback(&ref_name, remote_url, &oid, is_merge)
87 };
88
89 if res {
90 0
91 } else {
92 1
93 }
94 })
95 .unwrap_or(1)
96}
97
98pub struct Repository {
108 raw: *mut raw::git_repository,
109}
110
111unsafe impl Send for Repository {}
114
115pub struct RepositoryInitOptions {
117 flags: u32,
118 mode: u32,
119 workdir_path: Option<CString>,
120 description: Option<CString>,
121 template_path: Option<CString>,
122 initial_head: Option<CString>,
123 origin_url: Option<CString>,
124}
125
126impl Repository {
127 pub fn open<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
131 crate::init();
132 let path = path.as_ref().into_c_string()?;
134 let mut ret = ptr::null_mut();
135 unsafe {
136 try_call!(raw::git_repository_open(&mut ret, path));
137 Ok(Binding::from_raw(ret))
138 }
139 }
140
141 pub fn open_bare<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
145 crate::init();
146 let path = path.as_ref().into_c_string()?;
148 let mut ret = ptr::null_mut();
149 unsafe {
150 try_call!(raw::git_repository_open_bare(&mut ret, path));
151 Ok(Binding::from_raw(ret))
152 }
153 }
154
155 pub fn open_from_env() -> Result<Repository, Error> {
161 crate::init();
162 let mut ret = ptr::null_mut();
163 let flags = raw::GIT_REPOSITORY_OPEN_FROM_ENV;
164 unsafe {
165 try_call!(raw::git_repository_open_ext(
166 &mut ret,
167 ptr::null(),
168 flags as c_uint,
169 ptr::null()
170 ));
171 Ok(Binding::from_raw(ret))
172 }
173 }
174
175 pub fn open_ext<P, O, I>(
204 path: P,
205 flags: RepositoryOpenFlags,
206 ceiling_dirs: I,
207 ) -> Result<Repository, Error>
208 where
209 P: AsRef<Path>,
210 O: AsRef<OsStr>,
211 I: IntoIterator<Item = O>,
212 {
213 crate::init();
214 let path = path.as_ref().into_c_string()?;
216 let ceiling_dirs_os = env::join_paths(ceiling_dirs)?;
217 let ceiling_dirs = ceiling_dirs_os.into_c_string()?;
218 let mut ret = ptr::null_mut();
219 unsafe {
220 try_call!(raw::git_repository_open_ext(
221 &mut ret,
222 path,
223 flags.bits() as c_uint,
224 ceiling_dirs
225 ));
226 Ok(Binding::from_raw(ret))
227 }
228 }
229
230 pub fn open_from_worktree(worktree: &Worktree) -> Result<Repository, Error> {
232 let mut ret = ptr::null_mut();
233 unsafe {
234 try_call!(raw::git_repository_open_from_worktree(
235 &mut ret,
236 worktree.raw()
237 ));
238 Ok(Binding::from_raw(ret))
239 }
240 }
241
242 pub fn discover<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
247 crate::init();
249 let buf = Buf::new();
250 let path = path.as_ref().into_c_string()?;
252 unsafe {
253 try_call!(raw::git_repository_discover(
254 buf.raw(),
255 path,
256 1,
257 ptr::null()
258 ));
259 }
260 Repository::open(util::bytes2path(&*buf))
261 }
262
263 pub fn discover_path<P: AsRef<Path>, I, O>(path: P, ceiling_dirs: I) -> Result<PathBuf, Error>
268 where
269 O: AsRef<OsStr>,
270 I: IntoIterator<Item = O>,
271 {
272 crate::init();
273 let buf = Buf::new();
274 let path = path.as_ref().into_c_string()?;
276 let ceiling_dirs_os = env::join_paths(ceiling_dirs)?;
277 let ceiling_dirs = ceiling_dirs_os.into_c_string()?;
278 unsafe {
279 try_call!(raw::git_repository_discover(
280 buf.raw(),
281 path,
282 1,
283 ceiling_dirs
284 ));
285 }
286
287 Ok(util::bytes2path(&*buf).to_path_buf())
288 }
289
290 pub fn init<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
296 Repository::init_opts(path, &RepositoryInitOptions::new())
297 }
298
299 pub fn init_bare<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
303 Repository::init_opts(path, RepositoryInitOptions::new().bare(true))
304 }
305
306 pub fn init_opts<P: AsRef<Path>>(
310 path: P,
311 opts: &RepositoryInitOptions,
312 ) -> Result<Repository, Error> {
313 crate::init();
314 let path = path.as_ref().into_c_string()?;
316 let mut ret = ptr::null_mut();
317 unsafe {
318 let mut opts = opts.raw();
319 try_call!(raw::git_repository_init_ext(&mut ret, path, &mut opts));
320 Ok(Binding::from_raw(ret))
321 }
322 }
323
324 pub fn clone<P: AsRef<Path>>(url: &str, into: P) -> Result<Repository, Error> {
329 crate::init();
330 RepoBuilder::new().clone(url, into.as_ref())
331 }
332
333 pub fn clone_recurse<P: AsRef<Path>>(url: &str, into: P) -> Result<Repository, Error> {
338 let repo = Repository::clone(url, into)?;
339 repo.update_submodules()?;
340 Ok(repo)
341 }
342
343 pub fn from_odb(odb: Odb<'_>) -> Result<Repository, Error> {
345 crate::init();
346 let mut ret = ptr::null_mut();
347 unsafe {
348 try_call!(raw::git_repository_wrap_odb(&mut ret, odb.raw()));
349 Ok(Binding::from_raw(ret))
350 }
351 }
352
353 fn update_submodules(&self) -> Result<(), Error> {
357 fn add_subrepos(repo: &Repository, list: &mut Vec<Repository>) -> Result<(), Error> {
358 for mut subm in repo.submodules()? {
359 subm.update(true, None)?;
360 list.push(subm.open()?);
361 }
362 Ok(())
363 }
364
365 let mut repos = Vec::new();
366 add_subrepos(self, &mut repos)?;
367 while let Some(repo) = repos.pop() {
368 add_subrepos(&repo, &mut repos)?;
369 }
370 Ok(())
371 }
372
373 pub fn revparse(&self, spec: &str) -> Result<Revspec<'_>, Error> {
378 let mut raw = raw::git_revspec {
379 from: ptr::null_mut(),
380 to: ptr::null_mut(),
381 flags: 0,
382 };
383 let spec = CString::new(spec)?;
384 unsafe {
385 try_call!(raw::git_revparse(&mut raw, self.raw, spec));
386 let to = Binding::from_raw_opt(raw.to);
387 let from = Binding::from_raw_opt(raw.from);
388 let mode = RevparseMode::from_bits_truncate(raw.flags as u32);
389 Ok(Revspec::from_objects(from, to, mode))
390 }
391 }
392
393 pub fn revparse_single(&self, spec: &str) -> Result<Object<'_>, Error> {
395 let spec = CString::new(spec)?;
396 let mut obj = ptr::null_mut();
397 unsafe {
398 try_call!(raw::git_revparse_single(&mut obj, self.raw, spec));
399 assert!(!obj.is_null());
400 Ok(Binding::from_raw(obj))
401 }
402 }
403
404 pub fn revparse_ext(&self, spec: &str) -> Result<(Object<'_>, Option<Reference<'_>>), Error> {
414 let spec = CString::new(spec)?;
415 let mut git_obj = ptr::null_mut();
416 let mut git_ref = ptr::null_mut();
417 unsafe {
418 try_call!(raw::git_revparse_ext(
419 &mut git_obj,
420 &mut git_ref,
421 self.raw,
422 spec
423 ));
424 assert!(!git_obj.is_null());
425 Ok((Binding::from_raw(git_obj), Binding::from_raw_opt(git_ref)))
426 }
427 }
428
429 pub fn is_bare(&self) -> bool {
431 unsafe { raw::git_repository_is_bare(self.raw) == 1 }
432 }
433
434 pub fn is_shallow(&self) -> bool {
436 unsafe { raw::git_repository_is_shallow(self.raw) == 1 }
437 }
438
439 pub fn is_worktree(&self) -> bool {
441 unsafe { raw::git_repository_is_worktree(self.raw) == 1 }
442 }
443
444 pub fn is_empty(&self) -> Result<bool, Error> {
446 let empty = unsafe { try_call!(raw::git_repository_is_empty(self.raw)) };
447 Ok(empty == 1)
448 }
449
450 pub fn path(&self) -> &Path {
453 unsafe {
454 let ptr = raw::git_repository_path(self.raw);
455 util::bytes2path(crate::opt_bytes(self, ptr).unwrap())
456 }
457 }
458
459 pub fn commondir(&self) -> &Path {
465 unsafe {
466 let ptr = raw::git_repository_commondir(self.raw);
467 util::bytes2path(crate::opt_bytes(self, ptr).unwrap())
468 }
469 }
470
471 pub fn state(&self) -> RepositoryState {
473 let state = unsafe { raw::git_repository_state(self.raw) };
474 macro_rules! check( ($($raw:ident => $real:ident),*) => (
475 $(if state == raw::$raw as c_int {
476 super::RepositoryState::$real
477 }) else *
478 else {
479 panic!("unknown repository state: {}", state)
480 }
481 ) );
482
483 check!(
484 GIT_REPOSITORY_STATE_NONE => Clean,
485 GIT_REPOSITORY_STATE_MERGE => Merge,
486 GIT_REPOSITORY_STATE_REVERT => Revert,
487 GIT_REPOSITORY_STATE_REVERT_SEQUENCE => RevertSequence,
488 GIT_REPOSITORY_STATE_CHERRYPICK => CherryPick,
489 GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE => CherryPickSequence,
490 GIT_REPOSITORY_STATE_BISECT => Bisect,
491 GIT_REPOSITORY_STATE_REBASE => Rebase,
492 GIT_REPOSITORY_STATE_REBASE_INTERACTIVE => RebaseInteractive,
493 GIT_REPOSITORY_STATE_REBASE_MERGE => RebaseMerge,
494 GIT_REPOSITORY_STATE_APPLY_MAILBOX => ApplyMailbox,
495 GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE => ApplyMailboxOrRebase
496 )
497 }
498
499 pub fn workdir(&self) -> Option<&Path> {
503 unsafe {
504 let ptr = raw::git_repository_workdir(self.raw);
505 if ptr.is_null() {
506 None
507 } else {
508 Some(util::bytes2path(CStr::from_ptr(ptr).to_bytes()))
509 }
510 }
511 }
512
513 pub fn set_workdir(&self, path: &Path, update_gitlink: bool) -> Result<(), Error> {
519 let path = path.into_c_string()?;
521 unsafe {
522 try_call!(raw::git_repository_set_workdir(
523 self.raw(),
524 path,
525 update_gitlink
526 ));
527 }
528 Ok(())
529 }
530
531 pub fn namespace(&self) -> Option<&str> {
536 self.namespace_bytes().and_then(|s| str::from_utf8(s).ok())
537 }
538
539 pub fn namespace_bytes(&self) -> Option<&[u8]> {
543 unsafe { crate::opt_bytes(self, raw::git_repository_get_namespace(self.raw)) }
544 }
545
546 pub fn set_namespace(&self, namespace: &str) -> Result<(), Error> {
548 self.set_namespace_bytes(namespace.as_bytes())
549 }
550
551 pub fn set_namespace_bytes(&self, namespace: &[u8]) -> Result<(), Error> {
553 unsafe {
554 let namespace = CString::new(namespace)?;
555 try_call!(raw::git_repository_set_namespace(self.raw, namespace));
556 Ok(())
557 }
558 }
559
560 pub fn remove_namespace(&self) -> Result<(), Error> {
562 unsafe {
563 try_call!(raw::git_repository_set_namespace(self.raw, ptr::null()));
564 Ok(())
565 }
566 }
567
568 pub fn message(&self) -> Result<String, Error> {
571 unsafe {
572 let buf = Buf::new();
573 try_call!(raw::git_repository_message(buf.raw(), self.raw));
574 Ok(str::from_utf8(&buf).unwrap().to_string())
575 }
576 }
577
578 pub fn remove_message(&self) -> Result<(), Error> {
580 unsafe {
581 try_call!(raw::git_repository_message_remove(self.raw));
582 Ok(())
583 }
584 }
585
586 pub fn remotes(&self) -> Result<StringArray, Error> {
588 let mut arr = raw::git_strarray {
589 strings: ptr::null_mut(),
590 count: 0,
591 };
592 unsafe {
593 try_call!(raw::git_remote_list(&mut arr, self.raw));
594 Ok(Binding::from_raw(arr))
595 }
596 }
597
598 pub fn find_remote(&self, name: &str) -> Result<Remote<'_>, Error> {
600 let mut ret = ptr::null_mut();
601 let name = CString::new(name)?;
602 unsafe {
603 try_call!(raw::git_remote_lookup(&mut ret, self.raw, name));
604 Ok(Binding::from_raw(ret))
605 }
606 }
607
608 pub fn remote(&self, name: &str, url: &str) -> Result<Remote<'_>, Error> {
611 let mut ret = ptr::null_mut();
612 let name = CString::new(name)?;
613 let url = CString::new(url)?;
614 unsafe {
615 try_call!(raw::git_remote_create(&mut ret, self.raw, name, url));
616 Ok(Binding::from_raw(ret))
617 }
618 }
619
620 pub fn remote_with_fetch(
623 &self,
624 name: &str,
625 url: &str,
626 fetch: &str,
627 ) -> Result<Remote<'_>, Error> {
628 let mut ret = ptr::null_mut();
629 let name = CString::new(name)?;
630 let url = CString::new(url)?;
631 let fetch = CString::new(fetch)?;
632 unsafe {
633 try_call!(raw::git_remote_create_with_fetchspec(
634 &mut ret, self.raw, name, url, fetch
635 ));
636 Ok(Binding::from_raw(ret))
637 }
638 }
639
640 pub fn remote_anonymous(&self, url: &str) -> Result<Remote<'_>, Error> {
646 let mut ret = ptr::null_mut();
647 let url = CString::new(url)?;
648 unsafe {
649 try_call!(raw::git_remote_create_anonymous(&mut ret, self.raw, url));
650 Ok(Binding::from_raw(ret))
651 }
652 }
653
654 pub fn remote_rename(&self, name: &str, new_name: &str) -> Result<StringArray, Error> {
668 let name = CString::new(name)?;
669 let new_name = CString::new(new_name)?;
670 let mut problems = raw::git_strarray {
671 count: 0,
672 strings: ptr::null_mut(),
673 };
674 unsafe {
675 try_call!(raw::git_remote_rename(
676 &mut problems,
677 self.raw,
678 name,
679 new_name
680 ));
681 Ok(Binding::from_raw(problems))
682 }
683 }
684
685 pub fn remote_delete(&self, name: &str) -> Result<(), Error> {
690 let name = CString::new(name)?;
691 unsafe {
692 try_call!(raw::git_remote_delete(self.raw, name));
693 }
694 Ok(())
695 }
696
697 pub fn remote_add_fetch(&self, name: &str, spec: &str) -> Result<(), Error> {
702 let name = CString::new(name)?;
703 let spec = CString::new(spec)?;
704 unsafe {
705 try_call!(raw::git_remote_add_fetch(self.raw, name, spec));
706 }
707 Ok(())
708 }
709
710 pub fn remote_add_push(&self, name: &str, spec: &str) -> Result<(), Error> {
715 let name = CString::new(name)?;
716 let spec = CString::new(spec)?;
717 unsafe {
718 try_call!(raw::git_remote_add_push(self.raw, name, spec));
719 }
720 Ok(())
721 }
722
723 pub fn remote_set_url(&self, name: &str, url: &str) -> Result<(), Error> {
729 let name = CString::new(name)?;
730 let url = CString::new(url)?;
731 unsafe {
732 try_call!(raw::git_remote_set_url(self.raw, name, url));
733 }
734 Ok(())
735 }
736
737 pub fn remote_set_pushurl(&self, name: &str, pushurl: Option<&str>) -> Result<(), Error> {
745 let name = CString::new(name)?;
746 let pushurl = crate::opt_cstr(pushurl)?;
747 unsafe {
748 try_call!(raw::git_remote_set_pushurl(self.raw, name, pushurl));
749 }
750 Ok(())
751 }
752
753 pub fn reset(
771 &self,
772 target: &Object<'_>,
773 kind: ResetType,
774 checkout: Option<&mut CheckoutBuilder<'_>>,
775 ) -> Result<(), Error> {
776 unsafe {
777 let mut opts: raw::git_checkout_options = mem::zeroed();
778 try_call!(raw::git_checkout_init_options(
779 &mut opts,
780 raw::GIT_CHECKOUT_OPTIONS_VERSION
781 ));
782 let opts = checkout.map(|c| {
783 c.configure(&mut opts);
784 &mut opts
785 });
786 try_call!(raw::git_reset(self.raw, target.raw(), kind, opts));
787 }
788 Ok(())
789 }
790
791 pub fn reset_default<T, I>(&self, target: Option<&Object<'_>>, paths: I) -> Result<(), Error>
799 where
800 T: IntoCString,
801 I: IntoIterator<Item = T>,
802 {
803 let (_a, _b, mut arr) = crate::util::iter2cstrs_paths(paths)?;
804 let target = target.map(|t| t.raw());
805 unsafe {
806 try_call!(raw::git_reset_default(self.raw, target, &mut arr));
807 }
808 Ok(())
809 }
810
811 pub fn head(&self) -> Result<Reference<'_>, Error> {
813 let mut ret = ptr::null_mut();
814 unsafe {
815 try_call!(raw::git_repository_head(&mut ret, self.raw));
816 Ok(Binding::from_raw(ret))
817 }
818 }
819
820 pub fn set_head(&self, refname: &str) -> Result<(), Error> {
833 self.set_head_bytes(refname.as_bytes())
834 }
835
836 pub fn set_head_bytes(&self, refname: &[u8]) -> Result<(), Error> {
849 let refname = CString::new(refname)?;
850 unsafe {
851 try_call!(raw::git_repository_set_head(self.raw, refname));
852 }
853 Ok(())
854 }
855
856 pub fn head_detached(&self) -> Result<bool, Error> {
858 unsafe {
859 let value = raw::git_repository_head_detached(self.raw);
860 match value {
861 0 => Ok(false),
862 1 => Ok(true),
863 _ => Err(Error::last_error(value)),
864 }
865 }
866 }
867
868 pub fn set_head_detached(&self, commitish: Oid) -> Result<(), Error> {
879 unsafe {
880 try_call!(raw::git_repository_set_head_detached(
881 self.raw,
882 commitish.raw()
883 ));
884 }
885 Ok(())
886 }
887
888 pub fn set_head_detached_from_annotated(
897 &self,
898 commitish: AnnotatedCommit<'_>,
899 ) -> Result<(), Error> {
900 unsafe {
901 try_call!(raw::git_repository_set_head_detached_from_annotated(
902 self.raw,
903 commitish.raw()
904 ));
905 }
906 Ok(())
907 }
908
909 pub fn references(&self) -> Result<References<'_>, Error> {
911 let mut ret = ptr::null_mut();
912 unsafe {
913 try_call!(raw::git_reference_iterator_new(&mut ret, self.raw));
914 Ok(Binding::from_raw(ret))
915 }
916 }
917
918 pub fn references_glob(&self, glob: &str) -> Result<References<'_>, Error> {
921 let mut ret = ptr::null_mut();
922 let glob = CString::new(glob)?;
923 unsafe {
924 try_call!(raw::git_reference_iterator_glob_new(
925 &mut ret, self.raw, glob
926 ));
927
928 Ok(Binding::from_raw(ret))
929 }
930 }
931
932 pub fn submodules(&self) -> Result<Vec<Submodule<'_>>, Error> {
934 struct Data<'a, 'b> {
935 repo: &'b Repository,
936 ret: &'a mut Vec<Submodule<'b>>,
937 }
938 let mut ret = Vec::new();
939
940 unsafe {
941 let mut data = Data {
942 repo: self,
943 ret: &mut ret,
944 };
945 let cb: raw::git_submodule_cb = Some(append);
946 try_call!(raw::git_submodule_foreach(
947 self.raw,
948 cb,
949 &mut data as *mut _ as *mut c_void
950 ));
951 }
952
953 return Ok(ret);
954
955 extern "C" fn append(
956 _repo: *mut raw::git_submodule,
957 name: *const c_char,
958 data: *mut c_void,
959 ) -> c_int {
960 unsafe {
961 let data = &mut *(data as *mut Data<'_, '_>);
962 let mut raw = ptr::null_mut();
963 let rc = raw::git_submodule_lookup(&mut raw, data.repo.raw(), name);
964 assert_eq!(rc, 0);
965 data.ret.push(Binding::from_raw(raw));
966 }
967 0
968 }
969 }
970
971 pub fn statuses(&self, options: Option<&mut StatusOptions>) -> Result<Statuses<'_>, Error> {
978 let mut ret = ptr::null_mut();
979 unsafe {
980 try_call!(raw::git_status_list_new(
981 &mut ret,
982 self.raw,
983 options.map(|s| s.raw()).unwrap_or(ptr::null())
984 ));
985 Ok(Binding::from_raw(ret))
986 }
987 }
988
989 pub fn status_should_ignore(&self, path: &Path) -> Result<bool, Error> {
998 let mut ret = 0 as c_int;
999 let path = util::cstring_to_repo_path(path)?;
1000 unsafe {
1001 try_call!(raw::git_status_should_ignore(&mut ret, self.raw, path));
1002 }
1003 Ok(ret != 0)
1004 }
1005
1006 pub fn status_file(&self, path: &Path) -> Result<Status, Error> {
1023 let mut ret = 0 as c_uint;
1024 let path = path_to_repo_path(path)?;
1025 unsafe {
1026 try_call!(raw::git_status_file(&mut ret, self.raw, path));
1027 }
1028 Ok(Status::from_bits_truncate(ret as u32))
1029 }
1030
1031 pub fn branches(&self, filter: Option<BranchType>) -> Result<Branches<'_>, Error> {
1033 let mut raw = ptr::null_mut();
1034 unsafe {
1035 try_call!(raw::git_branch_iterator_new(&mut raw, self.raw(), filter));
1036 Ok(Branches::from_raw(raw))
1037 }
1038 }
1039
1040 pub fn index(&self) -> Result<Index, Error> {
1050 let mut raw = ptr::null_mut();
1051 unsafe {
1052 try_call!(raw::git_repository_index(&mut raw, self.raw()));
1053 Ok(Binding::from_raw(raw))
1054 }
1055 }
1056
1057 pub fn set_index(&self, index: &mut Index) -> Result<(), Error> {
1059 unsafe {
1060 try_call!(raw::git_repository_set_index(self.raw(), index.raw()));
1061 }
1062 Ok(())
1063 }
1064
1065 pub fn config(&self) -> Result<Config, Error> {
1071 let mut raw = ptr::null_mut();
1072 unsafe {
1073 try_call!(raw::git_repository_config(&mut raw, self.raw()));
1074 Ok(Binding::from_raw(raw))
1075 }
1076 }
1077
1078 pub fn get_attr(
1088 &self,
1089 path: &Path,
1090 name: &str,
1091 flags: AttrCheckFlags,
1092 ) -> Result<Option<&str>, Error> {
1093 Ok(self
1094 .get_attr_bytes(path, name, flags)?
1095 .and_then(|a| str::from_utf8(a).ok()))
1096 }
1097
1098 pub fn get_attr_bytes(
1108 &self,
1109 path: &Path,
1110 name: &str,
1111 flags: AttrCheckFlags,
1112 ) -> Result<Option<&[u8]>, Error> {
1113 let mut ret = ptr::null();
1114 let path = util::cstring_to_repo_path(path)?;
1115 let name = CString::new(name)?;
1116 unsafe {
1117 try_call!(raw::git_attr_get(
1118 &mut ret,
1119 self.raw(),
1120 flags.bits(),
1121 path,
1122 name
1123 ));
1124 Ok(crate::opt_bytes(self, ret))
1125 }
1126 }
1127
1128 pub fn blob(&self, data: &[u8]) -> Result<Oid, Error> {
1133 let mut raw = raw::git_oid {
1134 id: [0; raw::GIT_OID_RAWSZ],
1135 };
1136 unsafe {
1137 let ptr = data.as_ptr() as *const c_void;
1138 let len = data.len() as size_t;
1139 try_call!(raw::git_blob_create_frombuffer(
1140 &mut raw,
1141 self.raw(),
1142 ptr,
1143 len
1144 ));
1145 Ok(Binding::from_raw(&raw as *const _))
1146 }
1147 }
1148
1149 pub fn blob_path(&self, path: &Path) -> Result<Oid, Error> {
1155 let path = path.into_c_string()?;
1157 let mut raw = raw::git_oid {
1158 id: [0; raw::GIT_OID_RAWSZ],
1159 };
1160 unsafe {
1161 try_call!(raw::git_blob_create_fromdisk(&mut raw, self.raw(), path));
1162 Ok(Binding::from_raw(&raw as *const _))
1163 }
1164 }
1165
1166 pub fn blob_writer(&self, hintpath: Option<&Path>) -> Result<BlobWriter<'_>, Error> {
1178 let path_str = match hintpath {
1179 Some(path) => Some(path.into_c_string()?),
1180 None => None,
1181 };
1182 let path = match path_str {
1183 Some(ref path) => path.as_ptr(),
1184 None => ptr::null(),
1185 };
1186 let mut out = ptr::null_mut();
1187 unsafe {
1188 try_call!(raw::git_blob_create_fromstream(&mut out, self.raw(), path));
1189 Ok(BlobWriter::from_raw(out))
1190 }
1191 }
1192
1193 pub fn find_blob(&self, oid: Oid) -> Result<Blob<'_>, Error> {
1195 let mut raw = ptr::null_mut();
1196 unsafe {
1197 try_call!(raw::git_blob_lookup(&mut raw, self.raw(), oid.raw()));
1198 Ok(Binding::from_raw(raw))
1199 }
1200 }
1201
1202 pub fn odb(&self) -> Result<Odb<'_>, Error> {
1204 let mut odb = ptr::null_mut();
1205 unsafe {
1206 try_call!(raw::git_repository_odb(&mut odb, self.raw()));
1207 Ok(Odb::from_raw(odb))
1208 }
1209 }
1210
1211 pub fn set_odb(&self, odb: &Odb<'_>) -> Result<(), Error> {
1213 unsafe {
1214 try_call!(raw::git_repository_set_odb(self.raw(), odb.raw()));
1215 }
1216 Ok(())
1217 }
1218
1219 pub fn branch(
1225 &self,
1226 branch_name: &str,
1227 target: &Commit<'_>,
1228 force: bool,
1229 ) -> Result<Branch<'_>, Error> {
1230 let branch_name = CString::new(branch_name)?;
1231 let mut raw = ptr::null_mut();
1232 unsafe {
1233 try_call!(raw::git_branch_create(
1234 &mut raw,
1235 self.raw(),
1236 branch_name,
1237 target.raw(),
1238 force
1239 ));
1240 Ok(Branch::wrap(Binding::from_raw(raw)))
1241 }
1242 }
1243
1244 pub fn branch_from_annotated_commit(
1253 &self,
1254 branch_name: &str,
1255 target: &AnnotatedCommit<'_>,
1256 force: bool,
1257 ) -> Result<Branch<'_>, Error> {
1258 let branch_name = CString::new(branch_name)?;
1259 let mut raw = ptr::null_mut();
1260 unsafe {
1261 try_call!(raw::git_branch_create_from_annotated(
1262 &mut raw,
1263 self.raw(),
1264 branch_name,
1265 target.raw(),
1266 force
1267 ));
1268 Ok(Branch::wrap(Binding::from_raw(raw)))
1269 }
1270 }
1271
1272 pub fn find_branch(&self, name: &str, branch_type: BranchType) -> Result<Branch<'_>, Error> {
1274 let name = CString::new(name)?;
1275 let mut ret = ptr::null_mut();
1276 unsafe {
1277 try_call!(raw::git_branch_lookup(
1278 &mut ret,
1279 self.raw(),
1280 name,
1281 branch_type
1282 ));
1283 Ok(Branch::wrap(Binding::from_raw(ret)))
1284 }
1285 }
1286
1287 pub fn commit(
1296 &self,
1297 update_ref: Option<&str>,
1298 author: &Signature<'_>,
1299 committer: &Signature<'_>,
1300 message: &str,
1301 tree: &Tree<'_>,
1302 parents: &[&Commit<'_>],
1303 ) -> Result<Oid, Error> {
1304 let update_ref = crate::opt_cstr(update_ref)?;
1305 let mut parent_ptrs = parents
1306 .iter()
1307 .map(|p| p.raw() as *const raw::git_commit)
1308 .collect::<Vec<_>>();
1309 let message = CString::new(message)?;
1310 let mut raw = raw::git_oid {
1311 id: [0; raw::GIT_OID_RAWSZ],
1312 };
1313 unsafe {
1314 try_call!(raw::git_commit_create(
1315 &mut raw,
1316 self.raw(),
1317 update_ref,
1318 author.raw(),
1319 committer.raw(),
1320 ptr::null(),
1321 message,
1322 tree.raw(),
1323 parents.len() as size_t,
1324 parent_ptrs.as_mut_ptr()
1325 ));
1326 Ok(Binding::from_raw(&raw as *const _))
1327 }
1328 }
1329
1330 pub fn commit_create_buffer(
1336 &self,
1337 author: &Signature<'_>,
1338 committer: &Signature<'_>,
1339 message: &str,
1340 tree: &Tree<'_>,
1341 parents: &[&Commit<'_>],
1342 ) -> Result<Buf, Error> {
1343 let mut parent_ptrs = parents
1344 .iter()
1345 .map(|p| p.raw() as *const raw::git_commit)
1346 .collect::<Vec<_>>();
1347 let message = CString::new(message)?;
1348 let buf = Buf::new();
1349 unsafe {
1350 try_call!(raw::git_commit_create_buffer(
1351 buf.raw(),
1352 self.raw(),
1353 author.raw(),
1354 committer.raw(),
1355 ptr::null(),
1356 message,
1357 tree.raw(),
1358 parents.len() as size_t,
1359 parent_ptrs.as_mut_ptr()
1360 ));
1361 Ok(buf)
1362 }
1363 }
1364
1365 pub fn commit_signed(
1376 &self,
1377 commit_content: &str,
1378 signature: &str,
1379 signature_field: Option<&str>,
1380 ) -> Result<Oid, Error> {
1381 let commit_content = CString::new(commit_content)?;
1382 let signature = CString::new(signature)?;
1383 let signature_field = crate::opt_cstr(signature_field)?;
1384 let mut raw = raw::git_oid {
1385 id: [0; raw::GIT_OID_RAWSZ],
1386 };
1387 unsafe {
1388 try_call!(raw::git_commit_create_with_signature(
1389 &mut raw,
1390 self.raw(),
1391 commit_content,
1392 signature,
1393 signature_field
1394 ));
1395 Ok(Binding::from_raw(&raw as *const _))
1396 }
1397 }
1398
1399 pub fn extract_signature(
1404 &self,
1405 commit_id: &Oid,
1406 signature_field: Option<&str>,
1407 ) -> Result<(Buf, Buf), Error> {
1408 let signature_field = crate::opt_cstr(signature_field)?;
1409 let signature = Buf::new();
1410 let content = Buf::new();
1411 unsafe {
1412 try_call!(raw::git_commit_extract_signature(
1413 signature.raw(),
1414 content.raw(),
1415 self.raw(),
1416 commit_id.raw() as *mut _,
1417 signature_field
1418 ));
1419 Ok((signature, content))
1420 }
1421 }
1422
1423 pub fn find_commit(&self, oid: Oid) -> Result<Commit<'_>, Error> {
1425 let mut raw = ptr::null_mut();
1426 unsafe {
1427 try_call!(raw::git_commit_lookup(&mut raw, self.raw(), oid.raw()));
1428 Ok(Binding::from_raw(raw))
1429 }
1430 }
1431
1432 pub fn find_commit_by_prefix(&self, prefix_hash: &str) -> Result<Commit<'_>, Error> {
1434 let mut raw = ptr::null_mut();
1435 unsafe {
1436 try_call!(raw::git_commit_lookup_prefix(
1437 &mut raw,
1438 self.raw(),
1439 Oid::from_str(prefix_hash)?.raw(),
1440 prefix_hash.len()
1441 ));
1442 Ok(Binding::from_raw(raw))
1443 }
1444 }
1445
1446 pub fn find_annotated_commit(&self, id: Oid) -> Result<AnnotatedCommit<'_>, Error> {
1448 unsafe {
1449 let mut raw = ptr::null_mut();
1450 try_call!(raw::git_annotated_commit_lookup(
1451 &mut raw,
1452 self.raw(),
1453 id.raw()
1454 ));
1455 Ok(Binding::from_raw(raw))
1456 }
1457 }
1458
1459 pub fn find_object(&self, oid: Oid, kind: Option<ObjectType>) -> Result<Object<'_>, Error> {
1461 let mut raw = ptr::null_mut();
1462 unsafe {
1463 try_call!(raw::git_object_lookup(
1464 &mut raw,
1465 self.raw(),
1466 oid.raw(),
1467 kind
1468 ));
1469 Ok(Binding::from_raw(raw))
1470 }
1471 }
1472
1473 pub fn find_object_by_prefix(
1475 &self,
1476 prefix_hash: &str,
1477 kind: Option<ObjectType>,
1478 ) -> Result<Object<'_>, Error> {
1479 let mut raw = ptr::null_mut();
1480 unsafe {
1481 try_call!(raw::git_object_lookup_prefix(
1482 &mut raw,
1483 self.raw(),
1484 Oid::from_str(prefix_hash)?.raw(),
1485 prefix_hash.len(),
1486 kind
1487 ));
1488 Ok(Binding::from_raw(raw))
1489 }
1490 }
1491
1492 pub fn reference(
1498 &self,
1499 name: &str,
1500 id: Oid,
1501 force: bool,
1502 log_message: &str,
1503 ) -> Result<Reference<'_>, Error> {
1504 let name = CString::new(name)?;
1505 let log_message = CString::new(log_message)?;
1506 let mut raw = ptr::null_mut();
1507 unsafe {
1508 try_call!(raw::git_reference_create(
1509 &mut raw,
1510 self.raw(),
1511 name,
1512 id.raw(),
1513 force,
1514 log_message
1515 ));
1516 Ok(Binding::from_raw(raw))
1517 }
1518 }
1519
1520 pub fn reference_matching(
1551 &self,
1552 name: &str,
1553 id: Oid,
1554 force: bool,
1555 current_id: Oid,
1556 log_message: &str,
1557 ) -> Result<Reference<'_>, Error> {
1558 let name = CString::new(name)?;
1559 let log_message = CString::new(log_message)?;
1560 let mut raw = ptr::null_mut();
1561 unsafe {
1562 try_call!(raw::git_reference_create_matching(
1563 &mut raw,
1564 self.raw(),
1565 name,
1566 id.raw(),
1567 force,
1568 current_id.raw(),
1569 log_message
1570 ));
1571 Ok(Binding::from_raw(raw))
1572 }
1573 }
1574
1575 pub fn reference_symbolic(
1594 &self,
1595 name: &str,
1596 target: &str,
1597 force: bool,
1598 log_message: &str,
1599 ) -> Result<Reference<'_>, Error> {
1600 let name = CString::new(name)?;
1601 let target = CString::new(target)?;
1602 let log_message = CString::new(log_message)?;
1603 let mut raw = ptr::null_mut();
1604 unsafe {
1605 try_call!(raw::git_reference_symbolic_create(
1606 &mut raw,
1607 self.raw(),
1608 name,
1609 target,
1610 force,
1611 log_message
1612 ));
1613 Ok(Binding::from_raw(raw))
1614 }
1615 }
1616
1617 pub fn reference_symbolic_matching(
1627 &self,
1628 name: &str,
1629 target: &str,
1630 force: bool,
1631 current_value: &str,
1632 log_message: &str,
1633 ) -> Result<Reference<'_>, Error> {
1634 let name = CString::new(name)?;
1635 let target = CString::new(target)?;
1636 let current_value = CString::new(current_value)?;
1637 let log_message = CString::new(log_message)?;
1638 let mut raw = ptr::null_mut();
1639 unsafe {
1640 try_call!(raw::git_reference_symbolic_create_matching(
1641 &mut raw,
1642 self.raw(),
1643 name,
1644 target,
1645 force,
1646 current_value,
1647 log_message
1648 ));
1649 Ok(Binding::from_raw(raw))
1650 }
1651 }
1652
1653 pub fn find_reference(&self, name: &str) -> Result<Reference<'_>, Error> {
1655 let name = CString::new(name)?;
1656 let mut raw = ptr::null_mut();
1657 unsafe {
1658 try_call!(raw::git_reference_lookup(&mut raw, self.raw(), name));
1659 Ok(Binding::from_raw(raw))
1660 }
1661 }
1662
1663 pub fn resolve_reference_from_short_name(&self, refname: &str) -> Result<Reference<'_>, Error> {
1668 let refname = CString::new(refname)?;
1669 let mut raw = ptr::null_mut();
1670 unsafe {
1671 try_call!(raw::git_reference_dwim(&mut raw, self.raw(), refname));
1672 Ok(Binding::from_raw(raw))
1673 }
1674 }
1675
1676 pub fn refname_to_id(&self, name: &str) -> Result<Oid, Error> {
1682 let name = CString::new(name)?;
1683 let mut ret = raw::git_oid {
1684 id: [0; raw::GIT_OID_RAWSZ],
1685 };
1686 unsafe {
1687 try_call!(raw::git_reference_name_to_id(&mut ret, self.raw(), name));
1688 Ok(Binding::from_raw(&ret as *const _))
1689 }
1690 }
1691
1692 pub fn reference_to_annotated_commit(
1694 &self,
1695 reference: &Reference<'_>,
1696 ) -> Result<AnnotatedCommit<'_>, Error> {
1697 let mut ret = ptr::null_mut();
1698 unsafe {
1699 try_call!(raw::git_annotated_commit_from_ref(
1700 &mut ret,
1701 self.raw(),
1702 reference.raw()
1703 ));
1704 Ok(AnnotatedCommit::from_raw(ret))
1705 }
1706 }
1707
1708 pub fn annotated_commit_from_fetchhead(
1710 &self,
1711 branch_name: &str,
1712 remote_url: &str,
1713 id: &Oid,
1714 ) -> Result<AnnotatedCommit<'_>, Error> {
1715 let branch_name = CString::new(branch_name)?;
1716 let remote_url = CString::new(remote_url)?;
1717
1718 let mut ret = ptr::null_mut();
1719 unsafe {
1720 try_call!(raw::git_annotated_commit_from_fetchhead(
1721 &mut ret,
1722 self.raw(),
1723 branch_name,
1724 remote_url,
1725 id.raw()
1726 ));
1727 Ok(AnnotatedCommit::from_raw(ret))
1728 }
1729 }
1730
1731 pub fn signature(&self) -> Result<Signature<'static>, Error> {
1738 let mut ret = ptr::null_mut();
1739 unsafe {
1740 try_call!(raw::git_signature_default(&mut ret, self.raw()));
1741 Ok(Binding::from_raw(ret))
1742 }
1743 }
1744
1745 pub fn submodule(
1758 &self,
1759 url: &str,
1760 path: &Path,
1761 use_gitlink: bool,
1762 ) -> Result<Submodule<'_>, Error> {
1763 let url = CString::new(url)?;
1764 let path = path_to_repo_path(path)?;
1765 let mut raw = ptr::null_mut();
1766 unsafe {
1767 try_call!(raw::git_submodule_add_setup(
1768 &mut raw,
1769 self.raw(),
1770 url,
1771 path,
1772 use_gitlink
1773 ));
1774 Ok(Binding::from_raw(raw))
1775 }
1776 }
1777
1778 pub fn find_submodule(&self, name: &str) -> Result<Submodule<'_>, Error> {
1783 let name = CString::new(name)?;
1784 let mut raw = ptr::null_mut();
1785 unsafe {
1786 try_call!(raw::git_submodule_lookup(&mut raw, self.raw(), name));
1787 Ok(Binding::from_raw(raw))
1788 }
1789 }
1790
1791 pub fn submodule_status(
1796 &self,
1797 name: &str,
1798 ignore: SubmoduleIgnore,
1799 ) -> Result<SubmoduleStatus, Error> {
1800 let mut ret = 0;
1801 let name = CString::new(name)?;
1802 unsafe {
1803 try_call!(raw::git_submodule_status(&mut ret, self.raw, name, ignore));
1804 }
1805 Ok(SubmoduleStatus::from_bits_truncate(ret as u32))
1806 }
1807
1808 pub fn submodule_set_ignore(
1812 &mut self,
1813 name: &str,
1814 ignore: SubmoduleIgnore,
1815 ) -> Result<(), Error> {
1816 let name = CString::new(name)?;
1817 unsafe {
1818 try_call!(raw::git_submodule_set_ignore(self.raw(), name, ignore));
1819 }
1820 Ok(())
1821 }
1822
1823 pub fn submodule_set_update(
1827 &mut self,
1828 name: &str,
1829 update: SubmoduleUpdate,
1830 ) -> Result<(), Error> {
1831 let name = CString::new(name)?;
1832 unsafe {
1833 try_call!(raw::git_submodule_set_update(self.raw(), name, update));
1834 }
1835 Ok(())
1836 }
1837
1838 pub fn submodule_set_url(&mut self, name: &str, url: &str) -> Result<(), Error> {
1843 let name = CString::new(name)?;
1844 let url = CString::new(url)?;
1845 unsafe {
1846 try_call!(raw::git_submodule_set_url(self.raw(), name, url));
1847 }
1848 Ok(())
1849 }
1850
1851 pub fn submodule_set_branch(&mut self, name: &str, branch_name: &str) -> Result<(), Error> {
1856 let name = CString::new(name)?;
1857 let branch_name = CString::new(branch_name)?;
1858 unsafe {
1859 try_call!(raw::git_submodule_set_branch(self.raw(), name, branch_name));
1860 }
1861 Ok(())
1862 }
1863
1864 pub fn find_tree(&self, oid: Oid) -> Result<Tree<'_>, Error> {
1866 let mut raw = ptr::null_mut();
1867 unsafe {
1868 try_call!(raw::git_tree_lookup(&mut raw, self.raw(), oid.raw()));
1869 Ok(Binding::from_raw(raw))
1870 }
1871 }
1872
1873 pub fn treebuilder(&self, tree: Option<&Tree<'_>>) -> Result<TreeBuilder<'_>, Error> {
1879 unsafe {
1880 let mut ret = ptr::null_mut();
1881 let tree = match tree {
1882 Some(tree) => tree.raw(),
1883 None => ptr::null_mut(),
1884 };
1885 try_call!(raw::git_treebuilder_new(&mut ret, self.raw, tree));
1886 Ok(Binding::from_raw(ret))
1887 }
1888 }
1889
1890 pub fn tag(
1902 &self,
1903 name: &str,
1904 target: &Object<'_>,
1905 tagger: &Signature<'_>,
1906 message: &str,
1907 force: bool,
1908 ) -> Result<Oid, Error> {
1909 let name = CString::new(name)?;
1910 let message = CString::new(message)?;
1911 let mut raw = raw::git_oid {
1912 id: [0; raw::GIT_OID_RAWSZ],
1913 };
1914 unsafe {
1915 try_call!(raw::git_tag_create(
1916 &mut raw,
1917 self.raw,
1918 name,
1919 target.raw(),
1920 tagger.raw(),
1921 message,
1922 force
1923 ));
1924 Ok(Binding::from_raw(&raw as *const _))
1925 }
1926 }
1927
1928 pub fn tag_annotation_create(
1936 &self,
1937 name: &str,
1938 target: &Object<'_>,
1939 tagger: &Signature<'_>,
1940 message: &str,
1941 ) -> Result<Oid, Error> {
1942 let name = CString::new(name)?;
1943 let message = CString::new(message)?;
1944 let mut raw_oid = raw::git_oid {
1945 id: [0; raw::GIT_OID_RAWSZ],
1946 };
1947 unsafe {
1948 try_call!(raw::git_tag_annotation_create(
1949 &mut raw_oid,
1950 self.raw,
1951 name,
1952 target.raw(),
1953 tagger.raw(),
1954 message
1955 ));
1956 Ok(Binding::from_raw(&raw_oid as *const _))
1957 }
1958 }
1959
1960 pub fn tag_lightweight(
1966 &self,
1967 name: &str,
1968 target: &Object<'_>,
1969 force: bool,
1970 ) -> Result<Oid, Error> {
1971 let name = CString::new(name)?;
1972 let mut raw = raw::git_oid {
1973 id: [0; raw::GIT_OID_RAWSZ],
1974 };
1975 unsafe {
1976 try_call!(raw::git_tag_create_lightweight(
1977 &mut raw,
1978 self.raw,
1979 name,
1980 target.raw(),
1981 force
1982 ));
1983 Ok(Binding::from_raw(&raw as *const _))
1984 }
1985 }
1986
1987 pub fn find_tag(&self, id: Oid) -> Result<Tag<'_>, Error> {
1989 let mut raw = ptr::null_mut();
1990 unsafe {
1991 try_call!(raw::git_tag_lookup(&mut raw, self.raw, id.raw()));
1992 Ok(Binding::from_raw(raw))
1993 }
1994 }
1995
1996 pub fn find_tag_by_prefix(&self, prefix_hash: &str) -> Result<Tag<'_>, Error> {
1998 let mut raw = ptr::null_mut();
1999 unsafe {
2000 try_call!(raw::git_tag_lookup_prefix(
2001 &mut raw,
2002 self.raw,
2003 Oid::from_str(prefix_hash)?.raw(),
2004 prefix_hash.len()
2005 ));
2006 Ok(Binding::from_raw(raw))
2007 }
2008 }
2009
2010 pub fn tag_delete(&self, name: &str) -> Result<(), Error> {
2015 let name = CString::new(name)?;
2016 unsafe {
2017 try_call!(raw::git_tag_delete(self.raw, name));
2018 Ok(())
2019 }
2020 }
2021
2022 pub fn tag_names(&self, pattern: Option<&str>) -> Result<StringArray, Error> {
2026 let mut arr = raw::git_strarray {
2027 strings: ptr::null_mut(),
2028 count: 0,
2029 };
2030 unsafe {
2031 match pattern {
2032 Some(s) => {
2033 let s = CString::new(s)?;
2034 try_call!(raw::git_tag_list_match(&mut arr, s, self.raw));
2035 }
2036 None => {
2037 try_call!(raw::git_tag_list(&mut arr, self.raw));
2038 }
2039 }
2040 Ok(Binding::from_raw(arr))
2041 }
2042 }
2043
2044 pub fn tag_foreach<T>(&self, cb: T) -> Result<(), Error>
2047 where
2048 T: FnMut(Oid, &[u8]) -> bool,
2049 {
2050 let mut data = TagForeachData {
2051 cb: Box::new(cb) as TagForeachCB<'_>,
2052 };
2053
2054 unsafe {
2055 raw::git_tag_foreach(
2056 self.raw,
2057 Some(tag_foreach_cb),
2058 (&mut data) as *mut _ as *mut _,
2059 );
2060 }
2061 Ok(())
2062 }
2063
2064 pub fn checkout_head(&self, opts: Option<&mut CheckoutBuilder<'_>>) -> Result<(), Error> {
2067 unsafe {
2068 let mut raw_opts = mem::zeroed();
2069 try_call!(raw::git_checkout_init_options(
2070 &mut raw_opts,
2071 raw::GIT_CHECKOUT_OPTIONS_VERSION
2072 ));
2073 if let Some(c) = opts {
2074 c.configure(&mut raw_opts);
2075 }
2076
2077 try_call!(raw::git_checkout_head(self.raw, &raw_opts));
2078 }
2079 Ok(())
2080 }
2081
2082 pub fn checkout_index(
2086 &self,
2087 index: Option<&mut Index>,
2088 opts: Option<&mut CheckoutBuilder<'_>>,
2089 ) -> Result<(), Error> {
2090 unsafe {
2091 let mut raw_opts = mem::zeroed();
2092 try_call!(raw::git_checkout_init_options(
2093 &mut raw_opts,
2094 raw::GIT_CHECKOUT_OPTIONS_VERSION
2095 ));
2096 if let Some(c) = opts {
2097 c.configure(&mut raw_opts);
2098 }
2099
2100 try_call!(raw::git_checkout_index(
2101 self.raw,
2102 index.map(|i| &mut *i.raw()),
2103 &raw_opts
2104 ));
2105 }
2106 Ok(())
2107 }
2108
2109 pub fn checkout_tree(
2112 &self,
2113 treeish: &Object<'_>,
2114 opts: Option<&mut CheckoutBuilder<'_>>,
2115 ) -> Result<(), Error> {
2116 unsafe {
2117 let mut raw_opts = mem::zeroed();
2118 try_call!(raw::git_checkout_init_options(
2119 &mut raw_opts,
2120 raw::GIT_CHECKOUT_OPTIONS_VERSION
2121 ));
2122 if let Some(c) = opts {
2123 c.configure(&mut raw_opts);
2124 }
2125
2126 try_call!(raw::git_checkout_tree(self.raw, &*treeish.raw(), &raw_opts));
2127 }
2128 Ok(())
2129 }
2130
2131 pub fn merge(
2140 &self,
2141 annotated_commits: &[&AnnotatedCommit<'_>],
2142 merge_opts: Option<&mut MergeOptions>,
2143 checkout_opts: Option<&mut CheckoutBuilder<'_>>,
2144 ) -> Result<(), Error> {
2145 unsafe {
2146 let mut raw_checkout_opts = mem::zeroed();
2147 try_call!(raw::git_checkout_init_options(
2148 &mut raw_checkout_opts,
2149 raw::GIT_CHECKOUT_OPTIONS_VERSION
2150 ));
2151 if let Some(c) = checkout_opts {
2152 c.configure(&mut raw_checkout_opts);
2153 }
2154
2155 let mut commit_ptrs = annotated_commits
2156 .iter()
2157 .map(|c| c.raw() as *const raw::git_annotated_commit)
2158 .collect::<Vec<_>>();
2159
2160 try_call!(raw::git_merge(
2161 self.raw,
2162 commit_ptrs.as_mut_ptr(),
2163 annotated_commits.len() as size_t,
2164 merge_opts.map(|o| o.raw()).unwrap_or(ptr::null()),
2165 &raw_checkout_opts
2166 ));
2167 }
2168 Ok(())
2169 }
2170
2171 pub fn merge_commits(
2176 &self,
2177 our_commit: &Commit<'_>,
2178 their_commit: &Commit<'_>,
2179 opts: Option<&MergeOptions>,
2180 ) -> Result<Index, Error> {
2181 let mut raw = ptr::null_mut();
2182 unsafe {
2183 try_call!(raw::git_merge_commits(
2184 &mut raw,
2185 self.raw,
2186 our_commit.raw(),
2187 their_commit.raw(),
2188 opts.map(|o| o.raw())
2189 ));
2190 Ok(Binding::from_raw(raw))
2191 }
2192 }
2193
2194 pub fn merge_trees(
2199 &self,
2200 ancestor_tree: &Tree<'_>,
2201 our_tree: &Tree<'_>,
2202 their_tree: &Tree<'_>,
2203 opts: Option<&MergeOptions>,
2204 ) -> Result<Index, Error> {
2205 let mut raw = ptr::null_mut();
2206 unsafe {
2207 try_call!(raw::git_merge_trees(
2208 &mut raw,
2209 self.raw,
2210 ancestor_tree.raw(),
2211 our_tree.raw(),
2212 their_tree.raw(),
2213 opts.map(|o| o.raw())
2214 ));
2215 Ok(Binding::from_raw(raw))
2216 }
2217 }
2218
2219 pub fn cleanup_state(&self) -> Result<(), Error> {
2222 unsafe {
2223 try_call!(raw::git_repository_state_cleanup(self.raw));
2224 }
2225 Ok(())
2226 }
2227
2228 pub fn merge_analysis(
2231 &self,
2232 their_heads: &[&AnnotatedCommit<'_>],
2233 ) -> Result<(MergeAnalysis, MergePreference), Error> {
2234 unsafe {
2235 let mut raw_merge_analysis = 0 as raw::git_merge_analysis_t;
2236 let mut raw_merge_preference = 0 as raw::git_merge_preference_t;
2237 let mut their_heads = their_heads
2238 .iter()
2239 .map(|v| v.raw() as *const _)
2240 .collect::<Vec<_>>();
2241 try_call!(raw::git_merge_analysis(
2242 &mut raw_merge_analysis,
2243 &mut raw_merge_preference,
2244 self.raw,
2245 their_heads.as_mut_ptr() as *mut _,
2246 their_heads.len()
2247 ));
2248 Ok((
2249 MergeAnalysis::from_bits_truncate(raw_merge_analysis as u32),
2250 MergePreference::from_bits_truncate(raw_merge_preference as u32),
2251 ))
2252 }
2253 }
2254
2255 pub fn merge_analysis_for_ref(
2258 &self,
2259 our_ref: &Reference<'_>,
2260 their_heads: &[&AnnotatedCommit<'_>],
2261 ) -> Result<(MergeAnalysis, MergePreference), Error> {
2262 unsafe {
2263 let mut raw_merge_analysis = 0 as raw::git_merge_analysis_t;
2264 let mut raw_merge_preference = 0 as raw::git_merge_preference_t;
2265 let mut their_heads = their_heads
2266 .iter()
2267 .map(|v| v.raw() as *const _)
2268 .collect::<Vec<_>>();
2269 try_call!(raw::git_merge_analysis_for_ref(
2270 &mut raw_merge_analysis,
2271 &mut raw_merge_preference,
2272 self.raw,
2273 our_ref.raw(),
2274 their_heads.as_mut_ptr() as *mut _,
2275 their_heads.len()
2276 ));
2277 Ok((
2278 MergeAnalysis::from_bits_truncate(raw_merge_analysis as u32),
2279 MergePreference::from_bits_truncate(raw_merge_preference as u32),
2280 ))
2281 }
2282 }
2283
2284 pub fn rebase(
2288 &self,
2289 branch: Option<&AnnotatedCommit<'_>>,
2290 upstream: Option<&AnnotatedCommit<'_>>,
2291 onto: Option<&AnnotatedCommit<'_>>,
2292 opts: Option<&mut RebaseOptions<'_>>,
2293 ) -> Result<Rebase<'_>, Error> {
2294 let mut rebase: *mut raw::git_rebase = ptr::null_mut();
2295 unsafe {
2296 try_call!(raw::git_rebase_init(
2297 &mut rebase,
2298 self.raw(),
2299 branch.map(|c| c.raw()),
2300 upstream.map(|c| c.raw()),
2301 onto.map(|c| c.raw()),
2302 opts.map(|o| o.raw()).unwrap_or(ptr::null())
2303 ));
2304
2305 Ok(Rebase::from_raw(rebase))
2306 }
2307 }
2308
2309 pub fn open_rebase(&self, opts: Option<&mut RebaseOptions<'_>>) -> Result<Rebase<'_>, Error> {
2312 let mut rebase: *mut raw::git_rebase = ptr::null_mut();
2313 unsafe {
2314 try_call!(raw::git_rebase_open(
2315 &mut rebase,
2316 self.raw(),
2317 opts.map(|o| o.raw()).unwrap_or(ptr::null())
2318 ));
2319 Ok(Rebase::from_raw(rebase))
2320 }
2321 }
2322
2323 pub fn note(
2329 &self,
2330 author: &Signature<'_>,
2331 committer: &Signature<'_>,
2332 notes_ref: Option<&str>,
2333 oid: Oid,
2334 note: &str,
2335 force: bool,
2336 ) -> Result<Oid, Error> {
2337 let notes_ref = crate::opt_cstr(notes_ref)?;
2338 let note = CString::new(note)?;
2339 let mut ret = raw::git_oid {
2340 id: [0; raw::GIT_OID_RAWSZ],
2341 };
2342 unsafe {
2343 try_call!(raw::git_note_create(
2344 &mut ret,
2345 self.raw,
2346 notes_ref,
2347 author.raw(),
2348 committer.raw(),
2349 oid.raw(),
2350 note,
2351 force
2352 ));
2353 Ok(Binding::from_raw(&ret as *const _))
2354 }
2355 }
2356
2357 pub fn note_default_ref(&self) -> Result<String, Error> {
2359 let ret = Buf::new();
2360 unsafe {
2361 try_call!(raw::git_note_default_ref(ret.raw(), self.raw));
2362 }
2363 Ok(str::from_utf8(&ret).unwrap().to_string())
2364 }
2365
2366 pub fn notes(&self, notes_ref: Option<&str>) -> Result<Notes<'_>, Error> {
2375 let notes_ref = crate::opt_cstr(notes_ref)?;
2376 let mut ret = ptr::null_mut();
2377 unsafe {
2378 try_call!(raw::git_note_iterator_new(&mut ret, self.raw, notes_ref));
2379 Ok(Binding::from_raw(ret))
2380 }
2381 }
2382
2383 pub fn find_note(&self, notes_ref: Option<&str>, id: Oid) -> Result<Note<'_>, Error> {
2390 let notes_ref = crate::opt_cstr(notes_ref)?;
2391 let mut ret = ptr::null_mut();
2392 unsafe {
2393 try_call!(raw::git_note_read(&mut ret, self.raw, notes_ref, id.raw()));
2394 Ok(Binding::from_raw(ret))
2395 }
2396 }
2397
2398 pub fn note_delete(
2405 &self,
2406 id: Oid,
2407 notes_ref: Option<&str>,
2408 author: &Signature<'_>,
2409 committer: &Signature<'_>,
2410 ) -> Result<(), Error> {
2411 let notes_ref = crate::opt_cstr(notes_ref)?;
2412 unsafe {
2413 try_call!(raw::git_note_remove(
2414 self.raw,
2415 notes_ref,
2416 author.raw(),
2417 committer.raw(),
2418 id.raw()
2419 ));
2420 Ok(())
2421 }
2422 }
2423
2424 pub fn revwalk(&self) -> Result<Revwalk<'_>, Error> {
2426 let mut raw = ptr::null_mut();
2427 unsafe {
2428 try_call!(raw::git_revwalk_new(&mut raw, self.raw()));
2429 Ok(Binding::from_raw(raw))
2430 }
2431 }
2432
2433 pub fn blame_file(
2435 &self,
2436 path: &Path,
2437 opts: Option<&mut BlameOptions>,
2438 ) -> Result<Blame<'_>, Error> {
2439 let path = path_to_repo_path(path)?;
2440 let mut raw = ptr::null_mut();
2441
2442 unsafe {
2443 try_call!(raw::git_blame_file(
2444 &mut raw,
2445 self.raw(),
2446 path,
2447 opts.map(|s| s.raw())
2448 ));
2449 Ok(Binding::from_raw(raw))
2450 }
2451 }
2452
2453 pub fn merge_base(&self, one: Oid, two: Oid) -> Result<Oid, Error> {
2455 let mut raw = raw::git_oid {
2456 id: [0; raw::GIT_OID_RAWSZ],
2457 };
2458 unsafe {
2459 try_call!(raw::git_merge_base(
2460 &mut raw,
2461 self.raw,
2462 one.raw(),
2463 two.raw()
2464 ));
2465 Ok(Binding::from_raw(&raw as *const _))
2466 }
2467 }
2468
2469 pub fn merge_base_many(&self, oids: &[Oid]) -> Result<Oid, Error> {
2503 let mut raw = raw::git_oid {
2504 id: [0; raw::GIT_OID_RAWSZ],
2505 };
2506
2507 unsafe {
2508 try_call!(raw::git_merge_base_many(
2509 &mut raw,
2510 self.raw,
2511 oids.len() as size_t,
2512 oids.as_ptr() as *const raw::git_oid
2513 ));
2514 Ok(Binding::from_raw(&raw as *const _))
2515 }
2516 }
2517
2518 pub fn merge_base_octopus(&self, oids: &[Oid]) -> Result<Oid, Error> {
2520 let mut raw = raw::git_oid {
2521 id: [0; raw::GIT_OID_RAWSZ],
2522 };
2523
2524 unsafe {
2525 try_call!(raw::git_merge_base_octopus(
2526 &mut raw,
2527 self.raw,
2528 oids.len() as size_t,
2529 oids.as_ptr() as *const raw::git_oid
2530 ));
2531 Ok(Binding::from_raw(&raw as *const _))
2532 }
2533 }
2534
2535 pub fn merge_bases(&self, one: Oid, two: Oid) -> Result<OidArray, Error> {
2537 let mut arr = raw::git_oidarray {
2538 ids: ptr::null_mut(),
2539 count: 0,
2540 };
2541 unsafe {
2542 try_call!(raw::git_merge_bases(
2543 &mut arr,
2544 self.raw,
2545 one.raw(),
2546 two.raw()
2547 ));
2548 Ok(Binding::from_raw(arr))
2549 }
2550 }
2551
2552 pub fn merge_bases_many(&self, oids: &[Oid]) -> Result<OidArray, Error> {
2554 let mut arr = raw::git_oidarray {
2555 ids: ptr::null_mut(),
2556 count: 0,
2557 };
2558 unsafe {
2559 try_call!(raw::git_merge_bases_many(
2560 &mut arr,
2561 self.raw,
2562 oids.len() as size_t,
2563 oids.as_ptr() as *const raw::git_oid
2564 ));
2565 Ok(Binding::from_raw(arr))
2566 }
2567 }
2568
2569 pub fn graph_ahead_behind(&self, local: Oid, upstream: Oid) -> Result<(usize, usize), Error> {
2576 unsafe {
2577 let mut ahead: size_t = 0;
2578 let mut behind: size_t = 0;
2579 try_call!(raw::git_graph_ahead_behind(
2580 &mut ahead,
2581 &mut behind,
2582 self.raw(),
2583 local.raw(),
2584 upstream.raw()
2585 ));
2586 Ok((ahead as usize, behind as usize))
2587 }
2588 }
2589
2590 pub fn graph_descendant_of(&self, commit: Oid, ancestor: Oid) -> Result<bool, Error> {
2595 unsafe {
2596 let rv = try_call!(raw::git_graph_descendant_of(
2597 self.raw(),
2598 commit.raw(),
2599 ancestor.raw()
2600 ));
2601 Ok(rv != 0)
2602 }
2603 }
2604
2605 pub fn reflog(&self, name: &str) -> Result<Reflog, Error> {
2610 let name = CString::new(name)?;
2611 let mut ret = ptr::null_mut();
2612 unsafe {
2613 try_call!(raw::git_reflog_read(&mut ret, self.raw, name));
2614 Ok(Binding::from_raw(ret))
2615 }
2616 }
2617
2618 pub fn reflog_delete(&self, name: &str) -> Result<(), Error> {
2620 let name = CString::new(name)?;
2621 unsafe {
2622 try_call!(raw::git_reflog_delete(self.raw, name));
2623 }
2624 Ok(())
2625 }
2626
2627 pub fn reflog_rename(&self, old_name: &str, new_name: &str) -> Result<(), Error> {
2631 let old_name = CString::new(old_name)?;
2632 let new_name = CString::new(new_name)?;
2633 unsafe {
2634 try_call!(raw::git_reflog_rename(self.raw, old_name, new_name));
2635 }
2636 Ok(())
2637 }
2638
2639 pub fn reference_has_log(&self, name: &str) -> Result<bool, Error> {
2641 let name = CString::new(name)?;
2642 let ret = unsafe { try_call!(raw::git_reference_has_log(self.raw, name)) };
2643 Ok(ret != 0)
2644 }
2645
2646 pub fn reference_ensure_log(&self, name: &str) -> Result<(), Error> {
2648 let name = CString::new(name)?;
2649 unsafe {
2650 try_call!(raw::git_reference_ensure_log(self.raw, name));
2651 }
2652 Ok(())
2653 }
2654
2655 pub fn describe(&self, opts: &DescribeOptions) -> Result<Describe<'_>, Error> {
2661 let mut ret = ptr::null_mut();
2662 unsafe {
2663 try_call!(raw::git_describe_workdir(&mut ret, self.raw, opts.raw()));
2664 Ok(Binding::from_raw(ret))
2665 }
2666 }
2667
2668 pub fn diff_blobs(
2683 &self,
2684 old_blob: Option<&Blob<'_>>,
2685 old_as_path: Option<&str>,
2686 new_blob: Option<&Blob<'_>>,
2687 new_as_path: Option<&str>,
2688 opts: Option<&mut DiffOptions>,
2689 file_cb: Option<&mut FileCb<'_>>,
2690 binary_cb: Option<&mut BinaryCb<'_>>,
2691 hunk_cb: Option<&mut HunkCb<'_>>,
2692 line_cb: Option<&mut LineCb<'_>>,
2693 ) -> Result<(), Error> {
2694 let old_as_path = crate::opt_cstr(old_as_path)?;
2695 let new_as_path = crate::opt_cstr(new_as_path)?;
2696 let mut cbs = DiffCallbacks {
2697 file: file_cb,
2698 binary: binary_cb,
2699 hunk: hunk_cb,
2700 line: line_cb,
2701 };
2702 let ptr = &mut cbs as *mut _;
2703 unsafe {
2704 let file_cb_c: raw::git_diff_file_cb = if cbs.file.is_some() {
2705 Some(file_cb_c)
2706 } else {
2707 None
2708 };
2709 let binary_cb_c: raw::git_diff_binary_cb = if cbs.binary.is_some() {
2710 Some(binary_cb_c)
2711 } else {
2712 None
2713 };
2714 let hunk_cb_c: raw::git_diff_hunk_cb = if cbs.hunk.is_some() {
2715 Some(hunk_cb_c)
2716 } else {
2717 None
2718 };
2719 let line_cb_c: raw::git_diff_line_cb = if cbs.line.is_some() {
2720 Some(line_cb_c)
2721 } else {
2722 None
2723 };
2724 try_call!(raw::git_diff_blobs(
2725 old_blob.map(|s| s.raw()),
2726 old_as_path,
2727 new_blob.map(|s| s.raw()),
2728 new_as_path,
2729 opts.map(|s| s.raw()),
2730 file_cb_c,
2731 binary_cb_c,
2732 hunk_cb_c,
2733 line_cb_c,
2734 ptr as *mut _
2735 ));
2736 Ok(())
2737 }
2738 }
2739
2740 pub fn diff_tree_to_tree(
2749 &self,
2750 old_tree: Option<&Tree<'_>>,
2751 new_tree: Option<&Tree<'_>>,
2752 opts: Option<&mut DiffOptions>,
2753 ) -> Result<Diff<'_>, Error> {
2754 let mut ret = ptr::null_mut();
2755 unsafe {
2756 try_call!(raw::git_diff_tree_to_tree(
2757 &mut ret,
2758 self.raw(),
2759 old_tree.map(|s| s.raw()),
2760 new_tree.map(|s| s.raw()),
2761 opts.map(|s| s.raw())
2762 ));
2763 Ok(Binding::from_raw(ret))
2764 }
2765 }
2766
2767 pub fn diff_tree_to_index(
2781 &self,
2782 old_tree: Option<&Tree<'_>>,
2783 index: Option<&Index>,
2784 opts: Option<&mut DiffOptions>,
2785 ) -> Result<Diff<'_>, Error> {
2786 let mut ret = ptr::null_mut();
2787 unsafe {
2788 try_call!(raw::git_diff_tree_to_index(
2789 &mut ret,
2790 self.raw(),
2791 old_tree.map(|s| s.raw()),
2792 index.map(|s| s.raw()),
2793 opts.map(|s| s.raw())
2794 ));
2795 Ok(Binding::from_raw(ret))
2796 }
2797 }
2798
2799 pub fn diff_index_to_index(
2804 &self,
2805 old_index: &Index,
2806 new_index: &Index,
2807 opts: Option<&mut DiffOptions>,
2808 ) -> Result<Diff<'_>, Error> {
2809 let mut ret = ptr::null_mut();
2810 unsafe {
2811 try_call!(raw::git_diff_index_to_index(
2812 &mut ret,
2813 self.raw(),
2814 old_index.raw(),
2815 new_index.raw(),
2816 opts.map(|s| s.raw())
2817 ));
2818 Ok(Binding::from_raw(ret))
2819 }
2820 }
2821
2822 pub fn diff_index_to_workdir(
2836 &self,
2837 index: Option<&Index>,
2838 opts: Option<&mut DiffOptions>,
2839 ) -> Result<Diff<'_>, Error> {
2840 let mut ret = ptr::null_mut();
2841 unsafe {
2842 try_call!(raw::git_diff_index_to_workdir(
2843 &mut ret,
2844 self.raw(),
2845 index.map(|s| s.raw()),
2846 opts.map(|s| s.raw())
2847 ));
2848 Ok(Binding::from_raw(ret))
2849 }
2850 }
2851
2852 pub fn diff_tree_to_workdir(
2871 &self,
2872 old_tree: Option<&Tree<'_>>,
2873 opts: Option<&mut DiffOptions>,
2874 ) -> Result<Diff<'_>, Error> {
2875 let mut ret = ptr::null_mut();
2876 unsafe {
2877 try_call!(raw::git_diff_tree_to_workdir(
2878 &mut ret,
2879 self.raw(),
2880 old_tree.map(|s| s.raw()),
2881 opts.map(|s| s.raw())
2882 ));
2883 Ok(Binding::from_raw(ret))
2884 }
2885 }
2886
2887 pub fn diff_tree_to_workdir_with_index(
2894 &self,
2895 old_tree: Option<&Tree<'_>>,
2896 opts: Option<&mut DiffOptions>,
2897 ) -> Result<Diff<'_>, Error> {
2898 let mut ret = ptr::null_mut();
2899 unsafe {
2900 try_call!(raw::git_diff_tree_to_workdir_with_index(
2901 &mut ret,
2902 self.raw(),
2903 old_tree.map(|s| s.raw()),
2904 opts.map(|s| s.raw())
2905 ));
2906 Ok(Binding::from_raw(ret))
2907 }
2908 }
2909
2910 pub fn packbuilder(&self) -> Result<PackBuilder<'_>, Error> {
2912 let mut ret = ptr::null_mut();
2913 unsafe {
2914 try_call!(raw::git_packbuilder_new(&mut ret, self.raw()));
2915 Ok(Binding::from_raw(ret))
2916 }
2917 }
2918
2919 pub fn stash_save(
2921 &mut self,
2922 stasher: &Signature<'_>,
2923 message: &str,
2924 flags: Option<StashFlags>,
2925 ) -> Result<Oid, Error> {
2926 self.stash_save2(stasher, Some(message), flags)
2927 }
2928
2929 pub fn stash_save2(
2932 &mut self,
2933 stasher: &Signature<'_>,
2934 message: Option<&str>,
2935 flags: Option<StashFlags>,
2936 ) -> Result<Oid, Error> {
2937 unsafe {
2938 let mut raw_oid = raw::git_oid {
2939 id: [0; raw::GIT_OID_RAWSZ],
2940 };
2941 let message = crate::opt_cstr(message)?;
2942 let flags = flags.unwrap_or_else(StashFlags::empty);
2943 try_call!(raw::git_stash_save(
2944 &mut raw_oid,
2945 self.raw(),
2946 stasher.raw(),
2947 message,
2948 flags.bits() as c_uint
2949 ));
2950 Ok(Binding::from_raw(&raw_oid as *const _))
2951 }
2952 }
2953
2954 pub fn stash_save_ext(
2956 &mut self,
2957 opts: Option<&mut StashSaveOptions<'_>>,
2958 ) -> Result<Oid, Error> {
2959 unsafe {
2960 let mut raw_oid = raw::git_oid {
2961 id: [0; raw::GIT_OID_RAWSZ],
2962 };
2963 let opts = opts.map(|opts| opts.raw());
2964 try_call!(raw::git_stash_save_with_opts(
2965 &mut raw_oid,
2966 self.raw(),
2967 opts
2968 ));
2969 Ok(Binding::from_raw(&raw_oid as *const _))
2970 }
2971 }
2972
2973 pub fn stash_apply(
2975 &mut self,
2976 index: usize,
2977 opts: Option<&mut StashApplyOptions<'_>>,
2978 ) -> Result<(), Error> {
2979 unsafe {
2980 let opts = opts.map(|opts| opts.raw());
2981 try_call!(raw::git_stash_apply(self.raw(), index, opts));
2982 Ok(())
2983 }
2984 }
2985
2986 pub fn stash_foreach<C>(&mut self, mut callback: C) -> Result<(), Error>
2990 where
2991 C: FnMut(usize, &str, &Oid) -> bool,
2992 {
2993 unsafe {
2994 let mut data = StashCbData {
2995 callback: &mut callback,
2996 };
2997 let cb: raw::git_stash_cb = Some(stash_cb);
2998 try_call!(raw::git_stash_foreach(
2999 self.raw(),
3000 cb,
3001 &mut data as *mut _ as *mut _
3002 ));
3003 Ok(())
3004 }
3005 }
3006
3007 pub fn stash_drop(&mut self, index: usize) -> Result<(), Error> {
3009 unsafe {
3010 try_call!(raw::git_stash_drop(self.raw(), index));
3011 Ok(())
3012 }
3013 }
3014
3015 pub fn stash_pop(
3017 &mut self,
3018 index: usize,
3019 opts: Option<&mut StashApplyOptions<'_>>,
3020 ) -> Result<(), Error> {
3021 unsafe {
3022 let opts = opts.map(|opts| opts.raw());
3023 try_call!(raw::git_stash_pop(self.raw(), index, opts));
3024 Ok(())
3025 }
3026 }
3027
3028 pub fn add_ignore_rule(&self, rules: &str) -> Result<(), Error> {
3032 let rules = CString::new(rules)?;
3033 unsafe {
3034 try_call!(raw::git_ignore_add_rule(self.raw, rules));
3035 }
3036 Ok(())
3037 }
3038
3039 pub fn clear_ignore_rules(&self) -> Result<(), Error> {
3041 unsafe {
3042 try_call!(raw::git_ignore_clear_internal_rules(self.raw));
3043 }
3044 Ok(())
3045 }
3046
3047 pub fn is_path_ignored<P: AsRef<Path>>(&self, path: P) -> Result<bool, Error> {
3049 let path = util::cstring_to_repo_path(path.as_ref())?;
3050 let mut ignored: c_int = 0;
3051 unsafe {
3052 try_call!(raw::git_ignore_path_is_ignored(
3053 &mut ignored,
3054 self.raw,
3055 path
3056 ));
3057 }
3058 Ok(ignored == 1)
3059 }
3060
3061 pub fn cherrypick(
3063 &self,
3064 commit: &Commit<'_>,
3065 options: Option<&mut CherrypickOptions<'_>>,
3066 ) -> Result<(), Error> {
3067 let raw_opts = options.map(|o| o.raw());
3068 let ptr_raw_opts = match raw_opts.as_ref() {
3069 Some(v) => v,
3070 None => std::ptr::null(),
3071 };
3072 unsafe {
3073 try_call!(raw::git_cherrypick(self.raw(), commit.raw(), ptr_raw_opts));
3074
3075 Ok(())
3076 }
3077 }
3078
3079 pub fn cherrypick_commit(
3082 &self,
3083 cherrypick_commit: &Commit<'_>,
3084 our_commit: &Commit<'_>,
3085 mainline: u32,
3086 options: Option<&MergeOptions>,
3087 ) -> Result<Index, Error> {
3088 let mut ret = ptr::null_mut();
3089 unsafe {
3090 try_call!(raw::git_cherrypick_commit(
3091 &mut ret,
3092 self.raw(),
3093 cherrypick_commit.raw(),
3094 our_commit.raw(),
3095 mainline,
3096 options.map(|o| o.raw())
3097 ));
3098 Ok(Binding::from_raw(ret))
3099 }
3100 }
3101
3102 pub fn branch_remote_name(&self, refname: &str) -> Result<Buf, Error> {
3104 let refname = CString::new(refname)?;
3105 unsafe {
3106 let buf = Buf::new();
3107 try_call!(raw::git_branch_remote_name(buf.raw(), self.raw, refname));
3108 Ok(buf)
3109 }
3110 }
3111
3112 pub fn branch_upstream_name(&self, refname: &str) -> Result<Buf, Error> {
3115 let refname = CString::new(refname)?;
3116 unsafe {
3117 let buf = Buf::new();
3118 try_call!(raw::git_branch_upstream_name(buf.raw(), self.raw, refname));
3119 Ok(buf)
3120 }
3121 }
3122
3123 pub fn branch_upstream_remote(&self, refname: &str) -> Result<Buf, Error> {
3127 let refname = CString::new(refname)?;
3128 unsafe {
3129 let buf = Buf::new();
3130 try_call!(raw::git_branch_upstream_remote(
3131 buf.raw(),
3132 self.raw,
3133 refname
3134 ));
3135 Ok(buf)
3136 }
3137 }
3138
3139 pub fn apply(
3141 &self,
3142 diff: &Diff<'_>,
3143 location: ApplyLocation,
3144 options: Option<&mut ApplyOptions<'_>>,
3145 ) -> Result<(), Error> {
3146 unsafe {
3147 try_call!(raw::git_apply(
3148 self.raw,
3149 diff.raw(),
3150 location.raw(),
3151 options.map(|s| s.raw()).unwrap_or(ptr::null())
3152 ));
3153
3154 Ok(())
3155 }
3156 }
3157
3158 pub fn apply_to_tree(
3160 &self,
3161 tree: &Tree<'_>,
3162 diff: &Diff<'_>,
3163 options: Option<&mut ApplyOptions<'_>>,
3164 ) -> Result<Index, Error> {
3165 let mut ret = ptr::null_mut();
3166 unsafe {
3167 try_call!(raw::git_apply_to_tree(
3168 &mut ret,
3169 self.raw,
3170 tree.raw(),
3171 diff.raw(),
3172 options.map(|s| s.raw()).unwrap_or(ptr::null())
3173 ));
3174 Ok(Binding::from_raw(ret))
3175 }
3176 }
3177
3178 pub fn revert(
3180 &self,
3181 commit: &Commit<'_>,
3182 options: Option<&mut RevertOptions<'_>>,
3183 ) -> Result<(), Error> {
3184 let raw_opts = options.map(|o| o.raw());
3185 let ptr_raw_opts = match raw_opts.as_ref() {
3186 Some(v) => v,
3187 None => 0 as *const _,
3188 };
3189 unsafe {
3190 try_call!(raw::git_revert(self.raw(), commit.raw(), ptr_raw_opts));
3191 Ok(())
3192 }
3193 }
3194
3195 pub fn revert_commit(
3198 &self,
3199 revert_commit: &Commit<'_>,
3200 our_commit: &Commit<'_>,
3201 mainline: u32,
3202 options: Option<&MergeOptions>,
3203 ) -> Result<Index, Error> {
3204 let mut ret = ptr::null_mut();
3205 unsafe {
3206 try_call!(raw::git_revert_commit(
3207 &mut ret,
3208 self.raw(),
3209 revert_commit.raw(),
3210 our_commit.raw(),
3211 mainline,
3212 options.map(|o| o.raw())
3213 ));
3214 Ok(Binding::from_raw(ret))
3215 }
3216 }
3217
3218 pub fn worktrees(&self) -> Result<StringArray, Error> {
3220 let mut arr = raw::git_strarray {
3221 strings: ptr::null_mut(),
3222 count: 0,
3223 };
3224 unsafe {
3225 try_call!(raw::git_worktree_list(&mut arr, self.raw));
3226 Ok(Binding::from_raw(arr))
3227 }
3228 }
3229
3230 pub fn find_worktree(&self, name: &str) -> Result<Worktree, Error> {
3234 let mut raw = ptr::null_mut();
3235 let raw_name = CString::new(name)?;
3236 unsafe {
3237 try_call!(raw::git_worktree_lookup(&mut raw, self.raw, raw_name));
3238 Ok(Binding::from_raw(raw))
3239 }
3240 }
3241
3242 pub fn worktree<'a>(
3244 &'a self,
3245 name: &str,
3246 path: &Path,
3247 opts: Option<&WorktreeAddOptions<'a>>,
3248 ) -> Result<Worktree, Error> {
3249 let mut raw = ptr::null_mut();
3250 let raw_name = CString::new(name)?;
3251 let raw_path = path.into_c_string()?;
3252
3253 unsafe {
3254 try_call!(raw::git_worktree_add(
3255 &mut raw,
3256 self.raw,
3257 raw_name,
3258 raw_path,
3259 opts.map(|o| o.raw())
3260 ));
3261 Ok(Binding::from_raw(raw))
3262 }
3263 }
3264
3265 pub fn transaction<'a>(&'a self) -> Result<Transaction<'a>, Error> {
3267 let mut raw = ptr::null_mut();
3268 unsafe {
3269 try_call!(raw::git_transaction_new(&mut raw, self.raw));
3270 Ok(Binding::from_raw(raw))
3271 }
3272 }
3273
3274 pub fn mailmap(&self) -> Result<Mailmap, Error> {
3276 let mut ret = ptr::null_mut();
3277 unsafe {
3278 try_call!(raw::git_mailmap_from_repository(&mut ret, self.raw));
3279 Ok(Binding::from_raw(ret))
3280 }
3281 }
3282
3283 pub fn mergehead_foreach<C>(&mut self, mut callback: C) -> Result<(), Error>
3286 where
3287 C: FnMut(&Oid) -> bool,
3288 {
3289 unsafe {
3290 let mut data = MergeheadForeachCbData {
3291 callback: &mut callback,
3292 };
3293 let cb: raw::git_repository_mergehead_foreach_cb = Some(mergehead_foreach_cb);
3294 try_call!(raw::git_repository_mergehead_foreach(
3295 self.raw(),
3296 cb,
3297 &mut data as *mut _ as *mut _
3298 ));
3299 Ok(())
3300 }
3301 }
3302
3303 pub fn fetchhead_foreach<C>(&self, mut callback: C) -> Result<(), Error>
3312 where
3313 C: FnMut(&str, &[u8], &Oid, bool) -> bool,
3314 {
3315 unsafe {
3316 let mut data = FetchheadForeachCbData {
3317 callback: &mut callback,
3318 };
3319 let cb: raw::git_repository_fetchhead_foreach_cb = Some(fetchhead_foreach_cb);
3320 try_call!(raw::git_repository_fetchhead_foreach(
3321 self.raw(),
3322 cb,
3323 &mut data as *mut _ as *mut _
3324 ));
3325 Ok(())
3326 }
3327 }
3328}
3329
3330impl Binding for Repository {
3331 type Raw = *mut raw::git_repository;
3332 unsafe fn from_raw(ptr: *mut raw::git_repository) -> Repository {
3333 Repository { raw: ptr }
3334 }
3335 fn raw(&self) -> *mut raw::git_repository {
3336 self.raw
3337 }
3338}
3339
3340impl Drop for Repository {
3341 fn drop(&mut self) {
3342 unsafe { raw::git_repository_free(self.raw) }
3343 }
3344}
3345
3346impl RepositoryInitOptions {
3347 pub fn new() -> RepositoryInitOptions {
3352 RepositoryInitOptions {
3353 flags: raw::GIT_REPOSITORY_INIT_MKDIR as u32
3354 | raw::GIT_REPOSITORY_INIT_MKPATH as u32
3355 | raw::GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE as u32,
3356 mode: 0,
3357 workdir_path: None,
3358 description: None,
3359 template_path: None,
3360 initial_head: None,
3361 origin_url: None,
3362 }
3363 }
3364
3365 pub fn bare(&mut self, bare: bool) -> &mut RepositoryInitOptions {
3369 self.flag(raw::GIT_REPOSITORY_INIT_BARE, bare)
3370 }
3371
3372 pub fn no_reinit(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3377 self.flag(raw::GIT_REPOSITORY_INIT_NO_REINIT, enabled)
3378 }
3379
3380 pub fn no_dotgit_dir(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3386 self.flag(raw::GIT_REPOSITORY_INIT_NO_DOTGIT_DIR, enabled)
3387 }
3388
3389 pub fn mkdir(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3394 self.flag(raw::GIT_REPOSITORY_INIT_MKDIR, enabled)
3395 }
3396
3397 pub fn mkpath(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3402 self.flag(raw::GIT_REPOSITORY_INIT_MKPATH, enabled)
3403 }
3404
3405 pub fn mode(&mut self, mode: RepositoryInitMode) -> &mut RepositoryInitOptions {
3407 self.mode = mode.bits();
3408 self
3409 }
3410
3411 pub fn external_template(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3419 self.flag(raw::GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE, enabled)
3420 }
3421
3422 fn flag(
3423 &mut self,
3424 flag: raw::git_repository_init_flag_t,
3425 on: bool,
3426 ) -> &mut RepositoryInitOptions {
3427 if on {
3428 self.flags |= flag as u32;
3429 } else {
3430 self.flags &= !(flag as u32);
3431 }
3432 self
3433 }
3434
3435 pub fn workdir_path(&mut self, path: &Path) -> &mut RepositoryInitOptions {
3441 self.workdir_path = Some(path.into_c_string().unwrap());
3443 self
3444 }
3445
3446 pub fn description(&mut self, desc: &str) -> &mut RepositoryInitOptions {
3449 self.description = Some(CString::new(desc).unwrap());
3450 self
3451 }
3452
3453 pub fn template_path(&mut self, path: &Path) -> &mut RepositoryInitOptions {
3459 self.template_path = Some(path.into_c_string().unwrap());
3461 self
3462 }
3463
3464 pub fn initial_head(&mut self, head: &str) -> &mut RepositoryInitOptions {
3470 self.initial_head = Some(CString::new(head).unwrap());
3471 self
3472 }
3473
3474 pub fn origin_url(&mut self, url: &str) -> &mut RepositoryInitOptions {
3477 self.origin_url = Some(CString::new(url).unwrap());
3478 self
3479 }
3480
3481 pub unsafe fn raw(&self) -> raw::git_repository_init_options {
3487 let mut opts = mem::zeroed();
3488 assert_eq!(
3489 raw::git_repository_init_init_options(
3490 &mut opts,
3491 raw::GIT_REPOSITORY_INIT_OPTIONS_VERSION
3492 ),
3493 0
3494 );
3495 opts.flags = self.flags;
3496 opts.mode = self.mode;
3497 opts.workdir_path = crate::call::convert(&self.workdir_path);
3498 opts.description = crate::call::convert(&self.description);
3499 opts.template_path = crate::call::convert(&self.template_path);
3500 opts.initial_head = crate::call::convert(&self.initial_head);
3501 opts.origin_url = crate::call::convert(&self.origin_url);
3502 opts
3503 }
3504}
3505
3506#[cfg(test)]
3507mod tests {
3508 use crate::build::CheckoutBuilder;
3509 use crate::CherrypickOptions;
3510 use crate::{
3511 ObjectType, Oid, Repository, ResetType, Signature, SubmoduleIgnore, SubmoduleUpdate,
3512 };
3513 use std::ffi::OsStr;
3514 use std::fs;
3515 use std::path::Path;
3516 use tempfile::TempDir;
3517
3518 #[test]
3519 fn smoke_init() {
3520 let td = TempDir::new().unwrap();
3521 let path = td.path();
3522
3523 let repo = Repository::init(path).unwrap();
3524 assert!(!repo.is_bare());
3525 }
3526
3527 #[test]
3528 fn smoke_init_bare() {
3529 let td = TempDir::new().unwrap();
3530 let path = td.path();
3531
3532 let repo = Repository::init_bare(path).unwrap();
3533 assert!(repo.is_bare());
3534 assert!(repo.namespace().is_none());
3535 }
3536
3537 #[test]
3538 fn smoke_open() {
3539 let td = TempDir::new().unwrap();
3540 let path = td.path();
3541 Repository::init(td.path()).unwrap();
3542 let repo = Repository::open(path).unwrap();
3543 assert!(!repo.is_bare());
3544 assert!(!repo.is_shallow());
3545 assert!(repo.is_empty().unwrap());
3546 assert_eq!(
3547 crate::test::realpath(&repo.path()).unwrap(),
3548 crate::test::realpath(&td.path().join(".git/")).unwrap()
3549 );
3550 assert_eq!(repo.state(), crate::RepositoryState::Clean);
3551 }
3552
3553 #[test]
3554 fn smoke_open_bare() {
3555 let td = TempDir::new().unwrap();
3556 let path = td.path();
3557 Repository::init_bare(td.path()).unwrap();
3558
3559 let repo = Repository::open(path).unwrap();
3560 assert!(repo.is_bare());
3561 assert_eq!(
3562 crate::test::realpath(&repo.path()).unwrap(),
3563 crate::test::realpath(&td.path().join("")).unwrap()
3564 );
3565 }
3566
3567 #[test]
3568 fn smoke_checkout() {
3569 let (_td, repo) = crate::test::repo_init();
3570 repo.checkout_head(None).unwrap();
3571 }
3572
3573 #[test]
3574 fn smoke_revparse() {
3575 let (_td, repo) = crate::test::repo_init();
3576 let rev = repo.revparse("HEAD").unwrap();
3577 assert!(rev.to().is_none());
3578 let from = rev.from().unwrap();
3579 assert!(rev.from().is_some());
3580
3581 assert_eq!(repo.revparse_single("HEAD").unwrap().id(), from.id());
3582 let obj = repo.find_object(from.id(), None).unwrap().clone();
3583 obj.peel(ObjectType::Any).unwrap();
3584 obj.short_id().unwrap();
3585 repo.reset(&obj, ResetType::Hard, None).unwrap();
3586 let mut opts = CheckoutBuilder::new();
3587 t!(repo.reset(&obj, ResetType::Soft, Some(&mut opts)));
3588 }
3589
3590 #[test]
3591 fn makes_dirs() {
3592 let td = TempDir::new().unwrap();
3593 Repository::init(&td.path().join("a/b/c/d")).unwrap();
3594 }
3595
3596 #[test]
3597 fn smoke_discover() {
3598 let td = TempDir::new().unwrap();
3599 let subdir = td.path().join("subdi");
3600 fs::create_dir(&subdir).unwrap();
3601 Repository::init_bare(td.path()).unwrap();
3602 let repo = Repository::discover(&subdir).unwrap();
3603 assert_eq!(
3604 crate::test::realpath(&repo.path()).unwrap(),
3605 crate::test::realpath(&td.path().join("")).unwrap()
3606 );
3607 }
3608
3609 #[test]
3610 fn smoke_discover_path() {
3611 let td = TempDir::new().unwrap();
3612 let subdir = td.path().join("subdi");
3613 fs::create_dir(&subdir).unwrap();
3614 Repository::init_bare(td.path()).unwrap();
3615 let path = Repository::discover_path(&subdir, &[] as &[&OsStr]).unwrap();
3616 assert_eq!(
3617 crate::test::realpath(&path).unwrap(),
3618 crate::test::realpath(&td.path().join("")).unwrap()
3619 );
3620 }
3621
3622 #[test]
3623 fn smoke_discover_path_ceiling_dir() {
3624 let td = TempDir::new().unwrap();
3625 let subdir = td.path().join("subdi");
3626 fs::create_dir(&subdir).unwrap();
3627 let ceilingdir = subdir.join("ceiling");
3628 fs::create_dir(&ceilingdir).unwrap();
3629 let testdir = ceilingdir.join("testdi");
3630 fs::create_dir(&testdir).unwrap();
3631 Repository::init_bare(td.path()).unwrap();
3632 let path = Repository::discover_path(&testdir, &[ceilingdir.as_os_str()]);
3633
3634 assert!(path.is_err());
3635 }
3636
3637 #[test]
3638 fn smoke_open_ext() {
3639 let td = TempDir::new().unwrap();
3640 let subdir = td.path().join("subdir");
3641 fs::create_dir(&subdir).unwrap();
3642 Repository::init(td.path()).unwrap();
3643
3644 let repo = Repository::open_ext(
3645 &subdir,
3646 crate::RepositoryOpenFlags::empty(),
3647 &[] as &[&OsStr],
3648 )
3649 .unwrap();
3650 assert!(!repo.is_bare());
3651 assert_eq!(
3652 crate::test::realpath(&repo.path()).unwrap(),
3653 crate::test::realpath(&td.path().join(".git")).unwrap()
3654 );
3655
3656 let repo =
3657 Repository::open_ext(&subdir, crate::RepositoryOpenFlags::BARE, &[] as &[&OsStr])
3658 .unwrap();
3659 assert!(repo.is_bare());
3660 assert_eq!(
3661 crate::test::realpath(&repo.path()).unwrap(),
3662 crate::test::realpath(&td.path().join(".git")).unwrap()
3663 );
3664
3665 let err = Repository::open_ext(
3666 &subdir,
3667 crate::RepositoryOpenFlags::NO_SEARCH,
3668 &[] as &[&OsStr],
3669 )
3670 .err()
3671 .unwrap();
3672 assert_eq!(err.code(), crate::ErrorCode::NotFound);
3673
3674 assert!(
3675 Repository::open_ext(&subdir, crate::RepositoryOpenFlags::empty(), &[&subdir]).is_ok()
3676 );
3677 }
3678
3679 fn graph_repo_init() -> (TempDir, Repository) {
3680 let (_td, repo) = crate::test::repo_init();
3681 {
3682 let head = repo.head().unwrap().target().unwrap();
3683 let head = repo.find_commit(head).unwrap();
3684
3685 let mut index = repo.index().unwrap();
3686 let id = index.write_tree().unwrap();
3687
3688 let tree = repo.find_tree(id).unwrap();
3689 let sig = repo.signature().unwrap();
3690 repo.commit(Some("HEAD"), &sig, &sig, "second", &tree, &[&head])
3691 .unwrap();
3692 }
3693 (_td, repo)
3694 }
3695
3696 #[test]
3697 fn smoke_graph_ahead_behind() {
3698 let (_td, repo) = graph_repo_init();
3699 let head = repo.head().unwrap().target().unwrap();
3700 let head = repo.find_commit(head).unwrap();
3701 let head_id = head.id();
3702 let head_parent_id = head.parent(0).unwrap().id();
3703 let (ahead, behind) = repo.graph_ahead_behind(head_id, head_parent_id).unwrap();
3704 assert_eq!(ahead, 1);
3705 assert_eq!(behind, 0);
3706 let (ahead, behind) = repo.graph_ahead_behind(head_parent_id, head_id).unwrap();
3707 assert_eq!(ahead, 0);
3708 assert_eq!(behind, 1);
3709 }
3710
3711 #[test]
3712 fn smoke_graph_descendant_of() {
3713 let (_td, repo) = graph_repo_init();
3714 let head = repo.head().unwrap().target().unwrap();
3715 let head = repo.find_commit(head).unwrap();
3716 let head_id = head.id();
3717 let head_parent_id = head.parent(0).unwrap().id();
3718 assert!(repo.graph_descendant_of(head_id, head_parent_id).unwrap());
3719 assert!(!repo.graph_descendant_of(head_parent_id, head_id).unwrap());
3720 }
3721
3722 #[test]
3723 fn smoke_reference_has_log_ensure_log() {
3724 let (_td, repo) = crate::test::repo_init();
3725
3726 assert_eq!(repo.reference_has_log("HEAD").unwrap(), true);
3727 assert_eq!(repo.reference_has_log("refs/heads/main").unwrap(), true);
3728 assert_eq!(repo.reference_has_log("NOT_HEAD").unwrap(), false);
3729 let main_oid = repo.revparse_single("main").unwrap().id();
3730 assert!(repo
3731 .reference("NOT_HEAD", main_oid, false, "creating a new branch")
3732 .is_ok());
3733 assert_eq!(repo.reference_has_log("NOT_HEAD").unwrap(), false);
3734 assert!(repo.reference_ensure_log("NOT_HEAD").is_ok());
3735 assert_eq!(repo.reference_has_log("NOT_HEAD").unwrap(), true);
3736 }
3737
3738 #[test]
3739 fn smoke_set_head() {
3740 let (_td, repo) = crate::test::repo_init();
3741
3742 assert!(repo.set_head("refs/heads/does-not-exist").is_ok());
3743 assert!(repo.head().is_err());
3744
3745 assert!(repo.set_head("refs/heads/main").is_ok());
3746 assert!(repo.head().is_ok());
3747
3748 assert!(repo.set_head("*").is_err());
3749 }
3750
3751 #[test]
3752 fn smoke_set_head_bytes() {
3753 let (_td, repo) = crate::test::repo_init();
3754
3755 assert!(repo.set_head_bytes(b"refs/heads/does-not-exist").is_ok());
3756 assert!(repo.head().is_err());
3757
3758 assert!(repo.set_head_bytes(b"refs/heads/main").is_ok());
3759 assert!(repo.head().is_ok());
3760
3761 assert!(repo.set_head_bytes(b"*").is_err());
3762 }
3763
3764 #[test]
3765 fn smoke_set_head_detached() {
3766 let (_td, repo) = crate::test::repo_init();
3767
3768 let void_oid = Oid::from_bytes(b"00000000000000000000").unwrap();
3769 assert!(repo.set_head_detached(void_oid).is_err());
3770
3771 let main_oid = repo.revparse_single("main").unwrap().id();
3772 assert!(repo.set_head_detached(main_oid).is_ok());
3773 assert_eq!(repo.head().unwrap().target().unwrap(), main_oid);
3774 }
3775
3776 #[test]
3777 fn smoke_find_object_by_prefix() {
3778 let (_td, repo) = crate::test::repo_init();
3779 let head = repo.head().unwrap().target().unwrap();
3780 let head = repo.find_commit(head).unwrap();
3781 let head_id = head.id();
3782 let head_prefix = &head_id.to_string()[..7];
3783 let obj = repo.find_object_by_prefix(head_prefix, None).unwrap();
3784 assert_eq!(obj.id(), head_id);
3785 }
3786
3787 #[test]
3792 fn smoke_merge_base() {
3793 let (_td, repo) = graph_repo_init();
3794 let sig = repo.signature().unwrap();
3795
3796 let oid1 = repo.head().unwrap().target().unwrap();
3798 let commit1 = repo.find_commit(oid1).unwrap();
3799 println!("created oid1 {:?}", oid1);
3800
3801 repo.branch("branch_a", &commit1, true).unwrap();
3802 repo.branch("branch_b", &commit1, true).unwrap();
3803 repo.branch("branch_c", &commit1, true).unwrap();
3804
3805 let mut index = repo.index().unwrap();
3807 let p = Path::new(repo.workdir().unwrap()).join("file_a");
3808 println!("using path {:?}", p);
3809 fs::File::create(&p).unwrap();
3810 index.add_path(Path::new("file_a")).unwrap();
3811 let id_a = index.write_tree().unwrap();
3812 let tree_a = repo.find_tree(id_a).unwrap();
3813 let oid2 = repo
3814 .commit(
3815 Some("refs/heads/branch_a"),
3816 &sig,
3817 &sig,
3818 "commit 2",
3819 &tree_a,
3820 &[&commit1],
3821 )
3822 .unwrap();
3823 repo.find_commit(oid2).unwrap();
3824 println!("created oid2 {:?}", oid2);
3825
3826 t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
3827
3828 let mut index = repo.index().unwrap();
3830 let p = Path::new(repo.workdir().unwrap()).join("file_b");
3831 fs::File::create(&p).unwrap();
3832 index.add_path(Path::new("file_b")).unwrap();
3833 let id_b = index.write_tree().unwrap();
3834 let tree_b = repo.find_tree(id_b).unwrap();
3835 let oid3 = repo
3836 .commit(
3837 Some("refs/heads/branch_b"),
3838 &sig,
3839 &sig,
3840 "commit 3",
3841 &tree_b,
3842 &[&commit1],
3843 )
3844 .unwrap();
3845 repo.find_commit(oid3).unwrap();
3846 println!("created oid3 {:?}", oid3);
3847
3848 t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
3849
3850 let mut index = repo.index().unwrap();
3852 let p = Path::new(repo.workdir().unwrap()).join("file_c");
3853 fs::File::create(&p).unwrap();
3854 index.add_path(Path::new("file_c")).unwrap();
3855 let id_c = index.write_tree().unwrap();
3856 let tree_c = repo.find_tree(id_c).unwrap();
3857 let oid4 = repo
3858 .commit(
3859 Some("refs/heads/branch_c"),
3860 &sig,
3861 &sig,
3862 "commit 3",
3863 &tree_c,
3864 &[&commit1],
3865 )
3866 .unwrap();
3867 repo.find_commit(oid4).unwrap();
3868 println!("created oid4 {:?}", oid4);
3869
3870 let merge_base = repo.merge_base(oid2, oid3).unwrap();
3872 assert_eq!(merge_base, oid1);
3873
3874 let merge_base = repo.merge_base_many(&[oid2, oid3, oid4]).unwrap();
3876 assert_eq!(merge_base, oid1);
3877
3878 let merge_base = repo.merge_base_octopus(&[oid2, oid3, oid4]).unwrap();
3880 assert_eq!(merge_base, oid1);
3881 }
3882
3883 #[test]
3889 fn smoke_merge_bases() {
3890 let (_td, repo) = graph_repo_init();
3891 let sig = repo.signature().unwrap();
3892
3893 let oid1 = repo.head().unwrap().target().unwrap();
3895 let commit1 = repo.find_commit(oid1).unwrap();
3896 println!("created oid1 {:?}", oid1);
3897
3898 repo.branch("branch_a", &commit1, true).unwrap();
3899 repo.branch("branch_b", &commit1, true).unwrap();
3900
3901 let mut index = repo.index().unwrap();
3903 let p = Path::new(repo.workdir().unwrap()).join("file_a");
3904 println!("using path {:?}", p);
3905 fs::File::create(&p).unwrap();
3906 index.add_path(Path::new("file_a")).unwrap();
3907 let id_a = index.write_tree().unwrap();
3908 let tree_a = repo.find_tree(id_a).unwrap();
3909 let oid2 = repo
3910 .commit(
3911 Some("refs/heads/branch_a"),
3912 &sig,
3913 &sig,
3914 "commit 2",
3915 &tree_a,
3916 &[&commit1],
3917 )
3918 .unwrap();
3919 let commit2 = repo.find_commit(oid2).unwrap();
3920 println!("created oid2 {:?}", oid2);
3921
3922 t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
3923
3924 let mut index = repo.index().unwrap();
3926 let p = Path::new(repo.workdir().unwrap()).join("file_b");
3927 fs::File::create(&p).unwrap();
3928 index.add_path(Path::new("file_b")).unwrap();
3929 let id_b = index.write_tree().unwrap();
3930 let tree_b = repo.find_tree(id_b).unwrap();
3931 let oid3 = repo
3932 .commit(
3933 Some("refs/heads/branch_b"),
3934 &sig,
3935 &sig,
3936 "commit 3",
3937 &tree_b,
3938 &[&commit1],
3939 )
3940 .unwrap();
3941 let commit3 = repo.find_commit(oid3).unwrap();
3942 println!("created oid3 {:?}", oid3);
3943
3944 repo.set_head("refs/heads/branch_a").unwrap();
3947 repo.checkout_head(None).unwrap();
3948 let oid4 = repo
3949 .commit(
3950 Some("refs/heads/branch_a"),
3951 &sig,
3952 &sig,
3953 "commit 4",
3954 &tree_a,
3955 &[&commit2, &commit3],
3956 )
3957 .unwrap();
3958 println!("created oid4 {:?}", oid4);
3960
3961 repo.set_head("refs/heads/branch_b").unwrap();
3964 repo.checkout_head(None).unwrap();
3965 let oid5 = repo
3966 .commit(
3967 Some("refs/heads/branch_b"),
3968 &sig,
3969 &sig,
3970 "commit 5",
3971 &tree_a,
3972 &[&commit3, &commit2],
3973 )
3974 .unwrap();
3975 println!("created oid5 {:?}", oid5);
3977
3978 let merge_bases = repo.merge_bases(oid4, oid5).unwrap();
3980 let mut found_oid2 = false;
3981 let mut found_oid3 = false;
3982 for mg in merge_bases.iter() {
3983 println!("found merge base {:?}", mg);
3984 if mg == &oid2 {
3985 found_oid2 = true;
3986 } else if mg == &oid3 {
3987 found_oid3 = true;
3988 } else {
3989 assert!(false);
3990 }
3991 }
3992 assert!(found_oid2);
3993 assert!(found_oid3);
3994 assert_eq!(merge_bases.len(), 2);
3995
3996 let merge_bases = repo.merge_bases_many(&[oid4, oid5]).unwrap();
3998 let mut found_oid2 = false;
3999 let mut found_oid3 = false;
4000 for mg in merge_bases.iter() {
4001 println!("found merge base {:?}", mg);
4002 if mg == &oid2 {
4003 found_oid2 = true;
4004 } else if mg == &oid3 {
4005 found_oid3 = true;
4006 } else {
4007 assert!(false);
4008 }
4009 }
4010 assert!(found_oid2);
4011 assert!(found_oid3);
4012 assert_eq!(merge_bases.len(), 2);
4013 }
4014
4015 #[test]
4016 fn smoke_revparse_ext() {
4017 let (_td, repo) = graph_repo_init();
4018
4019 {
4020 let short_refname = "main";
4021 let expected_refname = "refs/heads/main";
4022 let (obj, reference) = repo.revparse_ext(short_refname).unwrap();
4023 let expected_obj = repo.revparse_single(expected_refname).unwrap();
4024 assert_eq!(obj.id(), expected_obj.id());
4025 assert_eq!(reference.unwrap().name().unwrap(), expected_refname);
4026 }
4027 {
4028 let missing_refname = "refs/heads/does-not-exist";
4029 assert!(repo.revparse_ext(missing_refname).is_err());
4030 }
4031 {
4032 let (_obj, reference) = repo.revparse_ext("HEAD^").unwrap();
4033 assert!(reference.is_none());
4034 }
4035 }
4036
4037 #[test]
4038 fn smoke_is_path_ignored() {
4039 let (_td, repo) = graph_repo_init();
4040
4041 assert!(!repo.is_path_ignored(Path::new("foo")).unwrap());
4042
4043 let _ = repo.add_ignore_rule("/foo");
4044 assert!(repo.is_path_ignored(Path::new("foo")).unwrap());
4045 if cfg!(windows) {
4046 assert!(repo.is_path_ignored(Path::new("foo\\thing")).unwrap());
4047 }
4048
4049 let _ = repo.clear_ignore_rules();
4050 assert!(!repo.is_path_ignored(Path::new("foo")).unwrap());
4051 if cfg!(windows) {
4052 assert!(!repo.is_path_ignored(Path::new("foo\\thing")).unwrap());
4053 }
4054 }
4055
4056 #[test]
4057 fn smoke_cherrypick() {
4058 let (_td, repo) = crate::test::repo_init();
4059 let sig = repo.signature().unwrap();
4060
4061 let oid1 = repo.head().unwrap().target().unwrap();
4062 let commit1 = repo.find_commit(oid1).unwrap();
4063
4064 repo.branch("branch_a", &commit1, true).unwrap();
4065
4066 let mut index = repo.index().unwrap();
4068 let p1 = Path::new(repo.workdir().unwrap()).join("file_c");
4069 fs::File::create(&p1).unwrap();
4070 index.add_path(Path::new("file_c")).unwrap();
4071 let id = index.write_tree().unwrap();
4072 let tree_c = repo.find_tree(id).unwrap();
4073 let oid2 = repo
4074 .commit(
4075 Some("refs/heads/branch_a"),
4076 &sig,
4077 &sig,
4078 "commit 2",
4079 &tree_c,
4080 &[&commit1],
4081 )
4082 .unwrap();
4083 let commit2 = repo.find_commit(oid2).unwrap();
4084 println!("created oid2 {:?}", oid2);
4085 assert!(p1.exists());
4086
4087 let mut index = repo.index().unwrap();
4088 let p2 = Path::new(repo.workdir().unwrap()).join("file_d");
4089 fs::File::create(&p2).unwrap();
4090 index.add_path(Path::new("file_d")).unwrap();
4091 let id = index.write_tree().unwrap();
4092 let tree_d = repo.find_tree(id).unwrap();
4093 let oid3 = repo
4094 .commit(
4095 Some("refs/heads/branch_a"),
4096 &sig,
4097 &sig,
4098 "commit 3",
4099 &tree_d,
4100 &[&commit2],
4101 )
4102 .unwrap();
4103 let commit3 = repo.find_commit(oid3).unwrap();
4104 println!("created oid3 {:?}", oid3);
4105 assert!(p1.exists());
4106 assert!(p2.exists());
4107
4108 repo.reset(commit1.as_object(), ResetType::Hard, None)
4110 .unwrap();
4111 let mut cherrypick_opts = CherrypickOptions::new();
4112 repo.cherrypick(&commit3, Some(&mut cherrypick_opts))
4113 .unwrap();
4114 let id = repo.index().unwrap().write_tree().unwrap();
4115 let tree_d = repo.find_tree(id).unwrap();
4116 let oid4 = repo
4117 .commit(Some("HEAD"), &sig, &sig, "commit 4", &tree_d, &[&commit1])
4118 .unwrap();
4119 let commit4 = repo.find_commit(oid4).unwrap();
4120 assert_eq!(commit4.parent(0).unwrap().id(), commit1.id());
4122 assert!(!p1.exists());
4123 assert!(p2.exists());
4124 }
4125
4126 #[test]
4127 fn smoke_revert() {
4128 let (_td, repo) = crate::test::repo_init();
4129 let foo_file = Path::new(repo.workdir().unwrap()).join("foo");
4130 assert!(!foo_file.exists());
4131
4132 let (oid1, _id) = crate::test::commit(&repo);
4133 let commit1 = repo.find_commit(oid1).unwrap();
4134 t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
4135 assert!(foo_file.exists());
4136
4137 repo.revert(&commit1, None).unwrap();
4138 let id = repo.index().unwrap().write_tree().unwrap();
4139 let tree2 = repo.find_tree(id).unwrap();
4140 let sig = repo.signature().unwrap();
4141 repo.commit(Some("HEAD"), &sig, &sig, "commit 1", &tree2, &[&commit1])
4142 .unwrap();
4143 assert!(!foo_file.exists());
4145
4146 let oid2 = repo.head().unwrap().target().unwrap();
4147 let commit2 = repo.find_commit(oid2).unwrap();
4148 repo.revert(&commit2, None).unwrap();
4149 let id = repo.index().unwrap().write_tree().unwrap();
4150 let tree3 = repo.find_tree(id).unwrap();
4151 repo.commit(Some("HEAD"), &sig, &sig, "commit 2", &tree3, &[&commit2])
4152 .unwrap();
4153 assert!(foo_file.exists());
4155 }
4156
4157 #[test]
4158 fn smoke_config_write_and_read() {
4159 let (td, repo) = crate::test::repo_init();
4160
4161 let mut config = repo.config().unwrap();
4162
4163 config.set_bool("commit.gpgsign", false).unwrap();
4164
4165 let c = fs::read_to_string(td.path().join(".git").join("config")).unwrap();
4166
4167 assert!(c.contains("[commit]"));
4168 assert!(c.contains("gpgsign = false"));
4169
4170 let config = repo.config().unwrap();
4171
4172 assert!(!config.get_bool("commit.gpgsign").unwrap());
4173 }
4174
4175 #[test]
4176 fn smoke_merge_analysis_for_ref() -> Result<(), crate::Error> {
4177 let (_td, repo) = graph_repo_init();
4178
4179 let head_commit = repo.head()?.peel_to_commit()?;
4187 repo.set_head_detached(head_commit.id())?;
4188
4189 let their_branch = repo.branch("their-branch", &head_commit, false)?;
4191
4192 let mut parents_iter = head_commit.parents();
4194 let parent = parents_iter.next().unwrap();
4195 assert!(parents_iter.next().is_none());
4196
4197 let main = repo.branch("main", &parent, true)?;
4198
4199 repo.set_head(main.get().name().expect("should be utf-8"))?;
4201
4202 let (merge_analysis, _merge_preference) = repo.merge_analysis_for_ref(
4203 main.get(),
4204 &[&repo.reference_to_annotated_commit(their_branch.get())?],
4205 )?;
4206
4207 assert!(merge_analysis.contains(crate::MergeAnalysis::ANALYSIS_FASTFORWARD));
4208
4209 Ok(())
4210 }
4211
4212 #[test]
4213 fn smoke_submodule_set() -> Result<(), crate::Error> {
4214 let (td1, _repo) = crate::test::repo_init();
4215 let (td2, mut repo2) = crate::test::repo_init();
4216 let url = crate::test::path2url(td1.path());
4217 let name = "bar";
4218 {
4219 let mut s = repo2.submodule(&url, Path::new(name), true)?;
4220 fs::remove_dir_all(td2.path().join("bar")).unwrap();
4221 Repository::clone(&url, td2.path().join("bar"))?;
4222 s.add_to_index(false)?;
4223 s.add_finalize()?;
4224 }
4225
4226 repo2.submodule_set_update(name, SubmoduleUpdate::None)?;
4228 assert!(matches!(
4229 repo2.find_submodule(name)?.update_strategy(),
4230 SubmoduleUpdate::None
4231 ));
4232 repo2.submodule_set_update(name, SubmoduleUpdate::Rebase)?;
4233 assert!(matches!(
4234 repo2.find_submodule(name)?.update_strategy(),
4235 SubmoduleUpdate::Rebase
4236 ));
4237
4238 repo2.submodule_set_ignore(name, SubmoduleIgnore::Untracked)?;
4240 assert!(matches!(
4241 repo2.find_submodule(name)?.ignore_rule(),
4242 SubmoduleIgnore::Untracked
4243 ));
4244 repo2.submodule_set_ignore(name, SubmoduleIgnore::Dirty)?;
4245 assert!(matches!(
4246 repo2.find_submodule(name)?.ignore_rule(),
4247 SubmoduleIgnore::Dirty
4248 ));
4249
4250 repo2.submodule_set_url(name, "fake-url")?;
4252 assert_eq!(repo2.find_submodule(name)?.url(), Some("fake-url"));
4253
4254 repo2.submodule_set_branch(name, "fake-branch")?;
4256 assert_eq!(repo2.find_submodule(name)?.branch(), Some("fake-branch"));
4257
4258 Ok(())
4259 }
4260
4261 #[test]
4262 fn smoke_mailmap_from_repository() {
4263 let (_td, repo) = crate::test::repo_init();
4264
4265 let commit = {
4266 let head = t!(repo.head()).target().unwrap();
4267 t!(repo.find_commit(head))
4268 };
4269
4270 let author = commit.author();
4272 let committer = commit.committer();
4273 assert_eq!(author.name(), Some("name"));
4274 assert_eq!(author.email(), Some("email"));
4275 assert_eq!(committer.name(), Some("name"));
4276 assert_eq!(committer.email(), Some("email"));
4277
4278 let mailmap = t!(repo.mailmap());
4280 let mailmapped_author = t!(commit.author_with_mailmap(&mailmap));
4281 let mailmapped_committer = t!(commit.committer_with_mailmap(&mailmap));
4282 assert_eq!(mailmapped_author.name(), author.name());
4283 assert_eq!(mailmapped_author.email(), author.email());
4284 assert_eq!(mailmapped_committer.name(), committer.name());
4285 assert_eq!(mailmapped_committer.email(), committer.email());
4286
4287 let commit = {
4288 let mailmap_file = Path::new(".mailmap");
4293 let p = Path::new(repo.workdir().unwrap()).join(&mailmap_file);
4294 t!(fs::write(
4295 p,
4296 r#"
4297Author Name <author.proper@email> name <email>
4298Committer Name <committer.proper@email> <committer@email>"#,
4299 ));
4300 let mut index = t!(repo.index());
4301 t!(index.add_path(&mailmap_file));
4302 let id_mailmap = t!(index.write_tree());
4303 let tree_mailmap = t!(repo.find_tree(id_mailmap));
4304
4305 let head = t!(repo.commit(
4306 Some("HEAD"),
4307 &author,
4308 t!(&Signature::now("committer", "committer@email")),
4309 "Add mailmap",
4310 &tree_mailmap,
4311 &[&commit],
4312 ));
4313 t!(repo.find_commit(head))
4314 };
4315
4316 let author = commit.author();
4319 let committer = commit.committer();
4320 assert_ne!(author.name(), committer.name());
4321 assert_ne!(author.email(), committer.email());
4322 assert_eq!(author.name(), Some("name"));
4323 assert_eq!(author.email(), Some("email"));
4324 assert_eq!(committer.name(), Some("committer"));
4325 assert_eq!(committer.email(), Some("committer@email"));
4326
4327 let mailmap = t!(repo.mailmap());
4329 let mailmapped_author = t!(commit.author_with_mailmap(&mailmap));
4330 let mailmapped_committer = t!(commit.committer_with_mailmap(&mailmap));
4331
4332 let mm_resolve_author = t!(mailmap.resolve_signature(&author));
4333 let mm_resolve_committer = t!(mailmap.resolve_signature(&committer));
4334
4335 drop(author);
4337 drop(committer);
4338 drop(commit);
4339
4340 assert_eq!(mailmapped_author.name(), Some("Author Name"));
4342 assert_eq!(mailmapped_author.email(), Some("author.proper@email"));
4343 assert_eq!(mailmapped_committer.name(), Some("Committer Name"));
4344 assert_eq!(mailmapped_committer.email(), Some("committer.proper@email"));
4345
4346 assert_eq!(mm_resolve_author.email(), mailmapped_author.email());
4348 assert_eq!(mm_resolve_committer.email(), mailmapped_committer.email());
4349 }
4350
4351 #[test]
4352 fn smoke_find_tag_by_prefix() {
4353 let (_td, repo) = crate::test::repo_init();
4354 let head = repo.head().unwrap();
4355 let tag_oid = repo
4356 .tag(
4357 "tag",
4358 &repo
4359 .find_object(head.peel_to_commit().unwrap().id(), None)
4360 .unwrap(),
4361 &repo.signature().unwrap(),
4362 "message",
4363 false,
4364 )
4365 .unwrap();
4366 let tag = repo.find_tag(tag_oid).unwrap();
4367 let found_tag = repo
4368 .find_tag_by_prefix(&tag.id().to_string()[0..7])
4369 .unwrap();
4370 assert_eq!(tag.id(), found_tag.id());
4371 }
4372
4373 #[test]
4374 fn smoke_commondir() {
4375 let (td, repo) = crate::test::repo_init();
4376 assert_eq!(
4377 crate::test::realpath(repo.path()).unwrap(),
4378 crate::test::realpath(repo.commondir()).unwrap()
4379 );
4380
4381 let worktree = repo
4382 .worktree("test", &td.path().join("worktree"), None)
4383 .unwrap();
4384 let worktree_repo = Repository::open_from_worktree(&worktree).unwrap();
4385 assert_eq!(
4386 crate::test::realpath(repo.path()).unwrap(),
4387 crate::test::realpath(worktree_repo.commondir()).unwrap()
4388 );
4389 }
4390}