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, MergeFileOptions, MergeFileResult, MergeOptions,
28 MergePreference, SubmoduleIgnore, SubmoduleStatus, SubmoduleUpdate,
29};
30use crate::{ApplyLocation, ApplyOptions, Rebase, RebaseOptions};
31use crate::{Blame, BlameOptions, Reference, References, ResetType, Signature, Submodule};
32use crate::{
33 Blob, BlobWriter, Branch, BranchType, Branches, Commit, Config, Index, IndexEntry, Oid, Tree,
34};
35use crate::{Describe, IntoCString, Reflog, RepositoryInitMode, RevparseMode};
36use crate::{DescribeOptions, Diff, DiffOptions, Odb, PackBuilder, TreeBuilder};
37use crate::{Note, Notes, ObjectType, Revwalk, Status, StatusOptions, Statuses, Tag, Transaction};
38
39type MergeheadForeachCb<'a> = dyn FnMut(&Oid) -> bool + 'a;
40type FetchheadForeachCb<'a> = dyn FnMut(&str, &[u8], &Oid, bool) -> bool + 'a;
41
42struct FetchheadForeachCbData<'a> {
43 callback: &'a mut FetchheadForeachCb<'a>,
44}
45
46struct MergeheadForeachCbData<'a> {
47 callback: &'a mut MergeheadForeachCb<'a>,
48}
49
50extern "C" fn mergehead_foreach_cb(oid: *const raw::git_oid, payload: *mut c_void) -> c_int {
51 panic::wrap(|| unsafe {
52 let data = &mut *(payload as *mut MergeheadForeachCbData<'_>);
53 let res = {
54 let callback = &mut data.callback;
55 callback(&Binding::from_raw(oid))
56 };
57
58 if res {
59 0
60 } else {
61 1
62 }
63 })
64 .unwrap_or(1)
65}
66
67extern "C" fn fetchhead_foreach_cb(
68 ref_name: *const c_char,
69 remote_url: *const c_char,
70 oid: *const raw::git_oid,
71 is_merge: c_uint,
72 payload: *mut c_void,
73) -> c_int {
74 panic::wrap(|| unsafe {
75 let data = &mut *(payload as *mut FetchheadForeachCbData<'_>);
76 let res = {
77 let callback = &mut data.callback;
78
79 assert!(!ref_name.is_null());
80 assert!(!remote_url.is_null());
81 assert!(!oid.is_null());
82
83 let ref_name = str::from_utf8(CStr::from_ptr(ref_name).to_bytes()).unwrap();
84 let remote_url = CStr::from_ptr(remote_url).to_bytes();
85 let oid = Binding::from_raw(oid);
86 let is_merge = is_merge == 1;
87
88 callback(&ref_name, remote_url, &oid, is_merge)
89 };
90
91 if res {
92 0
93 } else {
94 1
95 }
96 })
97 .unwrap_or(1)
98}
99
100pub struct Repository {
110 raw: *mut raw::git_repository,
111}
112
113unsafe impl Send for Repository {}
116
117pub struct RepositoryInitOptions {
119 flags: u32,
120 mode: u32,
121 workdir_path: Option<CString>,
122 description: Option<CString>,
123 template_path: Option<CString>,
124 initial_head: Option<CString>,
125 origin_url: Option<CString>,
126}
127
128impl Repository {
129 pub fn open<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
133 crate::init();
134 let path = path.as_ref().into_c_string()?;
136 let mut ret = ptr::null_mut();
137 unsafe {
138 try_call!(raw::git_repository_open(&mut ret, path));
139 Ok(Binding::from_raw(ret))
140 }
141 }
142
143 pub fn open_bare<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
147 crate::init();
148 let path = path.as_ref().into_c_string()?;
150 let mut ret = ptr::null_mut();
151 unsafe {
152 try_call!(raw::git_repository_open_bare(&mut ret, path));
153 Ok(Binding::from_raw(ret))
154 }
155 }
156
157 pub fn open_from_env() -> Result<Repository, Error> {
163 crate::init();
164 let mut ret = ptr::null_mut();
165 let flags = raw::GIT_REPOSITORY_OPEN_FROM_ENV;
166 unsafe {
167 try_call!(raw::git_repository_open_ext(
168 &mut ret,
169 ptr::null(),
170 flags as c_uint,
171 ptr::null()
172 ));
173 Ok(Binding::from_raw(ret))
174 }
175 }
176
177 pub fn open_ext<P, O, I>(
206 path: P,
207 flags: RepositoryOpenFlags,
208 ceiling_dirs: I,
209 ) -> Result<Repository, Error>
210 where
211 P: AsRef<Path>,
212 O: AsRef<OsStr>,
213 I: IntoIterator<Item = O>,
214 {
215 crate::init();
216 let path = path.as_ref().into_c_string()?;
218 let ceiling_dirs_os = env::join_paths(ceiling_dirs)?;
219 let ceiling_dirs = ceiling_dirs_os.into_c_string()?;
220 let mut ret = ptr::null_mut();
221 unsafe {
222 try_call!(raw::git_repository_open_ext(
223 &mut ret,
224 path,
225 flags.bits() as c_uint,
226 ceiling_dirs
227 ));
228 Ok(Binding::from_raw(ret))
229 }
230 }
231
232 pub fn open_from_worktree(worktree: &Worktree) -> Result<Repository, Error> {
234 let mut ret = ptr::null_mut();
235 unsafe {
236 try_call!(raw::git_repository_open_from_worktree(
237 &mut ret,
238 worktree.raw()
239 ));
240 Ok(Binding::from_raw(ret))
241 }
242 }
243
244 pub fn discover<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
249 crate::init();
251 let buf = Buf::new();
252 let path = path.as_ref().into_c_string()?;
254 unsafe {
255 try_call!(raw::git_repository_discover(
256 buf.raw(),
257 path,
258 1,
259 ptr::null()
260 ));
261 }
262 Repository::open(util::bytes2path(&*buf))
263 }
264
265 pub fn discover_path<P: AsRef<Path>, I, O>(path: P, ceiling_dirs: I) -> Result<PathBuf, Error>
270 where
271 O: AsRef<OsStr>,
272 I: IntoIterator<Item = O>,
273 {
274 crate::init();
275 let buf = Buf::new();
276 let path = path.as_ref().into_c_string()?;
278 let ceiling_dirs_os = env::join_paths(ceiling_dirs)?;
279 let ceiling_dirs = ceiling_dirs_os.into_c_string()?;
280 unsafe {
281 try_call!(raw::git_repository_discover(
282 buf.raw(),
283 path,
284 1,
285 ceiling_dirs
286 ));
287 }
288
289 Ok(util::bytes2path(&*buf).to_path_buf())
290 }
291
292 pub fn init<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
298 Repository::init_opts(path, &RepositoryInitOptions::new())
299 }
300
301 pub fn init_bare<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
305 Repository::init_opts(path, RepositoryInitOptions::new().bare(true))
306 }
307
308 pub fn init_opts<P: AsRef<Path>>(
312 path: P,
313 opts: &RepositoryInitOptions,
314 ) -> Result<Repository, Error> {
315 crate::init();
316 let path = path.as_ref().into_c_string()?;
318 let mut ret = ptr::null_mut();
319 unsafe {
320 let mut opts = opts.raw();
321 try_call!(raw::git_repository_init_ext(&mut ret, path, &mut opts));
322 Ok(Binding::from_raw(ret))
323 }
324 }
325
326 pub fn clone<P: AsRef<Path>>(url: &str, into: P) -> Result<Repository, Error> {
331 crate::init();
332 RepoBuilder::new().clone(url, into.as_ref())
333 }
334
335 pub fn clone_recurse<P: AsRef<Path>>(url: &str, into: P) -> Result<Repository, Error> {
340 let repo = Repository::clone(url, into)?;
341 repo.update_submodules()?;
342 Ok(repo)
343 }
344
345 pub fn from_odb(odb: Odb<'_>) -> Result<Repository, Error> {
347 crate::init();
348 let mut ret = ptr::null_mut();
349 unsafe {
350 try_call!(raw::git_repository_wrap_odb(&mut ret, odb.raw()));
351 Ok(Binding::from_raw(ret))
352 }
353 }
354
355 fn update_submodules(&self) -> Result<(), Error> {
359 fn add_subrepos(repo: &Repository, list: &mut Vec<Repository>) -> Result<(), Error> {
360 for mut subm in repo.submodules()? {
361 subm.update(true, None)?;
362 list.push(subm.open()?);
363 }
364 Ok(())
365 }
366
367 let mut repos = Vec::new();
368 add_subrepos(self, &mut repos)?;
369 while let Some(repo) = repos.pop() {
370 add_subrepos(&repo, &mut repos)?;
371 }
372 Ok(())
373 }
374
375 pub fn revparse(&self, spec: &str) -> Result<Revspec<'_>, Error> {
380 let mut raw = raw::git_revspec {
381 from: ptr::null_mut(),
382 to: ptr::null_mut(),
383 flags: 0,
384 };
385 let spec = CString::new(spec)?;
386 unsafe {
387 try_call!(raw::git_revparse(&mut raw, self.raw, spec));
388 let to = Binding::from_raw_opt(raw.to);
389 let from = Binding::from_raw_opt(raw.from);
390 let mode = RevparseMode::from_bits_truncate(raw.flags as u32);
391 Ok(Revspec::from_objects(from, to, mode))
392 }
393 }
394
395 pub fn revparse_single(&self, spec: &str) -> Result<Object<'_>, Error> {
397 let spec = CString::new(spec)?;
398 let mut obj = ptr::null_mut();
399 unsafe {
400 try_call!(raw::git_revparse_single(&mut obj, self.raw, spec));
401 assert!(!obj.is_null());
402 Ok(Binding::from_raw(obj))
403 }
404 }
405
406 pub fn revparse_ext(&self, spec: &str) -> Result<(Object<'_>, Option<Reference<'_>>), Error> {
416 let spec = CString::new(spec)?;
417 let mut git_obj = ptr::null_mut();
418 let mut git_ref = ptr::null_mut();
419 unsafe {
420 try_call!(raw::git_revparse_ext(
421 &mut git_obj,
422 &mut git_ref,
423 self.raw,
424 spec
425 ));
426 assert!(!git_obj.is_null());
427 Ok((Binding::from_raw(git_obj), Binding::from_raw_opt(git_ref)))
428 }
429 }
430
431 pub fn is_bare(&self) -> bool {
433 unsafe { raw::git_repository_is_bare(self.raw) == 1 }
434 }
435
436 pub fn is_shallow(&self) -> bool {
438 unsafe { raw::git_repository_is_shallow(self.raw) == 1 }
439 }
440
441 pub fn is_worktree(&self) -> bool {
443 unsafe { raw::git_repository_is_worktree(self.raw) == 1 }
444 }
445
446 pub fn is_empty(&self) -> Result<bool, Error> {
448 let empty = unsafe { try_call!(raw::git_repository_is_empty(self.raw)) };
449 Ok(empty == 1)
450 }
451
452 pub fn path(&self) -> &Path {
455 unsafe {
456 let ptr = raw::git_repository_path(self.raw);
457 util::bytes2path(crate::opt_bytes(self, ptr).unwrap())
458 }
459 }
460
461 pub fn commondir(&self) -> &Path {
467 unsafe {
468 let ptr = raw::git_repository_commondir(self.raw);
469 util::bytes2path(crate::opt_bytes(self, ptr).unwrap())
470 }
471 }
472
473 pub fn state(&self) -> RepositoryState {
475 let state = unsafe { raw::git_repository_state(self.raw) };
476 macro_rules! check( ($($raw:ident => $real:ident),*) => (
477 $(if state == raw::$raw as c_int {
478 super::RepositoryState::$real
479 }) else *
480 else {
481 panic!("unknown repository state: {}", state)
482 }
483 ) );
484
485 check!(
486 GIT_REPOSITORY_STATE_NONE => Clean,
487 GIT_REPOSITORY_STATE_MERGE => Merge,
488 GIT_REPOSITORY_STATE_REVERT => Revert,
489 GIT_REPOSITORY_STATE_REVERT_SEQUENCE => RevertSequence,
490 GIT_REPOSITORY_STATE_CHERRYPICK => CherryPick,
491 GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE => CherryPickSequence,
492 GIT_REPOSITORY_STATE_BISECT => Bisect,
493 GIT_REPOSITORY_STATE_REBASE => Rebase,
494 GIT_REPOSITORY_STATE_REBASE_INTERACTIVE => RebaseInteractive,
495 GIT_REPOSITORY_STATE_REBASE_MERGE => RebaseMerge,
496 GIT_REPOSITORY_STATE_APPLY_MAILBOX => ApplyMailbox,
497 GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE => ApplyMailboxOrRebase
498 )
499 }
500
501 pub fn workdir(&self) -> Option<&Path> {
505 unsafe {
506 let ptr = raw::git_repository_workdir(self.raw);
507 if ptr.is_null() {
508 None
509 } else {
510 Some(util::bytes2path(CStr::from_ptr(ptr).to_bytes()))
511 }
512 }
513 }
514
515 pub fn set_workdir(&self, path: &Path, update_gitlink: bool) -> Result<(), Error> {
521 let path = path.into_c_string()?;
523 unsafe {
524 try_call!(raw::git_repository_set_workdir(
525 self.raw(),
526 path,
527 update_gitlink
528 ));
529 }
530 Ok(())
531 }
532
533 pub fn namespace(&self) -> Option<&str> {
538 self.namespace_bytes().and_then(|s| str::from_utf8(s).ok())
539 }
540
541 pub fn namespace_bytes(&self) -> Option<&[u8]> {
545 unsafe { crate::opt_bytes(self, raw::git_repository_get_namespace(self.raw)) }
546 }
547
548 pub fn set_namespace(&self, namespace: &str) -> Result<(), Error> {
550 self.set_namespace_bytes(namespace.as_bytes())
551 }
552
553 pub fn set_namespace_bytes(&self, namespace: &[u8]) -> Result<(), Error> {
555 unsafe {
556 let namespace = CString::new(namespace)?;
557 try_call!(raw::git_repository_set_namespace(self.raw, namespace));
558 Ok(())
559 }
560 }
561
562 pub fn remove_namespace(&self) -> Result<(), Error> {
564 unsafe {
565 try_call!(raw::git_repository_set_namespace(self.raw, ptr::null()));
566 Ok(())
567 }
568 }
569
570 pub fn message(&self) -> Result<String, Error> {
573 unsafe {
574 let buf = Buf::new();
575 try_call!(raw::git_repository_message(buf.raw(), self.raw));
576 Ok(str::from_utf8(&buf).unwrap().to_string())
577 }
578 }
579
580 pub fn remove_message(&self) -> Result<(), Error> {
582 unsafe {
583 try_call!(raw::git_repository_message_remove(self.raw));
584 Ok(())
585 }
586 }
587
588 pub fn remotes(&self) -> Result<StringArray, Error> {
590 let mut arr = raw::git_strarray {
591 strings: ptr::null_mut(),
592 count: 0,
593 };
594 unsafe {
595 try_call!(raw::git_remote_list(&mut arr, self.raw));
596 Ok(Binding::from_raw(arr))
597 }
598 }
599
600 pub fn find_remote(&self, name: &str) -> Result<Remote<'_>, Error> {
602 let mut ret = ptr::null_mut();
603 let name = CString::new(name)?;
604 unsafe {
605 try_call!(raw::git_remote_lookup(&mut ret, self.raw, name));
606 Ok(Binding::from_raw(ret))
607 }
608 }
609
610 pub fn remote(&self, name: &str, url: &str) -> Result<Remote<'_>, Error> {
613 let mut ret = ptr::null_mut();
614 let name = CString::new(name)?;
615 let url = CString::new(url)?;
616 unsafe {
617 try_call!(raw::git_remote_create(&mut ret, self.raw, name, url));
618 Ok(Binding::from_raw(ret))
619 }
620 }
621
622 pub fn remote_with_fetch(
625 &self,
626 name: &str,
627 url: &str,
628 fetch: &str,
629 ) -> Result<Remote<'_>, Error> {
630 let mut ret = ptr::null_mut();
631 let name = CString::new(name)?;
632 let url = CString::new(url)?;
633 let fetch = CString::new(fetch)?;
634 unsafe {
635 try_call!(raw::git_remote_create_with_fetchspec(
636 &mut ret, self.raw, name, url, fetch
637 ));
638 Ok(Binding::from_raw(ret))
639 }
640 }
641
642 pub fn remote_anonymous(&self, url: &str) -> Result<Remote<'_>, Error> {
648 let mut ret = ptr::null_mut();
649 let url = CString::new(url)?;
650 unsafe {
651 try_call!(raw::git_remote_create_anonymous(&mut ret, self.raw, url));
652 Ok(Binding::from_raw(ret))
653 }
654 }
655
656 pub fn remote_rename(&self, name: &str, new_name: &str) -> Result<StringArray, Error> {
670 let name = CString::new(name)?;
671 let new_name = CString::new(new_name)?;
672 let mut problems = raw::git_strarray {
673 count: 0,
674 strings: ptr::null_mut(),
675 };
676 unsafe {
677 try_call!(raw::git_remote_rename(
678 &mut problems,
679 self.raw,
680 name,
681 new_name
682 ));
683 Ok(Binding::from_raw(problems))
684 }
685 }
686
687 pub fn remote_delete(&self, name: &str) -> Result<(), Error> {
692 let name = CString::new(name)?;
693 unsafe {
694 try_call!(raw::git_remote_delete(self.raw, name));
695 }
696 Ok(())
697 }
698
699 pub fn remote_add_fetch(&self, name: &str, spec: &str) -> Result<(), Error> {
704 let name = CString::new(name)?;
705 let spec = CString::new(spec)?;
706 unsafe {
707 try_call!(raw::git_remote_add_fetch(self.raw, name, spec));
708 }
709 Ok(())
710 }
711
712 pub fn remote_add_push(&self, name: &str, spec: &str) -> Result<(), Error> {
717 let name = CString::new(name)?;
718 let spec = CString::new(spec)?;
719 unsafe {
720 try_call!(raw::git_remote_add_push(self.raw, name, spec));
721 }
722 Ok(())
723 }
724
725 pub fn remote_set_url(&self, name: &str, url: &str) -> Result<(), Error> {
731 let name = CString::new(name)?;
732 let url = CString::new(url)?;
733 unsafe {
734 try_call!(raw::git_remote_set_url(self.raw, name, url));
735 }
736 Ok(())
737 }
738
739 pub fn remote_set_pushurl(&self, name: &str, pushurl: Option<&str>) -> Result<(), Error> {
747 let name = CString::new(name)?;
748 let pushurl = crate::opt_cstr(pushurl)?;
749 unsafe {
750 try_call!(raw::git_remote_set_pushurl(self.raw, name, pushurl));
751 }
752 Ok(())
753 }
754
755 pub fn reset(
773 &self,
774 target: &Object<'_>,
775 kind: ResetType,
776 checkout: Option<&mut CheckoutBuilder<'_>>,
777 ) -> Result<(), Error> {
778 unsafe {
779 let mut opts: raw::git_checkout_options = mem::zeroed();
780 try_call!(raw::git_checkout_init_options(
781 &mut opts,
782 raw::GIT_CHECKOUT_OPTIONS_VERSION
783 ));
784 let opts = checkout.map(|c| {
785 c.configure(&mut opts);
786 &mut opts
787 });
788 try_call!(raw::git_reset(self.raw, target.raw(), kind, opts));
789 }
790 Ok(())
791 }
792
793 pub fn reset_default<T, I>(&self, target: Option<&Object<'_>>, paths: I) -> Result<(), Error>
801 where
802 T: IntoCString,
803 I: IntoIterator<Item = T>,
804 {
805 let (_a, _b, mut arr) = crate::util::iter2cstrs_paths(paths)?;
806 let target = target.map(|t| t.raw());
807 unsafe {
808 try_call!(raw::git_reset_default(self.raw, target, &mut arr));
809 }
810 Ok(())
811 }
812
813 pub fn head(&self) -> Result<Reference<'_>, Error> {
815 let mut ret = ptr::null_mut();
816 unsafe {
817 try_call!(raw::git_repository_head(&mut ret, self.raw));
818 Ok(Binding::from_raw(ret))
819 }
820 }
821
822 pub fn set_head(&self, refname: &str) -> Result<(), Error> {
835 self.set_head_bytes(refname.as_bytes())
836 }
837
838 pub fn set_head_bytes(&self, refname: &[u8]) -> Result<(), Error> {
851 let refname = CString::new(refname)?;
852 unsafe {
853 try_call!(raw::git_repository_set_head(self.raw, refname));
854 }
855 Ok(())
856 }
857
858 pub fn head_detached(&self) -> Result<bool, Error> {
860 unsafe {
861 let value = raw::git_repository_head_detached(self.raw);
862 match value {
863 0 => Ok(false),
864 1 => Ok(true),
865 _ => Err(Error::last_error(value)),
866 }
867 }
868 }
869
870 pub fn set_head_detached(&self, commitish: Oid) -> Result<(), Error> {
881 unsafe {
882 try_call!(raw::git_repository_set_head_detached(
883 self.raw,
884 commitish.raw()
885 ));
886 }
887 Ok(())
888 }
889
890 pub fn set_head_detached_from_annotated(
899 &self,
900 commitish: AnnotatedCommit<'_>,
901 ) -> Result<(), Error> {
902 unsafe {
903 try_call!(raw::git_repository_set_head_detached_from_annotated(
904 self.raw,
905 commitish.raw()
906 ));
907 }
908 Ok(())
909 }
910
911 pub fn references(&self) -> Result<References<'_>, Error> {
913 let mut ret = ptr::null_mut();
914 unsafe {
915 try_call!(raw::git_reference_iterator_new(&mut ret, self.raw));
916 Ok(Binding::from_raw(ret))
917 }
918 }
919
920 pub fn references_glob(&self, glob: &str) -> Result<References<'_>, Error> {
923 let mut ret = ptr::null_mut();
924 let glob = CString::new(glob)?;
925 unsafe {
926 try_call!(raw::git_reference_iterator_glob_new(
927 &mut ret, self.raw, glob
928 ));
929
930 Ok(Binding::from_raw(ret))
931 }
932 }
933
934 pub fn submodules(&self) -> Result<Vec<Submodule<'_>>, Error> {
936 struct Data<'a, 'b> {
937 repo: &'b Repository,
938 ret: &'a mut Vec<Submodule<'b>>,
939 }
940 let mut ret = Vec::new();
941
942 unsafe {
943 let mut data = Data {
944 repo: self,
945 ret: &mut ret,
946 };
947 let cb: raw::git_submodule_cb = Some(append);
948 try_call!(raw::git_submodule_foreach(
949 self.raw,
950 cb,
951 &mut data as *mut _ as *mut c_void
952 ));
953 }
954
955 return Ok(ret);
956
957 extern "C" fn append(
958 _repo: *mut raw::git_submodule,
959 name: *const c_char,
960 data: *mut c_void,
961 ) -> c_int {
962 unsafe {
963 let data = &mut *(data as *mut Data<'_, '_>);
964 let mut raw = ptr::null_mut();
965 let rc = raw::git_submodule_lookup(&mut raw, data.repo.raw(), name);
966 assert_eq!(rc, 0);
967 data.ret.push(Binding::from_raw(raw));
968 }
969 0
970 }
971 }
972
973 pub fn statuses(&self, options: Option<&mut StatusOptions>) -> Result<Statuses<'_>, Error> {
980 let mut ret = ptr::null_mut();
981 unsafe {
982 try_call!(raw::git_status_list_new(
983 &mut ret,
984 self.raw,
985 options.map(|s| s.raw()).unwrap_or(ptr::null())
986 ));
987 Ok(Binding::from_raw(ret))
988 }
989 }
990
991 pub fn status_should_ignore(&self, path: &Path) -> Result<bool, Error> {
1000 let mut ret = 0 as c_int;
1001 let path = util::cstring_to_repo_path(path)?;
1002 unsafe {
1003 try_call!(raw::git_status_should_ignore(&mut ret, self.raw, path));
1004 }
1005 Ok(ret != 0)
1006 }
1007
1008 pub fn status_file(&self, path: &Path) -> Result<Status, Error> {
1025 let mut ret = 0 as c_uint;
1026 let path = path_to_repo_path(path)?;
1027 unsafe {
1028 try_call!(raw::git_status_file(&mut ret, self.raw, path));
1029 }
1030 Ok(Status::from_bits_truncate(ret as u32))
1031 }
1032
1033 pub fn branches(&self, filter: Option<BranchType>) -> Result<Branches<'_>, Error> {
1035 let mut raw = ptr::null_mut();
1036 unsafe {
1037 try_call!(raw::git_branch_iterator_new(&mut raw, self.raw(), filter));
1038 Ok(Branches::from_raw(raw))
1039 }
1040 }
1041
1042 pub fn index(&self) -> Result<Index, Error> {
1052 let mut raw = ptr::null_mut();
1053 unsafe {
1054 try_call!(raw::git_repository_index(&mut raw, self.raw()));
1055 Ok(Binding::from_raw(raw))
1056 }
1057 }
1058
1059 pub fn set_index(&self, index: &mut Index) -> Result<(), Error> {
1061 unsafe {
1062 try_call!(raw::git_repository_set_index(self.raw(), index.raw()));
1063 }
1064 Ok(())
1065 }
1066
1067 pub fn config(&self) -> Result<Config, Error> {
1073 let mut raw = ptr::null_mut();
1074 unsafe {
1075 try_call!(raw::git_repository_config(&mut raw, self.raw()));
1076 Ok(Binding::from_raw(raw))
1077 }
1078 }
1079
1080 pub fn get_attr(
1090 &self,
1091 path: &Path,
1092 name: &str,
1093 flags: AttrCheckFlags,
1094 ) -> Result<Option<&str>, Error> {
1095 Ok(self
1096 .get_attr_bytes(path, name, flags)?
1097 .and_then(|a| str::from_utf8(a).ok()))
1098 }
1099
1100 pub fn get_attr_bytes(
1110 &self,
1111 path: &Path,
1112 name: &str,
1113 flags: AttrCheckFlags,
1114 ) -> Result<Option<&[u8]>, Error> {
1115 let mut ret = ptr::null();
1116 let path = util::cstring_to_repo_path(path)?;
1117 let name = CString::new(name)?;
1118 unsafe {
1119 try_call!(raw::git_attr_get(
1120 &mut ret,
1121 self.raw(),
1122 flags.bits(),
1123 path,
1124 name
1125 ));
1126 Ok(crate::opt_bytes(self, ret))
1127 }
1128 }
1129
1130 pub fn blob(&self, data: &[u8]) -> Result<Oid, Error> {
1135 let mut raw = raw::git_oid {
1136 id: [0; raw::GIT_OID_RAWSZ],
1137 };
1138 unsafe {
1139 let ptr = data.as_ptr() as *const c_void;
1140 let len = data.len() as size_t;
1141 try_call!(raw::git_blob_create_frombuffer(
1142 &mut raw,
1143 self.raw(),
1144 ptr,
1145 len
1146 ));
1147 Ok(Binding::from_raw(&raw as *const _))
1148 }
1149 }
1150
1151 pub fn blob_path(&self, path: &Path) -> Result<Oid, Error> {
1157 let path = path.into_c_string()?;
1159 let mut raw = raw::git_oid {
1160 id: [0; raw::GIT_OID_RAWSZ],
1161 };
1162 unsafe {
1163 try_call!(raw::git_blob_create_fromdisk(&mut raw, self.raw(), path));
1164 Ok(Binding::from_raw(&raw as *const _))
1165 }
1166 }
1167
1168 pub fn blob_writer(&self, hintpath: Option<&Path>) -> Result<BlobWriter<'_>, Error> {
1180 let path_str = match hintpath {
1181 Some(path) => Some(path.into_c_string()?),
1182 None => None,
1183 };
1184 let path = match path_str {
1185 Some(ref path) => path.as_ptr(),
1186 None => ptr::null(),
1187 };
1188 let mut out = ptr::null_mut();
1189 unsafe {
1190 try_call!(raw::git_blob_create_fromstream(&mut out, self.raw(), path));
1191 Ok(BlobWriter::from_raw(out))
1192 }
1193 }
1194
1195 pub fn find_blob(&self, oid: Oid) -> Result<Blob<'_>, Error> {
1197 let mut raw = ptr::null_mut();
1198 unsafe {
1199 try_call!(raw::git_blob_lookup(&mut raw, self.raw(), oid.raw()));
1200 Ok(Binding::from_raw(raw))
1201 }
1202 }
1203
1204 pub fn odb(&self) -> Result<Odb<'_>, Error> {
1206 let mut odb = ptr::null_mut();
1207 unsafe {
1208 try_call!(raw::git_repository_odb(&mut odb, self.raw()));
1209 Ok(Odb::from_raw(odb))
1210 }
1211 }
1212
1213 pub fn set_odb(&self, odb: &Odb<'_>) -> Result<(), Error> {
1215 unsafe {
1216 try_call!(raw::git_repository_set_odb(self.raw(), odb.raw()));
1217 }
1218 Ok(())
1219 }
1220
1221 pub fn branch(
1227 &self,
1228 branch_name: &str,
1229 target: &Commit<'_>,
1230 force: bool,
1231 ) -> Result<Branch<'_>, Error> {
1232 let branch_name = CString::new(branch_name)?;
1233 let mut raw = ptr::null_mut();
1234 unsafe {
1235 try_call!(raw::git_branch_create(
1236 &mut raw,
1237 self.raw(),
1238 branch_name,
1239 target.raw(),
1240 force
1241 ));
1242 Ok(Branch::wrap(Binding::from_raw(raw)))
1243 }
1244 }
1245
1246 pub fn branch_from_annotated_commit(
1255 &self,
1256 branch_name: &str,
1257 target: &AnnotatedCommit<'_>,
1258 force: bool,
1259 ) -> Result<Branch<'_>, Error> {
1260 let branch_name = CString::new(branch_name)?;
1261 let mut raw = ptr::null_mut();
1262 unsafe {
1263 try_call!(raw::git_branch_create_from_annotated(
1264 &mut raw,
1265 self.raw(),
1266 branch_name,
1267 target.raw(),
1268 force
1269 ));
1270 Ok(Branch::wrap(Binding::from_raw(raw)))
1271 }
1272 }
1273
1274 pub fn find_branch(&self, name: &str, branch_type: BranchType) -> Result<Branch<'_>, Error> {
1276 let name = CString::new(name)?;
1277 let mut ret = ptr::null_mut();
1278 unsafe {
1279 try_call!(raw::git_branch_lookup(
1280 &mut ret,
1281 self.raw(),
1282 name,
1283 branch_type
1284 ));
1285 Ok(Branch::wrap(Binding::from_raw(ret)))
1286 }
1287 }
1288
1289 pub fn commit(
1298 &self,
1299 update_ref: Option<&str>,
1300 author: &Signature<'_>,
1301 committer: &Signature<'_>,
1302 message: &str,
1303 tree: &Tree<'_>,
1304 parents: &[&Commit<'_>],
1305 ) -> Result<Oid, Error> {
1306 let update_ref = crate::opt_cstr(update_ref)?;
1307 let mut parent_ptrs = parents
1308 .iter()
1309 .map(|p| p.raw() as *const raw::git_commit)
1310 .collect::<Vec<_>>();
1311 let message = CString::new(message)?;
1312 let mut raw = raw::git_oid {
1313 id: [0; raw::GIT_OID_RAWSZ],
1314 };
1315 unsafe {
1316 try_call!(raw::git_commit_create(
1317 &mut raw,
1318 self.raw(),
1319 update_ref,
1320 author.raw(),
1321 committer.raw(),
1322 ptr::null(),
1323 message,
1324 tree.raw(),
1325 parents.len() as size_t,
1326 parent_ptrs.as_mut_ptr()
1327 ));
1328 Ok(Binding::from_raw(&raw as *const _))
1329 }
1330 }
1331
1332 pub fn commit_create_buffer(
1338 &self,
1339 author: &Signature<'_>,
1340 committer: &Signature<'_>,
1341 message: &str,
1342 tree: &Tree<'_>,
1343 parents: &[&Commit<'_>],
1344 ) -> Result<Buf, Error> {
1345 let mut parent_ptrs = parents
1346 .iter()
1347 .map(|p| p.raw() as *const raw::git_commit)
1348 .collect::<Vec<_>>();
1349 let message = CString::new(message)?;
1350 let buf = Buf::new();
1351 unsafe {
1352 try_call!(raw::git_commit_create_buffer(
1353 buf.raw(),
1354 self.raw(),
1355 author.raw(),
1356 committer.raw(),
1357 ptr::null(),
1358 message,
1359 tree.raw(),
1360 parents.len() as size_t,
1361 parent_ptrs.as_mut_ptr()
1362 ));
1363 Ok(buf)
1364 }
1365 }
1366
1367 pub fn commit_signed(
1378 &self,
1379 commit_content: &str,
1380 signature: &str,
1381 signature_field: Option<&str>,
1382 ) -> Result<Oid, Error> {
1383 let commit_content = CString::new(commit_content)?;
1384 let signature = CString::new(signature)?;
1385 let signature_field = crate::opt_cstr(signature_field)?;
1386 let mut raw = raw::git_oid {
1387 id: [0; raw::GIT_OID_RAWSZ],
1388 };
1389 unsafe {
1390 try_call!(raw::git_commit_create_with_signature(
1391 &mut raw,
1392 self.raw(),
1393 commit_content,
1394 signature,
1395 signature_field
1396 ));
1397 Ok(Binding::from_raw(&raw as *const _))
1398 }
1399 }
1400
1401 pub fn extract_signature(
1406 &self,
1407 commit_id: &Oid,
1408 signature_field: Option<&str>,
1409 ) -> Result<(Buf, Buf), Error> {
1410 let signature_field = crate::opt_cstr(signature_field)?;
1411 let signature = Buf::new();
1412 let content = Buf::new();
1413 unsafe {
1414 try_call!(raw::git_commit_extract_signature(
1415 signature.raw(),
1416 content.raw(),
1417 self.raw(),
1418 commit_id.raw() as *mut _,
1419 signature_field
1420 ));
1421 Ok((signature, content))
1422 }
1423 }
1424
1425 pub fn find_commit(&self, oid: Oid) -> Result<Commit<'_>, Error> {
1427 let mut raw = ptr::null_mut();
1428 unsafe {
1429 try_call!(raw::git_commit_lookup(&mut raw, self.raw(), oid.raw()));
1430 Ok(Binding::from_raw(raw))
1431 }
1432 }
1433
1434 pub fn find_commit_by_prefix(&self, prefix_hash: &str) -> Result<Commit<'_>, Error> {
1436 let mut raw = ptr::null_mut();
1437 unsafe {
1438 try_call!(raw::git_commit_lookup_prefix(
1439 &mut raw,
1440 self.raw(),
1441 Oid::from_str(prefix_hash)?.raw(),
1442 prefix_hash.len()
1443 ));
1444 Ok(Binding::from_raw(raw))
1445 }
1446 }
1447
1448 pub fn find_annotated_commit(&self, id: Oid) -> Result<AnnotatedCommit<'_>, Error> {
1450 unsafe {
1451 let mut raw = ptr::null_mut();
1452 try_call!(raw::git_annotated_commit_lookup(
1453 &mut raw,
1454 self.raw(),
1455 id.raw()
1456 ));
1457 Ok(Binding::from_raw(raw))
1458 }
1459 }
1460
1461 pub fn find_object(&self, oid: Oid, kind: Option<ObjectType>) -> Result<Object<'_>, Error> {
1463 let mut raw = ptr::null_mut();
1464 unsafe {
1465 try_call!(raw::git_object_lookup(
1466 &mut raw,
1467 self.raw(),
1468 oid.raw(),
1469 kind
1470 ));
1471 Ok(Binding::from_raw(raw))
1472 }
1473 }
1474
1475 pub fn find_object_by_prefix(
1477 &self,
1478 prefix_hash: &str,
1479 kind: Option<ObjectType>,
1480 ) -> Result<Object<'_>, Error> {
1481 let mut raw = ptr::null_mut();
1482 unsafe {
1483 try_call!(raw::git_object_lookup_prefix(
1484 &mut raw,
1485 self.raw(),
1486 Oid::from_str(prefix_hash)?.raw(),
1487 prefix_hash.len(),
1488 kind
1489 ));
1490 Ok(Binding::from_raw(raw))
1491 }
1492 }
1493
1494 pub fn reference(
1500 &self,
1501 name: &str,
1502 id: Oid,
1503 force: bool,
1504 log_message: &str,
1505 ) -> Result<Reference<'_>, Error> {
1506 let name = CString::new(name)?;
1507 let log_message = CString::new(log_message)?;
1508 let mut raw = ptr::null_mut();
1509 unsafe {
1510 try_call!(raw::git_reference_create(
1511 &mut raw,
1512 self.raw(),
1513 name,
1514 id.raw(),
1515 force,
1516 log_message
1517 ));
1518 Ok(Binding::from_raw(raw))
1519 }
1520 }
1521
1522 pub fn reference_matching(
1553 &self,
1554 name: &str,
1555 id: Oid,
1556 force: bool,
1557 current_id: Oid,
1558 log_message: &str,
1559 ) -> Result<Reference<'_>, Error> {
1560 let name = CString::new(name)?;
1561 let log_message = CString::new(log_message)?;
1562 let mut raw = ptr::null_mut();
1563 unsafe {
1564 try_call!(raw::git_reference_create_matching(
1565 &mut raw,
1566 self.raw(),
1567 name,
1568 id.raw(),
1569 force,
1570 current_id.raw(),
1571 log_message
1572 ));
1573 Ok(Binding::from_raw(raw))
1574 }
1575 }
1576
1577 pub fn reference_symbolic(
1596 &self,
1597 name: &str,
1598 target: &str,
1599 force: bool,
1600 log_message: &str,
1601 ) -> Result<Reference<'_>, Error> {
1602 let name = CString::new(name)?;
1603 let target = CString::new(target)?;
1604 let log_message = CString::new(log_message)?;
1605 let mut raw = ptr::null_mut();
1606 unsafe {
1607 try_call!(raw::git_reference_symbolic_create(
1608 &mut raw,
1609 self.raw(),
1610 name,
1611 target,
1612 force,
1613 log_message
1614 ));
1615 Ok(Binding::from_raw(raw))
1616 }
1617 }
1618
1619 pub fn reference_symbolic_matching(
1629 &self,
1630 name: &str,
1631 target: &str,
1632 force: bool,
1633 current_value: &str,
1634 log_message: &str,
1635 ) -> Result<Reference<'_>, Error> {
1636 let name = CString::new(name)?;
1637 let target = CString::new(target)?;
1638 let current_value = CString::new(current_value)?;
1639 let log_message = CString::new(log_message)?;
1640 let mut raw = ptr::null_mut();
1641 unsafe {
1642 try_call!(raw::git_reference_symbolic_create_matching(
1643 &mut raw,
1644 self.raw(),
1645 name,
1646 target,
1647 force,
1648 current_value,
1649 log_message
1650 ));
1651 Ok(Binding::from_raw(raw))
1652 }
1653 }
1654
1655 pub fn find_reference(&self, name: &str) -> Result<Reference<'_>, Error> {
1657 let name = CString::new(name)?;
1658 let mut raw = ptr::null_mut();
1659 unsafe {
1660 try_call!(raw::git_reference_lookup(&mut raw, self.raw(), name));
1661 Ok(Binding::from_raw(raw))
1662 }
1663 }
1664
1665 pub fn resolve_reference_from_short_name(&self, refname: &str) -> Result<Reference<'_>, Error> {
1670 let refname = CString::new(refname)?;
1671 let mut raw = ptr::null_mut();
1672 unsafe {
1673 try_call!(raw::git_reference_dwim(&mut raw, self.raw(), refname));
1674 Ok(Binding::from_raw(raw))
1675 }
1676 }
1677
1678 pub fn refname_to_id(&self, name: &str) -> Result<Oid, Error> {
1684 let name = CString::new(name)?;
1685 let mut ret = raw::git_oid {
1686 id: [0; raw::GIT_OID_RAWSZ],
1687 };
1688 unsafe {
1689 try_call!(raw::git_reference_name_to_id(&mut ret, self.raw(), name));
1690 Ok(Binding::from_raw(&ret as *const _))
1691 }
1692 }
1693
1694 pub fn reference_to_annotated_commit(
1696 &self,
1697 reference: &Reference<'_>,
1698 ) -> Result<AnnotatedCommit<'_>, Error> {
1699 let mut ret = ptr::null_mut();
1700 unsafe {
1701 try_call!(raw::git_annotated_commit_from_ref(
1702 &mut ret,
1703 self.raw(),
1704 reference.raw()
1705 ));
1706 Ok(AnnotatedCommit::from_raw(ret))
1707 }
1708 }
1709
1710 pub fn annotated_commit_from_fetchhead(
1712 &self,
1713 branch_name: &str,
1714 remote_url: &str,
1715 id: &Oid,
1716 ) -> Result<AnnotatedCommit<'_>, Error> {
1717 let branch_name = CString::new(branch_name)?;
1718 let remote_url = CString::new(remote_url)?;
1719
1720 let mut ret = ptr::null_mut();
1721 unsafe {
1722 try_call!(raw::git_annotated_commit_from_fetchhead(
1723 &mut ret,
1724 self.raw(),
1725 branch_name,
1726 remote_url,
1727 id.raw()
1728 ));
1729 Ok(AnnotatedCommit::from_raw(ret))
1730 }
1731 }
1732
1733 pub fn signature(&self) -> Result<Signature<'static>, Error> {
1740 let mut ret = ptr::null_mut();
1741 unsafe {
1742 try_call!(raw::git_signature_default(&mut ret, self.raw()));
1743 Ok(Binding::from_raw(ret))
1744 }
1745 }
1746
1747 pub fn submodule(
1760 &self,
1761 url: &str,
1762 path: &Path,
1763 use_gitlink: bool,
1764 ) -> Result<Submodule<'_>, Error> {
1765 let url = CString::new(url)?;
1766 let path = path_to_repo_path(path)?;
1767 let mut raw = ptr::null_mut();
1768 unsafe {
1769 try_call!(raw::git_submodule_add_setup(
1770 &mut raw,
1771 self.raw(),
1772 url,
1773 path,
1774 use_gitlink
1775 ));
1776 Ok(Binding::from_raw(raw))
1777 }
1778 }
1779
1780 pub fn find_submodule(&self, name: &str) -> Result<Submodule<'_>, Error> {
1785 let name = CString::new(name)?;
1786 let mut raw = ptr::null_mut();
1787 unsafe {
1788 try_call!(raw::git_submodule_lookup(&mut raw, self.raw(), name));
1789 Ok(Binding::from_raw(raw))
1790 }
1791 }
1792
1793 pub fn submodule_status(
1798 &self,
1799 name: &str,
1800 ignore: SubmoduleIgnore,
1801 ) -> Result<SubmoduleStatus, Error> {
1802 let mut ret = 0;
1803 let name = CString::new(name)?;
1804 unsafe {
1805 try_call!(raw::git_submodule_status(&mut ret, self.raw, name, ignore));
1806 }
1807 Ok(SubmoduleStatus::from_bits_truncate(ret as u32))
1808 }
1809
1810 pub fn submodule_set_ignore(
1814 &mut self,
1815 name: &str,
1816 ignore: SubmoduleIgnore,
1817 ) -> Result<(), Error> {
1818 let name = CString::new(name)?;
1819 unsafe {
1820 try_call!(raw::git_submodule_set_ignore(self.raw(), name, ignore));
1821 }
1822 Ok(())
1823 }
1824
1825 pub fn submodule_set_update(
1829 &mut self,
1830 name: &str,
1831 update: SubmoduleUpdate,
1832 ) -> Result<(), Error> {
1833 let name = CString::new(name)?;
1834 unsafe {
1835 try_call!(raw::git_submodule_set_update(self.raw(), name, update));
1836 }
1837 Ok(())
1838 }
1839
1840 pub fn submodule_set_url(&mut self, name: &str, url: &str) -> Result<(), Error> {
1845 let name = CString::new(name)?;
1846 let url = CString::new(url)?;
1847 unsafe {
1848 try_call!(raw::git_submodule_set_url(self.raw(), name, url));
1849 }
1850 Ok(())
1851 }
1852
1853 pub fn submodule_set_branch(&mut self, name: &str, branch_name: &str) -> Result<(), Error> {
1858 let name = CString::new(name)?;
1859 let branch_name = CString::new(branch_name)?;
1860 unsafe {
1861 try_call!(raw::git_submodule_set_branch(self.raw(), name, branch_name));
1862 }
1863 Ok(())
1864 }
1865
1866 pub fn find_tree(&self, oid: Oid) -> Result<Tree<'_>, Error> {
1868 let mut raw = ptr::null_mut();
1869 unsafe {
1870 try_call!(raw::git_tree_lookup(&mut raw, self.raw(), oid.raw()));
1871 Ok(Binding::from_raw(raw))
1872 }
1873 }
1874
1875 pub fn treebuilder(&self, tree: Option<&Tree<'_>>) -> Result<TreeBuilder<'_>, Error> {
1881 unsafe {
1882 let mut ret = ptr::null_mut();
1883 let tree = match tree {
1884 Some(tree) => tree.raw(),
1885 None => ptr::null_mut(),
1886 };
1887 try_call!(raw::git_treebuilder_new(&mut ret, self.raw, tree));
1888 Ok(Binding::from_raw(ret))
1889 }
1890 }
1891
1892 pub fn tag(
1904 &self,
1905 name: &str,
1906 target: &Object<'_>,
1907 tagger: &Signature<'_>,
1908 message: &str,
1909 force: bool,
1910 ) -> Result<Oid, Error> {
1911 let name = CString::new(name)?;
1912 let message = CString::new(message)?;
1913 let mut raw = raw::git_oid {
1914 id: [0; raw::GIT_OID_RAWSZ],
1915 };
1916 unsafe {
1917 try_call!(raw::git_tag_create(
1918 &mut raw,
1919 self.raw,
1920 name,
1921 target.raw(),
1922 tagger.raw(),
1923 message,
1924 force
1925 ));
1926 Ok(Binding::from_raw(&raw as *const _))
1927 }
1928 }
1929
1930 pub fn tag_annotation_create(
1938 &self,
1939 name: &str,
1940 target: &Object<'_>,
1941 tagger: &Signature<'_>,
1942 message: &str,
1943 ) -> Result<Oid, Error> {
1944 let name = CString::new(name)?;
1945 let message = CString::new(message)?;
1946 let mut raw_oid = raw::git_oid {
1947 id: [0; raw::GIT_OID_RAWSZ],
1948 };
1949 unsafe {
1950 try_call!(raw::git_tag_annotation_create(
1951 &mut raw_oid,
1952 self.raw,
1953 name,
1954 target.raw(),
1955 tagger.raw(),
1956 message
1957 ));
1958 Ok(Binding::from_raw(&raw_oid as *const _))
1959 }
1960 }
1961
1962 pub fn tag_lightweight(
1968 &self,
1969 name: &str,
1970 target: &Object<'_>,
1971 force: bool,
1972 ) -> Result<Oid, Error> {
1973 let name = CString::new(name)?;
1974 let mut raw = raw::git_oid {
1975 id: [0; raw::GIT_OID_RAWSZ],
1976 };
1977 unsafe {
1978 try_call!(raw::git_tag_create_lightweight(
1979 &mut raw,
1980 self.raw,
1981 name,
1982 target.raw(),
1983 force
1984 ));
1985 Ok(Binding::from_raw(&raw as *const _))
1986 }
1987 }
1988
1989 pub fn find_tag(&self, id: Oid) -> Result<Tag<'_>, Error> {
1991 let mut raw = ptr::null_mut();
1992 unsafe {
1993 try_call!(raw::git_tag_lookup(&mut raw, self.raw, id.raw()));
1994 Ok(Binding::from_raw(raw))
1995 }
1996 }
1997
1998 pub fn find_tag_by_prefix(&self, prefix_hash: &str) -> Result<Tag<'_>, Error> {
2000 let mut raw = ptr::null_mut();
2001 unsafe {
2002 try_call!(raw::git_tag_lookup_prefix(
2003 &mut raw,
2004 self.raw,
2005 Oid::from_str(prefix_hash)?.raw(),
2006 prefix_hash.len()
2007 ));
2008 Ok(Binding::from_raw(raw))
2009 }
2010 }
2011
2012 pub fn tag_delete(&self, name: &str) -> Result<(), Error> {
2017 let name = CString::new(name)?;
2018 unsafe {
2019 try_call!(raw::git_tag_delete(self.raw, name));
2020 Ok(())
2021 }
2022 }
2023
2024 pub fn tag_names(&self, pattern: Option<&str>) -> Result<StringArray, Error> {
2028 let mut arr = raw::git_strarray {
2029 strings: ptr::null_mut(),
2030 count: 0,
2031 };
2032 unsafe {
2033 match pattern {
2034 Some(s) => {
2035 let s = CString::new(s)?;
2036 try_call!(raw::git_tag_list_match(&mut arr, s, self.raw));
2037 }
2038 None => {
2039 try_call!(raw::git_tag_list(&mut arr, self.raw));
2040 }
2041 }
2042 Ok(Binding::from_raw(arr))
2043 }
2044 }
2045
2046 pub fn tag_foreach<T>(&self, cb: T) -> Result<(), Error>
2049 where
2050 T: FnMut(Oid, &[u8]) -> bool,
2051 {
2052 let mut data = TagForeachData {
2053 cb: Box::new(cb) as TagForeachCB<'_>,
2054 };
2055
2056 unsafe {
2057 raw::git_tag_foreach(
2058 self.raw,
2059 Some(tag_foreach_cb),
2060 (&mut data) as *mut _ as *mut _,
2061 );
2062 }
2063 Ok(())
2064 }
2065
2066 pub fn checkout_head(&self, opts: Option<&mut CheckoutBuilder<'_>>) -> Result<(), Error> {
2069 unsafe {
2070 let mut raw_opts = mem::zeroed();
2071 try_call!(raw::git_checkout_init_options(
2072 &mut raw_opts,
2073 raw::GIT_CHECKOUT_OPTIONS_VERSION
2074 ));
2075 if let Some(c) = opts {
2076 c.configure(&mut raw_opts);
2077 }
2078
2079 try_call!(raw::git_checkout_head(self.raw, &raw_opts));
2080 }
2081 Ok(())
2082 }
2083
2084 pub fn checkout_index(
2088 &self,
2089 index: Option<&mut Index>,
2090 opts: Option<&mut CheckoutBuilder<'_>>,
2091 ) -> Result<(), Error> {
2092 unsafe {
2093 let mut raw_opts = mem::zeroed();
2094 try_call!(raw::git_checkout_init_options(
2095 &mut raw_opts,
2096 raw::GIT_CHECKOUT_OPTIONS_VERSION
2097 ));
2098 if let Some(c) = opts {
2099 c.configure(&mut raw_opts);
2100 }
2101
2102 try_call!(raw::git_checkout_index(
2103 self.raw,
2104 index.map(|i| &mut *i.raw()),
2105 &raw_opts
2106 ));
2107 }
2108 Ok(())
2109 }
2110
2111 pub fn checkout_tree(
2114 &self,
2115 treeish: &Object<'_>,
2116 opts: Option<&mut CheckoutBuilder<'_>>,
2117 ) -> Result<(), Error> {
2118 unsafe {
2119 let mut raw_opts = mem::zeroed();
2120 try_call!(raw::git_checkout_init_options(
2121 &mut raw_opts,
2122 raw::GIT_CHECKOUT_OPTIONS_VERSION
2123 ));
2124 if let Some(c) = opts {
2125 c.configure(&mut raw_opts);
2126 }
2127
2128 try_call!(raw::git_checkout_tree(self.raw, &*treeish.raw(), &raw_opts));
2129 }
2130 Ok(())
2131 }
2132
2133 pub fn merge(
2142 &self,
2143 annotated_commits: &[&AnnotatedCommit<'_>],
2144 merge_opts: Option<&mut MergeOptions>,
2145 checkout_opts: Option<&mut CheckoutBuilder<'_>>,
2146 ) -> Result<(), Error> {
2147 unsafe {
2148 let mut raw_checkout_opts = mem::zeroed();
2149 try_call!(raw::git_checkout_init_options(
2150 &mut raw_checkout_opts,
2151 raw::GIT_CHECKOUT_OPTIONS_VERSION
2152 ));
2153 if let Some(c) = checkout_opts {
2154 c.configure(&mut raw_checkout_opts);
2155 }
2156
2157 let mut commit_ptrs = annotated_commits
2158 .iter()
2159 .map(|c| c.raw() as *const raw::git_annotated_commit)
2160 .collect::<Vec<_>>();
2161
2162 try_call!(raw::git_merge(
2163 self.raw,
2164 commit_ptrs.as_mut_ptr(),
2165 annotated_commits.len() as size_t,
2166 merge_opts.map(|o| o.raw()).unwrap_or(ptr::null()),
2167 &raw_checkout_opts
2168 ));
2169 }
2170 Ok(())
2171 }
2172
2173 pub fn merge_commits(
2178 &self,
2179 our_commit: &Commit<'_>,
2180 their_commit: &Commit<'_>,
2181 opts: Option<&MergeOptions>,
2182 ) -> Result<Index, Error> {
2183 let mut raw = ptr::null_mut();
2184 unsafe {
2185 try_call!(raw::git_merge_commits(
2186 &mut raw,
2187 self.raw,
2188 our_commit.raw(),
2189 their_commit.raw(),
2190 opts.map(|o| o.raw())
2191 ));
2192 Ok(Binding::from_raw(raw))
2193 }
2194 }
2195
2196 pub fn merge_trees(
2201 &self,
2202 ancestor_tree: &Tree<'_>,
2203 our_tree: &Tree<'_>,
2204 their_tree: &Tree<'_>,
2205 opts: Option<&MergeOptions>,
2206 ) -> Result<Index, Error> {
2207 let mut raw = ptr::null_mut();
2208 unsafe {
2209 try_call!(raw::git_merge_trees(
2210 &mut raw,
2211 self.raw,
2212 ancestor_tree.raw(),
2213 our_tree.raw(),
2214 their_tree.raw(),
2215 opts.map(|o| o.raw())
2216 ));
2217 Ok(Binding::from_raw(raw))
2218 }
2219 }
2220
2221 pub fn cleanup_state(&self) -> Result<(), Error> {
2224 unsafe {
2225 try_call!(raw::git_repository_state_cleanup(self.raw));
2226 }
2227 Ok(())
2228 }
2229
2230 pub fn merge_analysis(
2233 &self,
2234 their_heads: &[&AnnotatedCommit<'_>],
2235 ) -> Result<(MergeAnalysis, MergePreference), Error> {
2236 unsafe {
2237 let mut raw_merge_analysis = 0 as raw::git_merge_analysis_t;
2238 let mut raw_merge_preference = 0 as raw::git_merge_preference_t;
2239 let mut their_heads = their_heads
2240 .iter()
2241 .map(|v| v.raw() as *const _)
2242 .collect::<Vec<_>>();
2243 try_call!(raw::git_merge_analysis(
2244 &mut raw_merge_analysis,
2245 &mut raw_merge_preference,
2246 self.raw,
2247 their_heads.as_mut_ptr() as *mut _,
2248 their_heads.len()
2249 ));
2250 Ok((
2251 MergeAnalysis::from_bits_truncate(raw_merge_analysis as u32),
2252 MergePreference::from_bits_truncate(raw_merge_preference as u32),
2253 ))
2254 }
2255 }
2256
2257 pub fn merge_analysis_for_ref(
2260 &self,
2261 our_ref: &Reference<'_>,
2262 their_heads: &[&AnnotatedCommit<'_>],
2263 ) -> Result<(MergeAnalysis, MergePreference), Error> {
2264 unsafe {
2265 let mut raw_merge_analysis = 0 as raw::git_merge_analysis_t;
2266 let mut raw_merge_preference = 0 as raw::git_merge_preference_t;
2267 let mut their_heads = their_heads
2268 .iter()
2269 .map(|v| v.raw() as *const _)
2270 .collect::<Vec<_>>();
2271 try_call!(raw::git_merge_analysis_for_ref(
2272 &mut raw_merge_analysis,
2273 &mut raw_merge_preference,
2274 self.raw,
2275 our_ref.raw(),
2276 their_heads.as_mut_ptr() as *mut _,
2277 their_heads.len()
2278 ));
2279 Ok((
2280 MergeAnalysis::from_bits_truncate(raw_merge_analysis as u32),
2281 MergePreference::from_bits_truncate(raw_merge_preference as u32),
2282 ))
2283 }
2284 }
2285
2286 pub fn rebase(
2290 &self,
2291 branch: Option<&AnnotatedCommit<'_>>,
2292 upstream: Option<&AnnotatedCommit<'_>>,
2293 onto: Option<&AnnotatedCommit<'_>>,
2294 opts: Option<&mut RebaseOptions<'_>>,
2295 ) -> Result<Rebase<'_>, Error> {
2296 let mut rebase: *mut raw::git_rebase = ptr::null_mut();
2297 unsafe {
2298 try_call!(raw::git_rebase_init(
2299 &mut rebase,
2300 self.raw(),
2301 branch.map(|c| c.raw()),
2302 upstream.map(|c| c.raw()),
2303 onto.map(|c| c.raw()),
2304 opts.map(|o| o.raw()).unwrap_or(ptr::null())
2305 ));
2306
2307 Ok(Rebase::from_raw(rebase))
2308 }
2309 }
2310
2311 pub fn open_rebase(&self, opts: Option<&mut RebaseOptions<'_>>) -> Result<Rebase<'_>, Error> {
2314 let mut rebase: *mut raw::git_rebase = ptr::null_mut();
2315 unsafe {
2316 try_call!(raw::git_rebase_open(
2317 &mut rebase,
2318 self.raw(),
2319 opts.map(|o| o.raw()).unwrap_or(ptr::null())
2320 ));
2321 Ok(Rebase::from_raw(rebase))
2322 }
2323 }
2324
2325 pub fn note(
2331 &self,
2332 author: &Signature<'_>,
2333 committer: &Signature<'_>,
2334 notes_ref: Option<&str>,
2335 oid: Oid,
2336 note: &str,
2337 force: bool,
2338 ) -> Result<Oid, Error> {
2339 let notes_ref = crate::opt_cstr(notes_ref)?;
2340 let note = CString::new(note)?;
2341 let mut ret = raw::git_oid {
2342 id: [0; raw::GIT_OID_RAWSZ],
2343 };
2344 unsafe {
2345 try_call!(raw::git_note_create(
2346 &mut ret,
2347 self.raw,
2348 notes_ref,
2349 author.raw(),
2350 committer.raw(),
2351 oid.raw(),
2352 note,
2353 force
2354 ));
2355 Ok(Binding::from_raw(&ret as *const _))
2356 }
2357 }
2358
2359 pub fn note_default_ref(&self) -> Result<String, Error> {
2361 let ret = Buf::new();
2362 unsafe {
2363 try_call!(raw::git_note_default_ref(ret.raw(), self.raw));
2364 }
2365 Ok(str::from_utf8(&ret).unwrap().to_string())
2366 }
2367
2368 pub fn notes(&self, notes_ref: Option<&str>) -> Result<Notes<'_>, Error> {
2377 let notes_ref = crate::opt_cstr(notes_ref)?;
2378 let mut ret = ptr::null_mut();
2379 unsafe {
2380 try_call!(raw::git_note_iterator_new(&mut ret, self.raw, notes_ref));
2381 Ok(Binding::from_raw(ret))
2382 }
2383 }
2384
2385 pub fn find_note(&self, notes_ref: Option<&str>, id: Oid) -> Result<Note<'_>, Error> {
2392 let notes_ref = crate::opt_cstr(notes_ref)?;
2393 let mut ret = ptr::null_mut();
2394 unsafe {
2395 try_call!(raw::git_note_read(&mut ret, self.raw, notes_ref, id.raw()));
2396 Ok(Binding::from_raw(ret))
2397 }
2398 }
2399
2400 pub fn note_delete(
2407 &self,
2408 id: Oid,
2409 notes_ref: Option<&str>,
2410 author: &Signature<'_>,
2411 committer: &Signature<'_>,
2412 ) -> Result<(), Error> {
2413 let notes_ref = crate::opt_cstr(notes_ref)?;
2414 unsafe {
2415 try_call!(raw::git_note_remove(
2416 self.raw,
2417 notes_ref,
2418 author.raw(),
2419 committer.raw(),
2420 id.raw()
2421 ));
2422 Ok(())
2423 }
2424 }
2425
2426 pub fn revwalk(&self) -> Result<Revwalk<'_>, Error> {
2428 let mut raw = ptr::null_mut();
2429 unsafe {
2430 try_call!(raw::git_revwalk_new(&mut raw, self.raw()));
2431 Ok(Binding::from_raw(raw))
2432 }
2433 }
2434
2435 pub fn blame_file(
2437 &self,
2438 path: &Path,
2439 opts: Option<&mut BlameOptions>,
2440 ) -> Result<Blame<'_>, Error> {
2441 let path = path_to_repo_path(path)?;
2442 let mut raw = ptr::null_mut();
2443
2444 unsafe {
2445 try_call!(raw::git_blame_file(
2446 &mut raw,
2447 self.raw(),
2448 path,
2449 opts.map(|s| s.raw())
2450 ));
2451 Ok(Binding::from_raw(raw))
2452 }
2453 }
2454
2455 pub fn merge_base(&self, one: Oid, two: Oid) -> Result<Oid, Error> {
2457 let mut raw = raw::git_oid {
2458 id: [0; raw::GIT_OID_RAWSZ],
2459 };
2460 unsafe {
2461 try_call!(raw::git_merge_base(
2462 &mut raw,
2463 self.raw,
2464 one.raw(),
2465 two.raw()
2466 ));
2467 Ok(Binding::from_raw(&raw as *const _))
2468 }
2469 }
2470
2471 pub fn merge_base_many(&self, oids: &[Oid]) -> Result<Oid, Error> {
2505 let mut raw = raw::git_oid {
2506 id: [0; raw::GIT_OID_RAWSZ],
2507 };
2508
2509 unsafe {
2510 try_call!(raw::git_merge_base_many(
2511 &mut raw,
2512 self.raw,
2513 oids.len() as size_t,
2514 oids.as_ptr() as *const raw::git_oid
2515 ));
2516 Ok(Binding::from_raw(&raw as *const _))
2517 }
2518 }
2519
2520 pub fn merge_base_octopus(&self, oids: &[Oid]) -> Result<Oid, Error> {
2522 let mut raw = raw::git_oid {
2523 id: [0; raw::GIT_OID_RAWSZ],
2524 };
2525
2526 unsafe {
2527 try_call!(raw::git_merge_base_octopus(
2528 &mut raw,
2529 self.raw,
2530 oids.len() as size_t,
2531 oids.as_ptr() as *const raw::git_oid
2532 ));
2533 Ok(Binding::from_raw(&raw as *const _))
2534 }
2535 }
2536
2537 pub fn merge_bases(&self, one: Oid, two: Oid) -> Result<OidArray, Error> {
2539 let mut arr = raw::git_oidarray {
2540 ids: ptr::null_mut(),
2541 count: 0,
2542 };
2543 unsafe {
2544 try_call!(raw::git_merge_bases(
2545 &mut arr,
2546 self.raw,
2547 one.raw(),
2548 two.raw()
2549 ));
2550 Ok(Binding::from_raw(arr))
2551 }
2552 }
2553
2554 pub fn merge_bases_many(&self, oids: &[Oid]) -> Result<OidArray, Error> {
2556 let mut arr = raw::git_oidarray {
2557 ids: ptr::null_mut(),
2558 count: 0,
2559 };
2560 unsafe {
2561 try_call!(raw::git_merge_bases_many(
2562 &mut arr,
2563 self.raw,
2564 oids.len() as size_t,
2565 oids.as_ptr() as *const raw::git_oid
2566 ));
2567 Ok(Binding::from_raw(arr))
2568 }
2569 }
2570
2571 pub fn merge_file_from_index(
2574 &self,
2575 ancestor: &IndexEntry,
2576 ours: &IndexEntry,
2577 theirs: &IndexEntry,
2578 opts: Option<&mut MergeFileOptions>,
2579 ) -> Result<MergeFileResult, Error> {
2580 unsafe {
2581 let (ancestor, _ancestor_path) = ancestor.to_raw()?;
2582 let (ours, _ours_path) = ours.to_raw()?;
2583 let (theirs, _theirs_path) = theirs.to_raw()?;
2584
2585 let mut ret = mem::zeroed();
2586 try_call!(raw::git_merge_file_from_index(
2587 &mut ret,
2588 self.raw(),
2589 &ancestor,
2590 &ours,
2591 &theirs,
2592 opts.map(|o| o.raw()).unwrap_or(ptr::null())
2593 ));
2594 Ok(Binding::from_raw(ret))
2595 }
2596 }
2597
2598 pub fn graph_ahead_behind(&self, local: Oid, upstream: Oid) -> Result<(usize, usize), Error> {
2605 unsafe {
2606 let mut ahead: size_t = 0;
2607 let mut behind: size_t = 0;
2608 try_call!(raw::git_graph_ahead_behind(
2609 &mut ahead,
2610 &mut behind,
2611 self.raw(),
2612 local.raw(),
2613 upstream.raw()
2614 ));
2615 Ok((ahead as usize, behind as usize))
2616 }
2617 }
2618
2619 pub fn graph_descendant_of(&self, commit: Oid, ancestor: Oid) -> Result<bool, Error> {
2624 unsafe {
2625 let rv = try_call!(raw::git_graph_descendant_of(
2626 self.raw(),
2627 commit.raw(),
2628 ancestor.raw()
2629 ));
2630 Ok(rv != 0)
2631 }
2632 }
2633
2634 pub fn reflog(&self, name: &str) -> Result<Reflog, Error> {
2639 let name = CString::new(name)?;
2640 let mut ret = ptr::null_mut();
2641 unsafe {
2642 try_call!(raw::git_reflog_read(&mut ret, self.raw, name));
2643 Ok(Binding::from_raw(ret))
2644 }
2645 }
2646
2647 pub fn reflog_delete(&self, name: &str) -> Result<(), Error> {
2649 let name = CString::new(name)?;
2650 unsafe {
2651 try_call!(raw::git_reflog_delete(self.raw, name));
2652 }
2653 Ok(())
2654 }
2655
2656 pub fn reflog_rename(&self, old_name: &str, new_name: &str) -> Result<(), Error> {
2660 let old_name = CString::new(old_name)?;
2661 let new_name = CString::new(new_name)?;
2662 unsafe {
2663 try_call!(raw::git_reflog_rename(self.raw, old_name, new_name));
2664 }
2665 Ok(())
2666 }
2667
2668 pub fn reference_has_log(&self, name: &str) -> Result<bool, Error> {
2670 let name = CString::new(name)?;
2671 let ret = unsafe { try_call!(raw::git_reference_has_log(self.raw, name)) };
2672 Ok(ret != 0)
2673 }
2674
2675 pub fn reference_ensure_log(&self, name: &str) -> Result<(), Error> {
2677 let name = CString::new(name)?;
2678 unsafe {
2679 try_call!(raw::git_reference_ensure_log(self.raw, name));
2680 }
2681 Ok(())
2682 }
2683
2684 pub fn describe(&self, opts: &DescribeOptions) -> Result<Describe<'_>, Error> {
2690 let mut ret = ptr::null_mut();
2691 unsafe {
2692 try_call!(raw::git_describe_workdir(&mut ret, self.raw, opts.raw()));
2693 Ok(Binding::from_raw(ret))
2694 }
2695 }
2696
2697 pub fn diff_blobs(
2712 &self,
2713 old_blob: Option<&Blob<'_>>,
2714 old_as_path: Option<&str>,
2715 new_blob: Option<&Blob<'_>>,
2716 new_as_path: Option<&str>,
2717 opts: Option<&mut DiffOptions>,
2718 file_cb: Option<&mut FileCb<'_>>,
2719 binary_cb: Option<&mut BinaryCb<'_>>,
2720 hunk_cb: Option<&mut HunkCb<'_>>,
2721 line_cb: Option<&mut LineCb<'_>>,
2722 ) -> Result<(), Error> {
2723 let old_as_path = crate::opt_cstr(old_as_path)?;
2724 let new_as_path = crate::opt_cstr(new_as_path)?;
2725 let mut cbs = DiffCallbacks {
2726 file: file_cb,
2727 binary: binary_cb,
2728 hunk: hunk_cb,
2729 line: line_cb,
2730 };
2731 let ptr = &mut cbs as *mut _;
2732 unsafe {
2733 let file_cb_c: raw::git_diff_file_cb = if cbs.file.is_some() {
2734 Some(file_cb_c)
2735 } else {
2736 None
2737 };
2738 let binary_cb_c: raw::git_diff_binary_cb = if cbs.binary.is_some() {
2739 Some(binary_cb_c)
2740 } else {
2741 None
2742 };
2743 let hunk_cb_c: raw::git_diff_hunk_cb = if cbs.hunk.is_some() {
2744 Some(hunk_cb_c)
2745 } else {
2746 None
2747 };
2748 let line_cb_c: raw::git_diff_line_cb = if cbs.line.is_some() {
2749 Some(line_cb_c)
2750 } else {
2751 None
2752 };
2753 try_call!(raw::git_diff_blobs(
2754 old_blob.map(|s| s.raw()),
2755 old_as_path,
2756 new_blob.map(|s| s.raw()),
2757 new_as_path,
2758 opts.map(|s| s.raw()),
2759 file_cb_c,
2760 binary_cb_c,
2761 hunk_cb_c,
2762 line_cb_c,
2763 ptr as *mut _
2764 ));
2765 Ok(())
2766 }
2767 }
2768
2769 pub fn diff_tree_to_tree(
2778 &self,
2779 old_tree: Option<&Tree<'_>>,
2780 new_tree: Option<&Tree<'_>>,
2781 opts: Option<&mut DiffOptions>,
2782 ) -> Result<Diff<'_>, Error> {
2783 let mut ret = ptr::null_mut();
2784 unsafe {
2785 try_call!(raw::git_diff_tree_to_tree(
2786 &mut ret,
2787 self.raw(),
2788 old_tree.map(|s| s.raw()),
2789 new_tree.map(|s| s.raw()),
2790 opts.map(|s| s.raw())
2791 ));
2792 Ok(Binding::from_raw(ret))
2793 }
2794 }
2795
2796 pub fn diff_tree_to_index(
2810 &self,
2811 old_tree: Option<&Tree<'_>>,
2812 index: Option<&Index>,
2813 opts: Option<&mut DiffOptions>,
2814 ) -> Result<Diff<'_>, Error> {
2815 let mut ret = ptr::null_mut();
2816 unsafe {
2817 try_call!(raw::git_diff_tree_to_index(
2818 &mut ret,
2819 self.raw(),
2820 old_tree.map(|s| s.raw()),
2821 index.map(|s| s.raw()),
2822 opts.map(|s| s.raw())
2823 ));
2824 Ok(Binding::from_raw(ret))
2825 }
2826 }
2827
2828 pub fn diff_index_to_index(
2833 &self,
2834 old_index: &Index,
2835 new_index: &Index,
2836 opts: Option<&mut DiffOptions>,
2837 ) -> Result<Diff<'_>, Error> {
2838 let mut ret = ptr::null_mut();
2839 unsafe {
2840 try_call!(raw::git_diff_index_to_index(
2841 &mut ret,
2842 self.raw(),
2843 old_index.raw(),
2844 new_index.raw(),
2845 opts.map(|s| s.raw())
2846 ));
2847 Ok(Binding::from_raw(ret))
2848 }
2849 }
2850
2851 pub fn diff_index_to_workdir(
2865 &self,
2866 index: Option<&Index>,
2867 opts: Option<&mut DiffOptions>,
2868 ) -> Result<Diff<'_>, Error> {
2869 let mut ret = ptr::null_mut();
2870 unsafe {
2871 try_call!(raw::git_diff_index_to_workdir(
2872 &mut ret,
2873 self.raw(),
2874 index.map(|s| s.raw()),
2875 opts.map(|s| s.raw())
2876 ));
2877 Ok(Binding::from_raw(ret))
2878 }
2879 }
2880
2881 pub fn diff_tree_to_workdir(
2900 &self,
2901 old_tree: Option<&Tree<'_>>,
2902 opts: Option<&mut DiffOptions>,
2903 ) -> Result<Diff<'_>, Error> {
2904 let mut ret = ptr::null_mut();
2905 unsafe {
2906 try_call!(raw::git_diff_tree_to_workdir(
2907 &mut ret,
2908 self.raw(),
2909 old_tree.map(|s| s.raw()),
2910 opts.map(|s| s.raw())
2911 ));
2912 Ok(Binding::from_raw(ret))
2913 }
2914 }
2915
2916 pub fn diff_tree_to_workdir_with_index(
2923 &self,
2924 old_tree: Option<&Tree<'_>>,
2925 opts: Option<&mut DiffOptions>,
2926 ) -> Result<Diff<'_>, Error> {
2927 let mut ret = ptr::null_mut();
2928 unsafe {
2929 try_call!(raw::git_diff_tree_to_workdir_with_index(
2930 &mut ret,
2931 self.raw(),
2932 old_tree.map(|s| s.raw()),
2933 opts.map(|s| s.raw())
2934 ));
2935 Ok(Binding::from_raw(ret))
2936 }
2937 }
2938
2939 pub fn packbuilder(&self) -> Result<PackBuilder<'_>, Error> {
2941 let mut ret = ptr::null_mut();
2942 unsafe {
2943 try_call!(raw::git_packbuilder_new(&mut ret, self.raw()));
2944 Ok(Binding::from_raw(ret))
2945 }
2946 }
2947
2948 pub fn stash_save(
2950 &mut self,
2951 stasher: &Signature<'_>,
2952 message: &str,
2953 flags: Option<StashFlags>,
2954 ) -> Result<Oid, Error> {
2955 self.stash_save2(stasher, Some(message), flags)
2956 }
2957
2958 pub fn stash_save2(
2961 &mut self,
2962 stasher: &Signature<'_>,
2963 message: Option<&str>,
2964 flags: Option<StashFlags>,
2965 ) -> Result<Oid, Error> {
2966 unsafe {
2967 let mut raw_oid = raw::git_oid {
2968 id: [0; raw::GIT_OID_RAWSZ],
2969 };
2970 let message = crate::opt_cstr(message)?;
2971 let flags = flags.unwrap_or_else(StashFlags::empty);
2972 try_call!(raw::git_stash_save(
2973 &mut raw_oid,
2974 self.raw(),
2975 stasher.raw(),
2976 message,
2977 flags.bits() as c_uint
2978 ));
2979 Ok(Binding::from_raw(&raw_oid as *const _))
2980 }
2981 }
2982
2983 pub fn stash_save_ext(
2985 &mut self,
2986 opts: Option<&mut StashSaveOptions<'_>>,
2987 ) -> Result<Oid, Error> {
2988 unsafe {
2989 let mut raw_oid = raw::git_oid {
2990 id: [0; raw::GIT_OID_RAWSZ],
2991 };
2992 let opts = opts.map(|opts| opts.raw());
2993 try_call!(raw::git_stash_save_with_opts(
2994 &mut raw_oid,
2995 self.raw(),
2996 opts
2997 ));
2998 Ok(Binding::from_raw(&raw_oid as *const _))
2999 }
3000 }
3001
3002 pub fn stash_apply(
3004 &mut self,
3005 index: usize,
3006 opts: Option<&mut StashApplyOptions<'_>>,
3007 ) -> Result<(), Error> {
3008 unsafe {
3009 let opts = opts.map(|opts| opts.raw());
3010 try_call!(raw::git_stash_apply(self.raw(), index, opts));
3011 Ok(())
3012 }
3013 }
3014
3015 pub fn stash_foreach<C>(&mut self, mut callback: C) -> Result<(), Error>
3019 where
3020 C: FnMut(usize, &str, &Oid) -> bool,
3021 {
3022 unsafe {
3023 let mut data = StashCbData {
3024 callback: &mut callback,
3025 };
3026 let cb: raw::git_stash_cb = Some(stash_cb);
3027 try_call!(raw::git_stash_foreach(
3028 self.raw(),
3029 cb,
3030 &mut data as *mut _ as *mut _
3031 ));
3032 Ok(())
3033 }
3034 }
3035
3036 pub fn stash_drop(&mut self, index: usize) -> Result<(), Error> {
3038 unsafe {
3039 try_call!(raw::git_stash_drop(self.raw(), index));
3040 Ok(())
3041 }
3042 }
3043
3044 pub fn stash_pop(
3046 &mut self,
3047 index: usize,
3048 opts: Option<&mut StashApplyOptions<'_>>,
3049 ) -> Result<(), Error> {
3050 unsafe {
3051 let opts = opts.map(|opts| opts.raw());
3052 try_call!(raw::git_stash_pop(self.raw(), index, opts));
3053 Ok(())
3054 }
3055 }
3056
3057 pub fn add_ignore_rule(&self, rules: &str) -> Result<(), Error> {
3061 let rules = CString::new(rules)?;
3062 unsafe {
3063 try_call!(raw::git_ignore_add_rule(self.raw, rules));
3064 }
3065 Ok(())
3066 }
3067
3068 pub fn clear_ignore_rules(&self) -> Result<(), Error> {
3070 unsafe {
3071 try_call!(raw::git_ignore_clear_internal_rules(self.raw));
3072 }
3073 Ok(())
3074 }
3075
3076 pub fn is_path_ignored<P: AsRef<Path>>(&self, path: P) -> Result<bool, Error> {
3078 let path = util::cstring_to_repo_path(path.as_ref())?;
3079 let mut ignored: c_int = 0;
3080 unsafe {
3081 try_call!(raw::git_ignore_path_is_ignored(
3082 &mut ignored,
3083 self.raw,
3084 path
3085 ));
3086 }
3087 Ok(ignored == 1)
3088 }
3089
3090 pub fn cherrypick(
3092 &self,
3093 commit: &Commit<'_>,
3094 options: Option<&mut CherrypickOptions<'_>>,
3095 ) -> Result<(), Error> {
3096 let raw_opts = options.map(|o| o.raw());
3097 let ptr_raw_opts = match raw_opts.as_ref() {
3098 Some(v) => v,
3099 None => std::ptr::null(),
3100 };
3101 unsafe {
3102 try_call!(raw::git_cherrypick(self.raw(), commit.raw(), ptr_raw_opts));
3103
3104 Ok(())
3105 }
3106 }
3107
3108 pub fn cherrypick_commit(
3111 &self,
3112 cherrypick_commit: &Commit<'_>,
3113 our_commit: &Commit<'_>,
3114 mainline: u32,
3115 options: Option<&MergeOptions>,
3116 ) -> Result<Index, Error> {
3117 let mut ret = ptr::null_mut();
3118 unsafe {
3119 try_call!(raw::git_cherrypick_commit(
3120 &mut ret,
3121 self.raw(),
3122 cherrypick_commit.raw(),
3123 our_commit.raw(),
3124 mainline,
3125 options.map(|o| o.raw())
3126 ));
3127 Ok(Binding::from_raw(ret))
3128 }
3129 }
3130
3131 pub fn branch_remote_name(&self, refname: &str) -> Result<Buf, Error> {
3133 let refname = CString::new(refname)?;
3134 unsafe {
3135 let buf = Buf::new();
3136 try_call!(raw::git_branch_remote_name(buf.raw(), self.raw, refname));
3137 Ok(buf)
3138 }
3139 }
3140
3141 pub fn branch_upstream_name(&self, refname: &str) -> Result<Buf, Error> {
3144 let refname = CString::new(refname)?;
3145 unsafe {
3146 let buf = Buf::new();
3147 try_call!(raw::git_branch_upstream_name(buf.raw(), self.raw, refname));
3148 Ok(buf)
3149 }
3150 }
3151
3152 pub fn branch_upstream_remote(&self, refname: &str) -> Result<Buf, Error> {
3156 let refname = CString::new(refname)?;
3157 unsafe {
3158 let buf = Buf::new();
3159 try_call!(raw::git_branch_upstream_remote(
3160 buf.raw(),
3161 self.raw,
3162 refname
3163 ));
3164 Ok(buf)
3165 }
3166 }
3167
3168 pub fn branch_upstream_merge(&self, refname: &str) -> Result<Buf, Error> {
3173 let refname = CString::new(refname)?;
3174 unsafe {
3175 let buf = Buf::new();
3176 try_call!(raw::git_branch_upstream_merge(buf.raw(), self.raw, refname));
3177 Ok(buf)
3178 }
3179 }
3180
3181 pub fn apply(
3183 &self,
3184 diff: &Diff<'_>,
3185 location: ApplyLocation,
3186 options: Option<&mut ApplyOptions<'_>>,
3187 ) -> Result<(), Error> {
3188 unsafe {
3189 try_call!(raw::git_apply(
3190 self.raw,
3191 diff.raw(),
3192 location.raw(),
3193 options.map(|s| s.raw()).unwrap_or(ptr::null())
3194 ));
3195
3196 Ok(())
3197 }
3198 }
3199
3200 pub fn apply_to_tree(
3202 &self,
3203 tree: &Tree<'_>,
3204 diff: &Diff<'_>,
3205 options: Option<&mut ApplyOptions<'_>>,
3206 ) -> Result<Index, Error> {
3207 let mut ret = ptr::null_mut();
3208 unsafe {
3209 try_call!(raw::git_apply_to_tree(
3210 &mut ret,
3211 self.raw,
3212 tree.raw(),
3213 diff.raw(),
3214 options.map(|s| s.raw()).unwrap_or(ptr::null())
3215 ));
3216 Ok(Binding::from_raw(ret))
3217 }
3218 }
3219
3220 pub fn revert(
3222 &self,
3223 commit: &Commit<'_>,
3224 options: Option<&mut RevertOptions<'_>>,
3225 ) -> Result<(), Error> {
3226 let raw_opts = options.map(|o| o.raw());
3227 let ptr_raw_opts = match raw_opts.as_ref() {
3228 Some(v) => v,
3229 None => 0 as *const _,
3230 };
3231 unsafe {
3232 try_call!(raw::git_revert(self.raw(), commit.raw(), ptr_raw_opts));
3233 Ok(())
3234 }
3235 }
3236
3237 pub fn revert_commit(
3240 &self,
3241 revert_commit: &Commit<'_>,
3242 our_commit: &Commit<'_>,
3243 mainline: u32,
3244 options: Option<&MergeOptions>,
3245 ) -> Result<Index, Error> {
3246 let mut ret = ptr::null_mut();
3247 unsafe {
3248 try_call!(raw::git_revert_commit(
3249 &mut ret,
3250 self.raw(),
3251 revert_commit.raw(),
3252 our_commit.raw(),
3253 mainline,
3254 options.map(|o| o.raw())
3255 ));
3256 Ok(Binding::from_raw(ret))
3257 }
3258 }
3259
3260 pub fn worktrees(&self) -> Result<StringArray, Error> {
3262 let mut arr = raw::git_strarray {
3263 strings: ptr::null_mut(),
3264 count: 0,
3265 };
3266 unsafe {
3267 try_call!(raw::git_worktree_list(&mut arr, self.raw));
3268 Ok(Binding::from_raw(arr))
3269 }
3270 }
3271
3272 pub fn find_worktree(&self, name: &str) -> Result<Worktree, Error> {
3276 let mut raw = ptr::null_mut();
3277 let raw_name = CString::new(name)?;
3278 unsafe {
3279 try_call!(raw::git_worktree_lookup(&mut raw, self.raw, raw_name));
3280 Ok(Binding::from_raw(raw))
3281 }
3282 }
3283
3284 pub fn worktree<'a>(
3286 &'a self,
3287 name: &str,
3288 path: &Path,
3289 opts: Option<&WorktreeAddOptions<'a>>,
3290 ) -> Result<Worktree, Error> {
3291 let mut raw = ptr::null_mut();
3292 let raw_name = CString::new(name)?;
3293 let raw_path = path.into_c_string()?;
3294
3295 unsafe {
3296 try_call!(raw::git_worktree_add(
3297 &mut raw,
3298 self.raw,
3299 raw_name,
3300 raw_path,
3301 opts.map(|o| o.raw())
3302 ));
3303 Ok(Binding::from_raw(raw))
3304 }
3305 }
3306
3307 pub fn transaction<'a>(&'a self) -> Result<Transaction<'a>, Error> {
3309 let mut raw = ptr::null_mut();
3310 unsafe {
3311 try_call!(raw::git_transaction_new(&mut raw, self.raw));
3312 Ok(Binding::from_raw(raw))
3313 }
3314 }
3315
3316 pub fn mailmap(&self) -> Result<Mailmap, Error> {
3318 let mut ret = ptr::null_mut();
3319 unsafe {
3320 try_call!(raw::git_mailmap_from_repository(&mut ret, self.raw));
3321 Ok(Binding::from_raw(ret))
3322 }
3323 }
3324
3325 pub fn mergehead_foreach<C>(&mut self, mut callback: C) -> Result<(), Error>
3328 where
3329 C: FnMut(&Oid) -> bool,
3330 {
3331 unsafe {
3332 let mut data = MergeheadForeachCbData {
3333 callback: &mut callback,
3334 };
3335 let cb: raw::git_repository_mergehead_foreach_cb = Some(mergehead_foreach_cb);
3336 try_call!(raw::git_repository_mergehead_foreach(
3337 self.raw(),
3338 cb,
3339 &mut data as *mut _ as *mut _
3340 ));
3341 Ok(())
3342 }
3343 }
3344
3345 pub fn fetchhead_foreach<C>(&self, mut callback: C) -> Result<(), Error>
3354 where
3355 C: FnMut(&str, &[u8], &Oid, bool) -> bool,
3356 {
3357 unsafe {
3358 let mut data = FetchheadForeachCbData {
3359 callback: &mut callback,
3360 };
3361 let cb: raw::git_repository_fetchhead_foreach_cb = Some(fetchhead_foreach_cb);
3362 try_call!(raw::git_repository_fetchhead_foreach(
3363 self.raw(),
3364 cb,
3365 &mut data as *mut _ as *mut _
3366 ));
3367 Ok(())
3368 }
3369 }
3370}
3371
3372impl Binding for Repository {
3373 type Raw = *mut raw::git_repository;
3374 unsafe fn from_raw(ptr: *mut raw::git_repository) -> Repository {
3375 Repository { raw: ptr }
3376 }
3377 fn raw(&self) -> *mut raw::git_repository {
3378 self.raw
3379 }
3380}
3381
3382impl Drop for Repository {
3383 fn drop(&mut self) {
3384 unsafe { raw::git_repository_free(self.raw) }
3385 }
3386}
3387
3388impl RepositoryInitOptions {
3389 pub fn new() -> RepositoryInitOptions {
3394 RepositoryInitOptions {
3395 flags: raw::GIT_REPOSITORY_INIT_MKDIR as u32
3396 | raw::GIT_REPOSITORY_INIT_MKPATH as u32
3397 | raw::GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE as u32,
3398 mode: 0,
3399 workdir_path: None,
3400 description: None,
3401 template_path: None,
3402 initial_head: None,
3403 origin_url: None,
3404 }
3405 }
3406
3407 pub fn bare(&mut self, bare: bool) -> &mut RepositoryInitOptions {
3411 self.flag(raw::GIT_REPOSITORY_INIT_BARE, bare)
3412 }
3413
3414 pub fn no_reinit(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3419 self.flag(raw::GIT_REPOSITORY_INIT_NO_REINIT, enabled)
3420 }
3421
3422 pub fn no_dotgit_dir(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3428 self.flag(raw::GIT_REPOSITORY_INIT_NO_DOTGIT_DIR, enabled)
3429 }
3430
3431 pub fn mkdir(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3436 self.flag(raw::GIT_REPOSITORY_INIT_MKDIR, enabled)
3437 }
3438
3439 pub fn mkpath(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3444 self.flag(raw::GIT_REPOSITORY_INIT_MKPATH, enabled)
3445 }
3446
3447 pub fn mode(&mut self, mode: RepositoryInitMode) -> &mut RepositoryInitOptions {
3449 self.mode = mode.bits();
3450 self
3451 }
3452
3453 pub fn external_template(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3461 self.flag(raw::GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE, enabled)
3462 }
3463
3464 fn flag(
3465 &mut self,
3466 flag: raw::git_repository_init_flag_t,
3467 on: bool,
3468 ) -> &mut RepositoryInitOptions {
3469 if on {
3470 self.flags |= flag as u32;
3471 } else {
3472 self.flags &= !(flag as u32);
3473 }
3474 self
3475 }
3476
3477 pub fn workdir_path(&mut self, path: &Path) -> &mut RepositoryInitOptions {
3483 self.workdir_path = Some(path.into_c_string().unwrap());
3485 self
3486 }
3487
3488 pub fn description(&mut self, desc: &str) -> &mut RepositoryInitOptions {
3491 self.description = Some(CString::new(desc).unwrap());
3492 self
3493 }
3494
3495 pub fn template_path(&mut self, path: &Path) -> &mut RepositoryInitOptions {
3501 self.template_path = Some(path.into_c_string().unwrap());
3503 self
3504 }
3505
3506 pub fn initial_head(&mut self, head: &str) -> &mut RepositoryInitOptions {
3512 self.initial_head = Some(CString::new(head).unwrap());
3513 self
3514 }
3515
3516 pub fn origin_url(&mut self, url: &str) -> &mut RepositoryInitOptions {
3519 self.origin_url = Some(CString::new(url).unwrap());
3520 self
3521 }
3522
3523 pub unsafe fn raw(&self) -> raw::git_repository_init_options {
3529 let mut opts = mem::zeroed();
3530 assert_eq!(
3531 raw::git_repository_init_init_options(
3532 &mut opts,
3533 raw::GIT_REPOSITORY_INIT_OPTIONS_VERSION
3534 ),
3535 0
3536 );
3537 opts.flags = self.flags;
3538 opts.mode = self.mode;
3539 opts.workdir_path = crate::call::convert(&self.workdir_path);
3540 opts.description = crate::call::convert(&self.description);
3541 opts.template_path = crate::call::convert(&self.template_path);
3542 opts.initial_head = crate::call::convert(&self.initial_head);
3543 opts.origin_url = crate::call::convert(&self.origin_url);
3544 opts
3545 }
3546}
3547
3548#[cfg(test)]
3549mod tests {
3550 use crate::build::CheckoutBuilder;
3551 use crate::{CherrypickOptions, MergeFileOptions};
3552 use crate::{
3553 ObjectType, Oid, Repository, ResetType, Signature, SubmoduleIgnore, SubmoduleUpdate,
3554 };
3555 use std::ffi::OsStr;
3556 use std::fs;
3557 use std::path::Path;
3558 use tempfile::TempDir;
3559
3560 #[test]
3561 fn smoke_init() {
3562 let td = TempDir::new().unwrap();
3563 let path = td.path();
3564
3565 let repo = Repository::init(path).unwrap();
3566 assert!(!repo.is_bare());
3567 }
3568
3569 #[test]
3570 fn smoke_init_bare() {
3571 let td = TempDir::new().unwrap();
3572 let path = td.path();
3573
3574 let repo = Repository::init_bare(path).unwrap();
3575 assert!(repo.is_bare());
3576 assert!(repo.namespace().is_none());
3577 }
3578
3579 #[test]
3580 fn smoke_open() {
3581 let td = TempDir::new().unwrap();
3582 let path = td.path();
3583 Repository::init(td.path()).unwrap();
3584 let repo = Repository::open(path).unwrap();
3585 assert!(!repo.is_bare());
3586 assert!(!repo.is_shallow());
3587 assert!(repo.is_empty().unwrap());
3588 assert_eq!(
3589 crate::test::realpath(&repo.path()).unwrap(),
3590 crate::test::realpath(&td.path().join(".git/")).unwrap()
3591 );
3592 assert_eq!(repo.state(), crate::RepositoryState::Clean);
3593 }
3594
3595 #[test]
3596 fn smoke_open_bare() {
3597 let td = TempDir::new().unwrap();
3598 let path = td.path();
3599 Repository::init_bare(td.path()).unwrap();
3600
3601 let repo = Repository::open(path).unwrap();
3602 assert!(repo.is_bare());
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_checkout() {
3611 let (_td, repo) = crate::test::repo_init();
3612 repo.checkout_head(None).unwrap();
3613 }
3614
3615 #[test]
3616 fn smoke_revparse() {
3617 let (_td, repo) = crate::test::repo_init();
3618 let rev = repo.revparse("HEAD").unwrap();
3619 assert!(rev.to().is_none());
3620 let from = rev.from().unwrap();
3621 assert!(rev.from().is_some());
3622
3623 assert_eq!(repo.revparse_single("HEAD").unwrap().id(), from.id());
3624 let obj = repo.find_object(from.id(), None).unwrap().clone();
3625 obj.peel(ObjectType::Any).unwrap();
3626 obj.short_id().unwrap();
3627 repo.reset(&obj, ResetType::Hard, None).unwrap();
3628 let mut opts = CheckoutBuilder::new();
3629 t!(repo.reset(&obj, ResetType::Soft, Some(&mut opts)));
3630 }
3631
3632 #[test]
3633 fn makes_dirs() {
3634 let td = TempDir::new().unwrap();
3635 Repository::init(&td.path().join("a/b/c/d")).unwrap();
3636 }
3637
3638 #[test]
3639 fn smoke_discover() {
3640 let td = TempDir::new().unwrap();
3641 let subdir = td.path().join("subdi");
3642 fs::create_dir(&subdir).unwrap();
3643 Repository::init_bare(td.path()).unwrap();
3644 let repo = Repository::discover(&subdir).unwrap();
3645 assert_eq!(
3646 crate::test::realpath(&repo.path()).unwrap(),
3647 crate::test::realpath(&td.path().join("")).unwrap()
3648 );
3649 }
3650
3651 #[test]
3652 fn smoke_discover_path() {
3653 let td = TempDir::new().unwrap();
3654 let subdir = td.path().join("subdi");
3655 fs::create_dir(&subdir).unwrap();
3656 Repository::init_bare(td.path()).unwrap();
3657 let path = Repository::discover_path(&subdir, &[] as &[&OsStr]).unwrap();
3658 assert_eq!(
3659 crate::test::realpath(&path).unwrap(),
3660 crate::test::realpath(&td.path().join("")).unwrap()
3661 );
3662 }
3663
3664 #[test]
3665 fn smoke_discover_path_ceiling_dir() {
3666 let td = TempDir::new().unwrap();
3667 let subdir = td.path().join("subdi");
3668 fs::create_dir(&subdir).unwrap();
3669 let ceilingdir = subdir.join("ceiling");
3670 fs::create_dir(&ceilingdir).unwrap();
3671 let testdir = ceilingdir.join("testdi");
3672 fs::create_dir(&testdir).unwrap();
3673 Repository::init_bare(td.path()).unwrap();
3674 let path = Repository::discover_path(&testdir, &[ceilingdir.as_os_str()]);
3675
3676 assert!(path.is_err());
3677 }
3678
3679 #[test]
3680 fn smoke_open_ext() {
3681 let td = TempDir::new().unwrap();
3682 let subdir = td.path().join("subdir");
3683 fs::create_dir(&subdir).unwrap();
3684 Repository::init(td.path()).unwrap();
3685
3686 let repo = Repository::open_ext(
3687 &subdir,
3688 crate::RepositoryOpenFlags::empty(),
3689 &[] as &[&OsStr],
3690 )
3691 .unwrap();
3692 assert!(!repo.is_bare());
3693 assert_eq!(
3694 crate::test::realpath(&repo.path()).unwrap(),
3695 crate::test::realpath(&td.path().join(".git")).unwrap()
3696 );
3697
3698 let repo =
3699 Repository::open_ext(&subdir, crate::RepositoryOpenFlags::BARE, &[] as &[&OsStr])
3700 .unwrap();
3701 assert!(repo.is_bare());
3702 assert_eq!(
3703 crate::test::realpath(&repo.path()).unwrap(),
3704 crate::test::realpath(&td.path().join(".git")).unwrap()
3705 );
3706
3707 let err = Repository::open_ext(
3708 &subdir,
3709 crate::RepositoryOpenFlags::NO_SEARCH,
3710 &[] as &[&OsStr],
3711 )
3712 .err()
3713 .unwrap();
3714 assert_eq!(err.code(), crate::ErrorCode::NotFound);
3715
3716 assert!(
3717 Repository::open_ext(&subdir, crate::RepositoryOpenFlags::empty(), &[&subdir]).is_ok()
3718 );
3719 }
3720
3721 fn graph_repo_init() -> (TempDir, Repository) {
3722 let (_td, repo) = crate::test::repo_init();
3723 {
3724 let head = repo.head().unwrap().target().unwrap();
3725 let head = repo.find_commit(head).unwrap();
3726
3727 let mut index = repo.index().unwrap();
3728 let id = index.write_tree().unwrap();
3729
3730 let tree = repo.find_tree(id).unwrap();
3731 let sig = repo.signature().unwrap();
3732 repo.commit(Some("HEAD"), &sig, &sig, "second", &tree, &[&head])
3733 .unwrap();
3734 }
3735 (_td, repo)
3736 }
3737
3738 #[test]
3739 fn smoke_graph_ahead_behind() {
3740 let (_td, repo) = graph_repo_init();
3741 let head = repo.head().unwrap().target().unwrap();
3742 let head = repo.find_commit(head).unwrap();
3743 let head_id = head.id();
3744 let head_parent_id = head.parent(0).unwrap().id();
3745 let (ahead, behind) = repo.graph_ahead_behind(head_id, head_parent_id).unwrap();
3746 assert_eq!(ahead, 1);
3747 assert_eq!(behind, 0);
3748 let (ahead, behind) = repo.graph_ahead_behind(head_parent_id, head_id).unwrap();
3749 assert_eq!(ahead, 0);
3750 assert_eq!(behind, 1);
3751 }
3752
3753 #[test]
3754 fn smoke_graph_descendant_of() {
3755 let (_td, repo) = graph_repo_init();
3756 let head = repo.head().unwrap().target().unwrap();
3757 let head = repo.find_commit(head).unwrap();
3758 let head_id = head.id();
3759 let head_parent_id = head.parent(0).unwrap().id();
3760 assert!(repo.graph_descendant_of(head_id, head_parent_id).unwrap());
3761 assert!(!repo.graph_descendant_of(head_parent_id, head_id).unwrap());
3762 }
3763
3764 #[test]
3765 fn smoke_reference_has_log_ensure_log() {
3766 let (_td, repo) = crate::test::repo_init();
3767
3768 assert_eq!(repo.reference_has_log("HEAD").unwrap(), true);
3769 assert_eq!(repo.reference_has_log("refs/heads/main").unwrap(), true);
3770 assert_eq!(repo.reference_has_log("NOT_HEAD").unwrap(), false);
3771 let main_oid = repo.revparse_single("main").unwrap().id();
3772 assert!(repo
3773 .reference("NOT_HEAD", main_oid, false, "creating a new branch")
3774 .is_ok());
3775 assert_eq!(repo.reference_has_log("NOT_HEAD").unwrap(), false);
3776 assert!(repo.reference_ensure_log("NOT_HEAD").is_ok());
3777 assert_eq!(repo.reference_has_log("NOT_HEAD").unwrap(), true);
3778 }
3779
3780 #[test]
3781 fn smoke_set_head() {
3782 let (_td, repo) = crate::test::repo_init();
3783
3784 assert!(repo.set_head("refs/heads/does-not-exist").is_ok());
3785 assert!(repo.head().is_err());
3786
3787 assert!(repo.set_head("refs/heads/main").is_ok());
3788 assert!(repo.head().is_ok());
3789
3790 assert!(repo.set_head("*").is_err());
3791 }
3792
3793 #[test]
3794 fn smoke_set_head_bytes() {
3795 let (_td, repo) = crate::test::repo_init();
3796
3797 assert!(repo.set_head_bytes(b"refs/heads/does-not-exist").is_ok());
3798 assert!(repo.head().is_err());
3799
3800 assert!(repo.set_head_bytes(b"refs/heads/main").is_ok());
3801 assert!(repo.head().is_ok());
3802
3803 assert!(repo.set_head_bytes(b"*").is_err());
3804 }
3805
3806 #[test]
3807 fn smoke_set_head_detached() {
3808 let (_td, repo) = crate::test::repo_init();
3809
3810 let void_oid = Oid::from_bytes(b"00000000000000000000").unwrap();
3811 assert!(repo.set_head_detached(void_oid).is_err());
3812
3813 let main_oid = repo.revparse_single("main").unwrap().id();
3814 assert!(repo.set_head_detached(main_oid).is_ok());
3815 assert_eq!(repo.head().unwrap().target().unwrap(), main_oid);
3816 }
3817
3818 #[test]
3819 fn smoke_find_object_by_prefix() {
3820 let (_td, repo) = crate::test::repo_init();
3821 let head = repo.head().unwrap().target().unwrap();
3822 let head = repo.find_commit(head).unwrap();
3823 let head_id = head.id();
3824 let head_prefix = &head_id.to_string()[..7];
3825 let obj = repo.find_object_by_prefix(head_prefix, None).unwrap();
3826 assert_eq!(obj.id(), head_id);
3827 }
3828
3829 #[test]
3834 fn smoke_merge_base() {
3835 let (_td, repo) = graph_repo_init();
3836 let sig = repo.signature().unwrap();
3837
3838 let oid1 = repo.head().unwrap().target().unwrap();
3840 let commit1 = repo.find_commit(oid1).unwrap();
3841 println!("created oid1 {:?}", oid1);
3842
3843 repo.branch("branch_a", &commit1, true).unwrap();
3844 repo.branch("branch_b", &commit1, true).unwrap();
3845 repo.branch("branch_c", &commit1, true).unwrap();
3846
3847 let mut index = repo.index().unwrap();
3849 let p = Path::new(repo.workdir().unwrap()).join("file_a");
3850 println!("using path {:?}", p);
3851 fs::File::create(&p).unwrap();
3852 index.add_path(Path::new("file_a")).unwrap();
3853 let id_a = index.write_tree().unwrap();
3854 let tree_a = repo.find_tree(id_a).unwrap();
3855 let oid2 = repo
3856 .commit(
3857 Some("refs/heads/branch_a"),
3858 &sig,
3859 &sig,
3860 "commit 2",
3861 &tree_a,
3862 &[&commit1],
3863 )
3864 .unwrap();
3865 repo.find_commit(oid2).unwrap();
3866 println!("created oid2 {:?}", oid2);
3867
3868 t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
3869
3870 let mut index = repo.index().unwrap();
3872 let p = Path::new(repo.workdir().unwrap()).join("file_b");
3873 fs::File::create(&p).unwrap();
3874 index.add_path(Path::new("file_b")).unwrap();
3875 let id_b = index.write_tree().unwrap();
3876 let tree_b = repo.find_tree(id_b).unwrap();
3877 let oid3 = repo
3878 .commit(
3879 Some("refs/heads/branch_b"),
3880 &sig,
3881 &sig,
3882 "commit 3",
3883 &tree_b,
3884 &[&commit1],
3885 )
3886 .unwrap();
3887 repo.find_commit(oid3).unwrap();
3888 println!("created oid3 {:?}", oid3);
3889
3890 t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
3891
3892 let mut index = repo.index().unwrap();
3894 let p = Path::new(repo.workdir().unwrap()).join("file_c");
3895 fs::File::create(&p).unwrap();
3896 index.add_path(Path::new("file_c")).unwrap();
3897 let id_c = index.write_tree().unwrap();
3898 let tree_c = repo.find_tree(id_c).unwrap();
3899 let oid4 = repo
3900 .commit(
3901 Some("refs/heads/branch_c"),
3902 &sig,
3903 &sig,
3904 "commit 3",
3905 &tree_c,
3906 &[&commit1],
3907 )
3908 .unwrap();
3909 repo.find_commit(oid4).unwrap();
3910 println!("created oid4 {:?}", oid4);
3911
3912 let merge_base = repo.merge_base(oid2, oid3).unwrap();
3914 assert_eq!(merge_base, oid1);
3915
3916 let merge_base = repo.merge_base_many(&[oid2, oid3, oid4]).unwrap();
3918 assert_eq!(merge_base, oid1);
3919
3920 let merge_base = repo.merge_base_octopus(&[oid2, oid3, oid4]).unwrap();
3922 assert_eq!(merge_base, oid1);
3923 }
3924
3925 #[test]
3931 fn smoke_merge_bases() {
3932 let (_td, repo) = graph_repo_init();
3933 let sig = repo.signature().unwrap();
3934
3935 let oid1 = repo.head().unwrap().target().unwrap();
3937 let commit1 = repo.find_commit(oid1).unwrap();
3938 println!("created oid1 {:?}", oid1);
3939
3940 repo.branch("branch_a", &commit1, true).unwrap();
3941 repo.branch("branch_b", &commit1, true).unwrap();
3942
3943 let mut index = repo.index().unwrap();
3945 let p = Path::new(repo.workdir().unwrap()).join("file_a");
3946 println!("using path {:?}", p);
3947 fs::File::create(&p).unwrap();
3948 index.add_path(Path::new("file_a")).unwrap();
3949 let id_a = index.write_tree().unwrap();
3950 let tree_a = repo.find_tree(id_a).unwrap();
3951 let oid2 = repo
3952 .commit(
3953 Some("refs/heads/branch_a"),
3954 &sig,
3955 &sig,
3956 "commit 2",
3957 &tree_a,
3958 &[&commit1],
3959 )
3960 .unwrap();
3961 let commit2 = repo.find_commit(oid2).unwrap();
3962 println!("created oid2 {:?}", oid2);
3963
3964 t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
3965
3966 let mut index = repo.index().unwrap();
3968 let p = Path::new(repo.workdir().unwrap()).join("file_b");
3969 fs::File::create(&p).unwrap();
3970 index.add_path(Path::new("file_b")).unwrap();
3971 let id_b = index.write_tree().unwrap();
3972 let tree_b = repo.find_tree(id_b).unwrap();
3973 let oid3 = repo
3974 .commit(
3975 Some("refs/heads/branch_b"),
3976 &sig,
3977 &sig,
3978 "commit 3",
3979 &tree_b,
3980 &[&commit1],
3981 )
3982 .unwrap();
3983 let commit3 = repo.find_commit(oid3).unwrap();
3984 println!("created oid3 {:?}", oid3);
3985
3986 repo.set_head("refs/heads/branch_a").unwrap();
3989 repo.checkout_head(None).unwrap();
3990 let oid4 = repo
3991 .commit(
3992 Some("refs/heads/branch_a"),
3993 &sig,
3994 &sig,
3995 "commit 4",
3996 &tree_a,
3997 &[&commit2, &commit3],
3998 )
3999 .unwrap();
4000 println!("created oid4 {:?}", oid4);
4002
4003 repo.set_head("refs/heads/branch_b").unwrap();
4006 repo.checkout_head(None).unwrap();
4007 let oid5 = repo
4008 .commit(
4009 Some("refs/heads/branch_b"),
4010 &sig,
4011 &sig,
4012 "commit 5",
4013 &tree_a,
4014 &[&commit3, &commit2],
4015 )
4016 .unwrap();
4017 println!("created oid5 {:?}", oid5);
4019
4020 let merge_bases = repo.merge_bases(oid4, oid5).unwrap();
4022 let mut found_oid2 = false;
4023 let mut found_oid3 = false;
4024 for mg in merge_bases.iter() {
4025 println!("found merge base {:?}", mg);
4026 if mg == &oid2 {
4027 found_oid2 = true;
4028 } else if mg == &oid3 {
4029 found_oid3 = true;
4030 } else {
4031 assert!(false);
4032 }
4033 }
4034 assert!(found_oid2);
4035 assert!(found_oid3);
4036 assert_eq!(merge_bases.len(), 2);
4037
4038 let merge_bases = repo.merge_bases_many(&[oid4, oid5]).unwrap();
4040 let mut found_oid2 = false;
4041 let mut found_oid3 = false;
4042 for mg in merge_bases.iter() {
4043 println!("found merge base {:?}", mg);
4044 if mg == &oid2 {
4045 found_oid2 = true;
4046 } else if mg == &oid3 {
4047 found_oid3 = true;
4048 } else {
4049 assert!(false);
4050 }
4051 }
4052 assert!(found_oid2);
4053 assert!(found_oid3);
4054 assert_eq!(merge_bases.len(), 2);
4055 }
4056
4057 #[test]
4058 fn smoke_merge_file_from_index() {
4059 let (_td, repo) = crate::test::repo_init();
4060
4061 let head_commit = {
4062 let head = t!(repo.head()).target().unwrap();
4063 t!(repo.find_commit(head))
4064 };
4065
4066 let file_path = Path::new("file");
4067 let author = t!(Signature::now("committer", "committer@email"));
4068
4069 let base_commit = {
4070 t!(fs::write(repo.workdir().unwrap().join(&file_path), "base"));
4071 let mut index = t!(repo.index());
4072 t!(index.add_path(&file_path));
4073 let tree_id = t!(index.write_tree());
4074 let tree = t!(repo.find_tree(tree_id));
4075
4076 let commit_id = t!(repo.commit(
4077 Some("HEAD"),
4078 &author,
4079 &author,
4080 r"Add file with contents 'base'",
4081 &tree,
4082 &[&head_commit],
4083 ));
4084 t!(repo.find_commit(commit_id))
4085 };
4086
4087 let foo_commit = {
4088 t!(fs::write(repo.workdir().unwrap().join(&file_path), "foo"));
4089 let mut index = t!(repo.index());
4090 t!(index.add_path(&file_path));
4091 let tree_id = t!(index.write_tree());
4092 let tree = t!(repo.find_tree(tree_id));
4093
4094 let commit_id = t!(repo.commit(
4095 Some("refs/heads/foo"),
4096 &author,
4097 &author,
4098 r"Update file with contents 'foo'",
4099 &tree,
4100 &[&base_commit],
4101 ));
4102 t!(repo.find_commit(commit_id))
4103 };
4104
4105 let bar_commit = {
4106 t!(fs::write(repo.workdir().unwrap().join(&file_path), "bar"));
4107 let mut index = t!(repo.index());
4108 t!(index.add_path(&file_path));
4109 let tree_id = t!(index.write_tree());
4110 let tree = t!(repo.find_tree(tree_id));
4111
4112 let commit_id = t!(repo.commit(
4113 Some("refs/heads/bar"),
4114 &author,
4115 &author,
4116 r"Update file with contents 'bar'",
4117 &tree,
4118 &[&base_commit],
4119 ));
4120 t!(repo.find_commit(commit_id))
4121 };
4122
4123 let index = t!(repo.merge_commits(&foo_commit, &bar_commit, None));
4124
4125 let base = index.get_path(file_path, 1).unwrap();
4126 let ours = index.get_path(file_path, 2).unwrap();
4127 let theirs = index.get_path(file_path, 3).unwrap();
4128
4129 let mut opts = MergeFileOptions::new();
4130 opts.ancestor_label("ancestor");
4131 opts.our_label("ours");
4132 opts.their_label("theirs");
4133 opts.style_diff3(true);
4134 let merge_file_result = repo
4135 .merge_file_from_index(&base, &ours, &theirs, Some(&mut opts))
4136 .unwrap();
4137
4138 assert!(!merge_file_result.is_automergeable());
4139 assert_eq!(merge_file_result.path(), Some("file"));
4140 assert_eq!(
4141 String::from_utf8_lossy(merge_file_result.content()).to_string(),
4142 r"<<<<<<< ours
4143foo
4144||||||| ancestor
4145base
4146=======
4147bar
4148>>>>>>> theirs
4149",
4150 );
4151 }
4152
4153 #[test]
4154 fn smoke_revparse_ext() {
4155 let (_td, repo) = graph_repo_init();
4156
4157 {
4158 let short_refname = "main";
4159 let expected_refname = "refs/heads/main";
4160 let (obj, reference) = repo.revparse_ext(short_refname).unwrap();
4161 let expected_obj = repo.revparse_single(expected_refname).unwrap();
4162 assert_eq!(obj.id(), expected_obj.id());
4163 assert_eq!(reference.unwrap().name().unwrap(), expected_refname);
4164 }
4165 {
4166 let missing_refname = "refs/heads/does-not-exist";
4167 assert!(repo.revparse_ext(missing_refname).is_err());
4168 }
4169 {
4170 let (_obj, reference) = repo.revparse_ext("HEAD^").unwrap();
4171 assert!(reference.is_none());
4172 }
4173 }
4174
4175 #[test]
4176 fn smoke_is_path_ignored() {
4177 let (_td, repo) = graph_repo_init();
4178
4179 assert!(!repo.is_path_ignored(Path::new("foo")).unwrap());
4180
4181 let _ = repo.add_ignore_rule("/foo");
4182 assert!(repo.is_path_ignored(Path::new("foo")).unwrap());
4183 if cfg!(windows) {
4184 assert!(repo.is_path_ignored(Path::new("foo\\thing")).unwrap());
4185 }
4186
4187 let _ = repo.clear_ignore_rules();
4188 assert!(!repo.is_path_ignored(Path::new("foo")).unwrap());
4189 if cfg!(windows) {
4190 assert!(!repo.is_path_ignored(Path::new("foo\\thing")).unwrap());
4191 }
4192 }
4193
4194 #[test]
4195 fn smoke_cherrypick() {
4196 let (_td, repo) = crate::test::repo_init();
4197 let sig = repo.signature().unwrap();
4198
4199 let oid1 = repo.head().unwrap().target().unwrap();
4200 let commit1 = repo.find_commit(oid1).unwrap();
4201
4202 repo.branch("branch_a", &commit1, true).unwrap();
4203
4204 let mut index = repo.index().unwrap();
4206 let p1 = Path::new(repo.workdir().unwrap()).join("file_c");
4207 fs::File::create(&p1).unwrap();
4208 index.add_path(Path::new("file_c")).unwrap();
4209 let id = index.write_tree().unwrap();
4210 let tree_c = repo.find_tree(id).unwrap();
4211 let oid2 = repo
4212 .commit(
4213 Some("refs/heads/branch_a"),
4214 &sig,
4215 &sig,
4216 "commit 2",
4217 &tree_c,
4218 &[&commit1],
4219 )
4220 .unwrap();
4221 let commit2 = repo.find_commit(oid2).unwrap();
4222 println!("created oid2 {:?}", oid2);
4223 assert!(p1.exists());
4224
4225 let mut index = repo.index().unwrap();
4226 let p2 = Path::new(repo.workdir().unwrap()).join("file_d");
4227 fs::File::create(&p2).unwrap();
4228 index.add_path(Path::new("file_d")).unwrap();
4229 let id = index.write_tree().unwrap();
4230 let tree_d = repo.find_tree(id).unwrap();
4231 let oid3 = repo
4232 .commit(
4233 Some("refs/heads/branch_a"),
4234 &sig,
4235 &sig,
4236 "commit 3",
4237 &tree_d,
4238 &[&commit2],
4239 )
4240 .unwrap();
4241 let commit3 = repo.find_commit(oid3).unwrap();
4242 println!("created oid3 {:?}", oid3);
4243 assert!(p1.exists());
4244 assert!(p2.exists());
4245
4246 repo.reset(commit1.as_object(), ResetType::Hard, None)
4248 .unwrap();
4249 let mut cherrypick_opts = CherrypickOptions::new();
4250 repo.cherrypick(&commit3, Some(&mut cherrypick_opts))
4251 .unwrap();
4252 let id = repo.index().unwrap().write_tree().unwrap();
4253 let tree_d = repo.find_tree(id).unwrap();
4254 let oid4 = repo
4255 .commit(Some("HEAD"), &sig, &sig, "commit 4", &tree_d, &[&commit1])
4256 .unwrap();
4257 let commit4 = repo.find_commit(oid4).unwrap();
4258 assert_eq!(commit4.parent(0).unwrap().id(), commit1.id());
4260 assert!(!p1.exists());
4261 assert!(p2.exists());
4262 }
4263
4264 #[test]
4265 fn smoke_revert() {
4266 let (_td, repo) = crate::test::repo_init();
4267 let foo_file = Path::new(repo.workdir().unwrap()).join("foo");
4268 assert!(!foo_file.exists());
4269
4270 let (oid1, _id) = crate::test::commit(&repo);
4271 let commit1 = repo.find_commit(oid1).unwrap();
4272 t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
4273 assert!(foo_file.exists());
4274
4275 repo.revert(&commit1, None).unwrap();
4276 let id = repo.index().unwrap().write_tree().unwrap();
4277 let tree2 = repo.find_tree(id).unwrap();
4278 let sig = repo.signature().unwrap();
4279 repo.commit(Some("HEAD"), &sig, &sig, "commit 1", &tree2, &[&commit1])
4280 .unwrap();
4281 assert!(!foo_file.exists());
4283
4284 let oid2 = repo.head().unwrap().target().unwrap();
4285 let commit2 = repo.find_commit(oid2).unwrap();
4286 repo.revert(&commit2, None).unwrap();
4287 let id = repo.index().unwrap().write_tree().unwrap();
4288 let tree3 = repo.find_tree(id).unwrap();
4289 repo.commit(Some("HEAD"), &sig, &sig, "commit 2", &tree3, &[&commit2])
4290 .unwrap();
4291 assert!(foo_file.exists());
4293 }
4294
4295 #[test]
4296 fn smoke_config_write_and_read() {
4297 let (td, repo) = crate::test::repo_init();
4298
4299 let mut config = repo.config().unwrap();
4300
4301 config.set_bool("commit.gpgsign", false).unwrap();
4302
4303 let c = fs::read_to_string(td.path().join(".git").join("config")).unwrap();
4304
4305 assert!(c.contains("[commit]"));
4306 assert!(c.contains("gpgsign = false"));
4307
4308 let config = repo.config().unwrap();
4309
4310 assert!(!config.get_bool("commit.gpgsign").unwrap());
4311 }
4312
4313 #[test]
4314 fn smoke_merge_analysis_for_ref() -> Result<(), crate::Error> {
4315 let (_td, repo) = graph_repo_init();
4316
4317 let head_commit = repo.head()?.peel_to_commit()?;
4325 repo.set_head_detached(head_commit.id())?;
4326
4327 let their_branch = repo.branch("their-branch", &head_commit, false)?;
4329
4330 let mut parents_iter = head_commit.parents();
4332 let parent = parents_iter.next().unwrap();
4333 assert!(parents_iter.next().is_none());
4334
4335 let main = repo.branch("main", &parent, true)?;
4336
4337 repo.set_head(main.get().name().expect("should be utf-8"))?;
4339
4340 let (merge_analysis, _merge_preference) = repo.merge_analysis_for_ref(
4341 main.get(),
4342 &[&repo.reference_to_annotated_commit(their_branch.get())?],
4343 )?;
4344
4345 assert!(merge_analysis.contains(crate::MergeAnalysis::ANALYSIS_FASTFORWARD));
4346
4347 Ok(())
4348 }
4349
4350 #[test]
4351 fn smoke_submodule_set() -> Result<(), crate::Error> {
4352 let (td1, _repo) = crate::test::repo_init();
4353 let (td2, mut repo2) = crate::test::repo_init();
4354 let url = crate::test::path2url(td1.path());
4355 let name = "bar";
4356 {
4357 let mut s = repo2.submodule(&url, Path::new(name), true)?;
4358 fs::remove_dir_all(td2.path().join("bar")).unwrap();
4359 Repository::clone(&url, td2.path().join("bar"))?;
4360 s.add_to_index(false)?;
4361 s.add_finalize()?;
4362 }
4363
4364 repo2.submodule_set_update(name, SubmoduleUpdate::None)?;
4366 assert!(matches!(
4367 repo2.find_submodule(name)?.update_strategy(),
4368 SubmoduleUpdate::None
4369 ));
4370 repo2.submodule_set_update(name, SubmoduleUpdate::Rebase)?;
4371 assert!(matches!(
4372 repo2.find_submodule(name)?.update_strategy(),
4373 SubmoduleUpdate::Rebase
4374 ));
4375
4376 repo2.submodule_set_ignore(name, SubmoduleIgnore::Untracked)?;
4378 assert!(matches!(
4379 repo2.find_submodule(name)?.ignore_rule(),
4380 SubmoduleIgnore::Untracked
4381 ));
4382 repo2.submodule_set_ignore(name, SubmoduleIgnore::Dirty)?;
4383 assert!(matches!(
4384 repo2.find_submodule(name)?.ignore_rule(),
4385 SubmoduleIgnore::Dirty
4386 ));
4387
4388 repo2.submodule_set_url(name, "fake-url")?;
4390 assert_eq!(repo2.find_submodule(name)?.url(), Some("fake-url"));
4391
4392 repo2.submodule_set_branch(name, "fake-branch")?;
4394 assert_eq!(repo2.find_submodule(name)?.branch(), Some("fake-branch"));
4395
4396 Ok(())
4397 }
4398
4399 #[test]
4400 fn smoke_mailmap_from_repository() {
4401 let (_td, repo) = crate::test::repo_init();
4402
4403 let commit = {
4404 let head = t!(repo.head()).target().unwrap();
4405 t!(repo.find_commit(head))
4406 };
4407
4408 let author = commit.author();
4410 let committer = commit.committer();
4411 assert_eq!(author.name(), Some("name"));
4412 assert_eq!(author.email(), Some("email"));
4413 assert_eq!(committer.name(), Some("name"));
4414 assert_eq!(committer.email(), Some("email"));
4415
4416 let mailmap = t!(repo.mailmap());
4418 let mailmapped_author = t!(commit.author_with_mailmap(&mailmap));
4419 let mailmapped_committer = t!(commit.committer_with_mailmap(&mailmap));
4420 assert_eq!(mailmapped_author.name(), author.name());
4421 assert_eq!(mailmapped_author.email(), author.email());
4422 assert_eq!(mailmapped_committer.name(), committer.name());
4423 assert_eq!(mailmapped_committer.email(), committer.email());
4424
4425 let commit = {
4426 let mailmap_file = Path::new(".mailmap");
4431 let p = Path::new(repo.workdir().unwrap()).join(&mailmap_file);
4432 t!(fs::write(
4433 p,
4434 r#"
4435Author Name <author.proper@email> name <email>
4436Committer Name <committer.proper@email> <committer@email>"#,
4437 ));
4438 let mut index = t!(repo.index());
4439 t!(index.add_path(&mailmap_file));
4440 let id_mailmap = t!(index.write_tree());
4441 let tree_mailmap = t!(repo.find_tree(id_mailmap));
4442
4443 let head = t!(repo.commit(
4444 Some("HEAD"),
4445 &author,
4446 t!(&Signature::now("committer", "committer@email")),
4447 "Add mailmap",
4448 &tree_mailmap,
4449 &[&commit],
4450 ));
4451 t!(repo.find_commit(head))
4452 };
4453
4454 let author = commit.author();
4457 let committer = commit.committer();
4458 assert_ne!(author.name(), committer.name());
4459 assert_ne!(author.email(), committer.email());
4460 assert_eq!(author.name(), Some("name"));
4461 assert_eq!(author.email(), Some("email"));
4462 assert_eq!(committer.name(), Some("committer"));
4463 assert_eq!(committer.email(), Some("committer@email"));
4464
4465 let mailmap = t!(repo.mailmap());
4467 let mailmapped_author = t!(commit.author_with_mailmap(&mailmap));
4468 let mailmapped_committer = t!(commit.committer_with_mailmap(&mailmap));
4469
4470 let mm_resolve_author = t!(mailmap.resolve_signature(&author));
4471 let mm_resolve_committer = t!(mailmap.resolve_signature(&committer));
4472
4473 drop(author);
4475 drop(committer);
4476 drop(commit);
4477
4478 assert_eq!(mailmapped_author.name(), Some("Author Name"));
4480 assert_eq!(mailmapped_author.email(), Some("author.proper@email"));
4481 assert_eq!(mailmapped_committer.name(), Some("Committer Name"));
4482 assert_eq!(mailmapped_committer.email(), Some("committer.proper@email"));
4483
4484 assert_eq!(mm_resolve_author.email(), mailmapped_author.email());
4486 assert_eq!(mm_resolve_committer.email(), mailmapped_committer.email());
4487 }
4488
4489 #[test]
4490 fn smoke_find_tag_by_prefix() {
4491 let (_td, repo) = crate::test::repo_init();
4492 let head = repo.head().unwrap();
4493 let tag_oid = repo
4494 .tag(
4495 "tag",
4496 &repo
4497 .find_object(head.peel_to_commit().unwrap().id(), None)
4498 .unwrap(),
4499 &repo.signature().unwrap(),
4500 "message",
4501 false,
4502 )
4503 .unwrap();
4504 let tag = repo.find_tag(tag_oid).unwrap();
4505 let found_tag = repo
4506 .find_tag_by_prefix(&tag.id().to_string()[0..7])
4507 .unwrap();
4508 assert_eq!(tag.id(), found_tag.id());
4509 }
4510
4511 #[test]
4512 fn smoke_commondir() {
4513 let (td, repo) = crate::test::repo_init();
4514 assert_eq!(
4515 crate::test::realpath(repo.path()).unwrap(),
4516 crate::test::realpath(repo.commondir()).unwrap()
4517 );
4518
4519 let worktree = repo
4520 .worktree("test", &td.path().join("worktree"), None)
4521 .unwrap();
4522 let worktree_repo = Repository::open_from_worktree(&worktree).unwrap();
4523 assert_eq!(
4524 crate::test::realpath(repo.path()).unwrap(),
4525 crate::test::realpath(worktree_repo.commondir()).unwrap()
4526 );
4527 }
4528}