git2/
repo.rs

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
100/// An owned git repository, representing all state associated with the
101/// underlying filesystem.
102///
103/// This structure corresponds to a `git_repository` in libgit2. Many other
104/// types in git2-rs are derivative from this structure and are attached to its
105/// lifetime.
106///
107/// When a repository goes out of scope it is freed in memory but not deleted
108/// from the filesystem.
109pub struct Repository {
110    raw: *mut raw::git_repository,
111}
112
113// It is the current belief that a `Repository` can be sent among threads, or
114// even shared among threads in a mutex.
115unsafe impl Send for Repository {}
116
117/// Options which can be used to configure how a repository is initialized
118pub 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    /// Attempt to open an already-existing repository at `path`.
130    ///
131    /// The path can point to either a normal or bare repository.
132    pub fn open<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
133        crate::init();
134        // Normal file path OK (does not need Windows conversion).
135        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    /// Attempt to open an already-existing bare repository at `path`.
144    ///
145    /// The path can point to only a bare repository.
146    pub fn open_bare<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
147        crate::init();
148        // Normal file path OK (does not need Windows conversion).
149        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    /// Find and open an existing repository, respecting git environment
158    /// variables.  This acts like `open_ext` with the
159    /// [FROM_ENV](RepositoryOpenFlags::FROM_ENV) flag, but additionally respects `$GIT_DIR`.
160    /// With `$GIT_DIR` unset, this will search for a repository starting in
161    /// the current directory.
162    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    /// Find and open an existing repository, with additional options.
178    ///
179    /// If flags contains [NO_SEARCH](RepositoryOpenFlags::NO_SEARCH), the path must point
180    /// directly to a repository; otherwise, this may point to a subdirectory
181    /// of a repository, and `open_ext` will search up through parent
182    /// directories.
183    ///
184    /// If flags contains [CROSS_FS](RepositoryOpenFlags::CROSS_FS), the search through parent
185    /// directories will not cross a filesystem boundary (detected when the
186    /// stat st_dev field changes).
187    ///
188    /// If flags contains [BARE](RepositoryOpenFlags::BARE), force opening the repository as
189    /// bare even if it isn't, ignoring any working directory, and defer
190    /// loading the repository configuration for performance.
191    ///
192    /// If flags contains [NO_DOTGIT](RepositoryOpenFlags::NO_DOTGIT), don't try appending
193    /// `/.git` to `path`.
194    ///
195    /// If flags contains [FROM_ENV](RepositoryOpenFlags::FROM_ENV), `open_ext` will ignore
196    /// other flags and `ceiling_dirs`, and respect the same environment
197    /// variables git does. Note, however, that `path` overrides `$GIT_DIR`; to
198    /// respect `$GIT_DIR` as well, use `open_from_env`.
199    ///
200    /// ceiling_dirs specifies a list of paths that the search through parent
201    /// directories will stop before entering.  Use the functions in std::env
202    /// to construct or manipulate such a path list. (You can use `&[] as
203    /// &[&std::ffi::OsStr]` as an argument if there are no ceiling
204    /// directories.)
205    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        // Normal file path OK (does not need Windows conversion).
217        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    /// Attempt to open an already-existing repository from a worktree.
233    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    /// Attempt to open an already-existing repository at or above `path`
245    ///
246    /// This starts at `path` and looks up the filesystem hierarchy
247    /// until it finds a repository.
248    pub fn discover<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
249        // TODO: this diverges significantly from the libgit2 API
250        crate::init();
251        let buf = Buf::new();
252        // Normal file path OK (does not need Windows conversion).
253        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    /// Attempt to find the path to a git repo for a given path
266    ///
267    /// This starts at `path` and looks up the filesystem hierarchy
268    /// until it finds a repository, stopping if it finds a member of ceiling_dirs
269    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        // Normal file path OK (does not need Windows conversion).
277        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    /// Creates a new repository in the specified folder.
293    ///
294    /// This by default will create any necessary directories to create the
295    /// repository, and it will read any user-specified templates when creating
296    /// the repository. This behavior can be configured through `init_opts`.
297    pub fn init<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
298        Repository::init_opts(path, &RepositoryInitOptions::new())
299    }
300
301    /// Creates a new `--bare` repository in the specified folder.
302    ///
303    /// The folder must exist prior to invoking this function.
304    pub fn init_bare<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
305        Repository::init_opts(path, RepositoryInitOptions::new().bare(true))
306    }
307
308    /// Creates a new repository in the specified folder with the given options.
309    ///
310    /// See `RepositoryInitOptions` struct for more information.
311    pub fn init_opts<P: AsRef<Path>>(
312        path: P,
313        opts: &RepositoryInitOptions,
314    ) -> Result<Repository, Error> {
315        crate::init();
316        // Normal file path OK (does not need Windows conversion).
317        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    /// Clone a remote repository.
327    ///
328    /// See the `RepoBuilder` struct for more information. This function will
329    /// delegate to a fresh `RepoBuilder`
330    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    /// Clone a remote repository, initialize and update its submodules
336    /// recursively.
337    ///
338    /// This is similar to `git clone --recursive`.
339    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    /// Attempt to wrap an object database as a repository.
346    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    /// Update submodules recursively.
356    ///
357    /// Uninitialized submodules will be initialized.
358    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    /// Execute a rev-parse operation against the `spec` listed.
376    ///
377    /// The resulting revision specification is returned, or an error is
378    /// returned if one occurs.
379    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    /// Find a single object, as specified by a revision string.
396    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    /// Find a single object and intermediate reference by a revision string.
407    ///
408    /// See `man gitrevisions`, or
409    /// <http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions> for
410    /// information on the syntax accepted.
411    ///
412    /// In some cases (`@{<-n>}` or `<branchname>@{upstream}`), the expression
413    /// may point to an intermediate reference. When such expressions are being
414    /// passed in, this intermediate reference is returned.
415    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    /// Tests whether this repository is a bare repository or not.
432    pub fn is_bare(&self) -> bool {
433        unsafe { raw::git_repository_is_bare(self.raw) == 1 }
434    }
435
436    /// Tests whether this repository is a shallow clone.
437    pub fn is_shallow(&self) -> bool {
438        unsafe { raw::git_repository_is_shallow(self.raw) == 1 }
439    }
440
441    /// Tests whether this repository is a worktree.
442    pub fn is_worktree(&self) -> bool {
443        unsafe { raw::git_repository_is_worktree(self.raw) == 1 }
444    }
445
446    /// Tests whether this repository is empty.
447    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    /// Returns the path to the `.git` folder for normal repositories or the
453    /// repository itself for bare repositories.
454    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    /// Returns the path of the shared common directory for this repository.
462    ///
463    /// If the repository is bare, it is the root directory for the repository.
464    /// If the repository is a worktree, it is the parent repo's gitdir.
465    /// Otherwise, it is the gitdir.
466    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    /// Returns the current state of this repository
474    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    /// Get the path of the working directory for this repository.
502    ///
503    /// If this repository is bare, then `None` is returned.
504    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    /// Set the path to the working directory for this repository.
516    ///
517    /// If `update_link` is true, create/update the gitlink file in the workdir
518    /// and set config "core.worktree" (if workdir is not the parent of the .git
519    /// directory).
520    pub fn set_workdir(&self, path: &Path, update_gitlink: bool) -> Result<(), Error> {
521        // Normal file path OK (does not need Windows conversion).
522        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    /// Get the currently active namespace for this repository.
534    ///
535    /// If there is no namespace, or the namespace is not a valid utf8 string,
536    /// `None` is returned.
537    pub fn namespace(&self) -> Option<&str> {
538        self.namespace_bytes().and_then(|s| str::from_utf8(s).ok())
539    }
540
541    /// Get the currently active namespace for this repository as a byte array.
542    ///
543    /// If there is no namespace, `None` is returned.
544    pub fn namespace_bytes(&self) -> Option<&[u8]> {
545        unsafe { crate::opt_bytes(self, raw::git_repository_get_namespace(self.raw)) }
546    }
547
548    /// Set the active namespace for this repository.
549    pub fn set_namespace(&self, namespace: &str) -> Result<(), Error> {
550        self.set_namespace_bytes(namespace.as_bytes())
551    }
552
553    /// Set the active namespace for this repository as a byte array.
554    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    /// Remove the active namespace for this repository.
563    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    /// Retrieves the Git merge message.
571    /// Remember to remove the message when finished.
572    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    /// Remove the Git merge message.
581    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    /// List all remotes for a given repository
589    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    /// Get the information for a particular remote
601    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    /// Add a remote with the default fetch refspec to the repository's
611    /// configuration.
612    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    /// Add a remote with the provided fetch refspec to the repository's
623    /// configuration.
624    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    /// Create an anonymous remote
643    ///
644    /// Create a remote with the given URL and refspec in memory. You can use
645    /// this when you have a URL instead of a remote's name. Note that anonymous
646    /// remotes cannot be converted to persisted remotes.
647    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    /// Give a remote a new name
657    ///
658    /// All remote-tracking branches and configuration settings for the remote
659    /// are updated.
660    ///
661    /// A temporary in-memory remote cannot be given a name with this method.
662    ///
663    /// No loaded instances of the remote with the old name will change their
664    /// name or their list of refspecs.
665    ///
666    /// The returned array of strings is a list of the non-default refspecs
667    /// which cannot be renamed and are returned for further processing by the
668    /// caller.
669    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    /// Delete an existing persisted remote.
688    ///
689    /// All remote-tracking branches and configuration settings for the remote
690    /// will be removed.
691    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    /// Add a fetch refspec to the remote's configuration
700    ///
701    /// Add the given refspec to the fetch list in the configuration. No loaded
702    /// remote instances will be affected.
703    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    /// Add a push refspec to the remote's configuration.
713    ///
714    /// Add the given refspec to the push list in the configuration. No
715    /// loaded remote instances will be affected.
716    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    /// Set the remote's URL in the configuration
726    ///
727    /// Remote objects already in memory will not be affected. This assumes
728    /// the common case of a single-url remote and will otherwise return an
729    /// error.
730    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    /// Set the remote's URL for pushing in the configuration.
740    ///
741    /// Remote objects already in memory will not be affected. This assumes
742    /// the common case of a single-url remote and will otherwise return an
743    /// error.
744    ///
745    /// `None` indicates that it should be cleared.
746    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    /// Sets the current head to the specified object and optionally resets
756    /// the index and working tree to match.
757    ///
758    /// A soft reset means the head will be moved to the commit.
759    ///
760    /// A mixed reset will trigger a soft reset, plus the index will be
761    /// replaced with the content of the commit tree.
762    ///
763    /// A hard reset will trigger a mixed reset and the working directory will
764    /// be replaced with the content of the index. (Untracked and ignored files
765    /// will be left alone, however.)
766    ///
767    /// The `target` is a commit-ish to which the head should be moved to. The
768    /// object can either be a commit or a tag, but tags must be dereferenceable
769    /// to a commit.
770    ///
771    /// The `checkout` options will only be used for a hard reset.
772    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    /// Updates some entries in the index from the target commit tree.
794    ///
795    /// The scope of the updated entries is determined by the paths being
796    /// in the iterator provided.
797    ///
798    /// Passing a `None` target will result in removing entries in the index
799    /// matching the provided pathspecs.
800    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    /// Retrieve and resolve the reference pointed at by HEAD.
814    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    /// Make the repository HEAD point to the specified reference.
823    ///
824    /// If the provided reference points to a tree or a blob, the HEAD is
825    /// unaltered and an error is returned.
826    ///
827    /// If the provided reference points to a branch, the HEAD will point to
828    /// that branch, staying attached, or become attached if it isn't yet. If
829    /// the branch doesn't exist yet, no error will be returned. The HEAD will
830    /// then be attached to an unborn branch.
831    ///
832    /// Otherwise, the HEAD will be detached and will directly point to the
833    /// commit.
834    pub fn set_head(&self, refname: &str) -> Result<(), Error> {
835        self.set_head_bytes(refname.as_bytes())
836    }
837
838    /// Make the repository HEAD point to the specified reference as a byte array.
839    ///
840    /// If the provided reference points to a tree or a blob, the HEAD is
841    /// unaltered and an error is returned.
842    ///
843    /// If the provided reference points to a branch, the HEAD will point to
844    /// that branch, staying attached, or become attached if it isn't yet. If
845    /// the branch doesn't exist yet, no error will be returned. The HEAD will
846    /// then be attached to an unborn branch.
847    ///
848    /// Otherwise, the HEAD will be detached and will directly point to the
849    /// commit.
850    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    /// Determines whether the repository HEAD is detached.
859    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    /// Make the repository HEAD directly point to the commit.
871    ///
872    /// If the provided commitish cannot be found in the repository, the HEAD
873    /// is unaltered and an error is returned.
874    ///
875    /// If the provided commitish cannot be peeled into a commit, the HEAD is
876    /// unaltered and an error is returned.
877    ///
878    /// Otherwise, the HEAD will eventually be detached and will directly point
879    /// to the peeled commit.
880    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    /// Make the repository HEAD directly point to the commit.
891    ///
892    /// If the provided commitish cannot be found in the repository, the HEAD
893    /// is unaltered and an error is returned.
894    /// If the provided commitish cannot be peeled into a commit, the HEAD is
895    /// unaltered and an error is returned.
896    /// Otherwise, the HEAD will eventually be detached and will directly point
897    /// to the peeled commit.
898    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    /// Create an iterator for the repo's references
912    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    /// Create an iterator for the repo's references that match the specified
921    /// glob
922    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    /// Load all submodules for this repository and return them.
935    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    /// Gather file status information and populate the returned structure.
974    ///
975    /// Note that if a pathspec is given in the options to filter the
976    /// status, then the results from rename detection (if you enable it) may
977    /// not be accurate. To do rename detection properly, this must be called
978    /// with no pathspec so that all files can be considered.
979    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    /// Test if the ignore rules apply to a given file.
992    ///
993    /// This function checks the ignore rules to see if they would apply to the
994    /// given file. This indicates if the file would be ignored regardless of
995    /// whether the file is already in the index or committed to the repository.
996    ///
997    /// One way to think of this is if you were to do "git add ." on the
998    /// directory containing the file, would it be added or not?
999    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    /// Get file status for a single file.
1009    ///
1010    /// This tries to get status for the filename that you give. If no files
1011    /// match that name (in either the HEAD, index, or working directory), this
1012    /// returns NotFound.
1013    ///
1014    /// If the name matches multiple files (for example, if the path names a
1015    /// directory or if running on a case- insensitive filesystem and yet the
1016    /// HEAD has two entries that both match the path), then this returns
1017    /// Ambiguous because it cannot give correct results.
1018    ///
1019    /// This does not do any sort of rename detection. Renames require a set of
1020    /// targets and because of the path filtering, there is not enough
1021    /// information to check renames correctly. To check file status with rename
1022    /// detection, there is no choice but to do a full `statuses` and scan
1023    /// through looking for the path that you are interested in.
1024    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    /// Create an iterator which loops over the requested branches.
1034    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    /// Get the Index file for this repository.
1043    ///
1044    /// If a custom index has not been set, the default index for the repository
1045    /// will be returned (the one located in .git/index).
1046    ///
1047    /// **Caution**: If the [`Repository`] of this index is dropped, then this
1048    /// [`Index`] will become detached, and most methods on it will fail. See
1049    /// [`Index::open`]. Be sure the repository has a binding such as a local
1050    /// variable to keep it alive at least as long as the index.
1051    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    /// Set the Index file for this repository.
1060    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    /// Get the configuration file for this repository.
1068    ///
1069    /// If a configuration file has not been set, the default config set for the
1070    /// repository will be returned, including global and system configurations
1071    /// (if they are available).
1072    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    /// Get the value of a git attribute for a path as a string.
1081    ///
1082    /// This function will return a special string if the attribute is set to a special value.
1083    /// Interpreting the special string is discouraged. You should always use
1084    /// [`AttrValue::from_string`](crate::AttrValue::from_string) to interpret the return value
1085    /// and avoid the special string.
1086    ///
1087    /// As such, the return type of this function will probably be changed in the next major version
1088    /// to prevent interpreting the returned string without checking whether it's special.
1089    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    /// Get the value of a git attribute for a path as a byte slice.
1101    ///
1102    /// This function will return a special byte slice if the attribute is set to a special value.
1103    /// Interpreting the special byte slice is discouraged. You should always use
1104    /// [`AttrValue::from_bytes`](crate::AttrValue::from_bytes) to interpret the return value and
1105    /// avoid the special string.
1106    ///
1107    /// As such, the return type of this function will probably be changed in the next major version
1108    /// to prevent interpreting the returned byte slice without checking whether it's special.
1109    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    /// Write an in-memory buffer to the ODB as a blob.
1131    ///
1132    /// The Oid returned can in turn be passed to `find_blob` to get a handle to
1133    /// the blob.
1134    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    /// Read a file from the filesystem and write its content to the Object
1152    /// Database as a loose blob
1153    ///
1154    /// The Oid returned can in turn be passed to `find_blob` to get a handle to
1155    /// the blob.
1156    pub fn blob_path(&self, path: &Path) -> Result<Oid, Error> {
1157        // Normal file path OK (does not need Windows conversion).
1158        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    /// Create a stream to write blob
1169    ///
1170    /// This function may need to buffer the data on disk and will in general
1171    /// not be the right choice if you know the size of the data to write.
1172    ///
1173    /// Use `BlobWriter::commit()` to commit the write to the object db
1174    /// and get the object id.
1175    ///
1176    /// If the `hintpath` parameter is filled, it will be used to determine
1177    /// what git filters should be applied to the object before it is written
1178    /// to the object database.
1179    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    /// Lookup a reference to one of the objects in a repository.
1196    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    /// Get the object database for this repository
1205    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    /// Override the object database for this repository
1214    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    /// Create a new branch pointing at a target commit
1222    ///
1223    /// A new direct reference will be created pointing to this target commit.
1224    /// If `force` is true and a reference already exists with the given name,
1225    /// it'll be replaced.
1226    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    /// Create a new branch pointing at a target commit
1247    ///
1248    /// This behaves like `Repository::branch()` but takes
1249    /// an annotated commit, which lets you specify which
1250    /// extended SHA syntax string was specified by a user,
1251    /// allowing for more exact reflog messages.
1252    ///
1253    /// See the documentation for `Repository::branch()`
1254    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    /// Lookup a branch by its name in a repository.
1275    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    /// Create new commit in the repository
1290    ///
1291    /// If the `update_ref` is not `None`, name of the reference that will be
1292    /// updated to point to this commit. If the reference is not direct, it will
1293    /// be resolved to a direct reference. Use "HEAD" to update the HEAD of the
1294    /// current branch and make it point to this commit. If the reference
1295    /// doesn't exist yet, it will be created. If it does exist, the first
1296    /// parent must be the tip of this branch.
1297    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    /// Create a commit object and return that as a Buf.
1333    ///
1334    /// That can be converted to a string like this `str::from_utf8(&buf).unwrap().to_string()`.
1335    /// And that string can be passed to the `commit_signed` function,
1336    /// the arguments behave the same as in the `commit` function.
1337    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    /// Create a commit object from the given buffer and signature
1368    ///
1369    /// Given the unsigned commit object's contents, its signature and the
1370    /// header field in which to store the signature, attach the signature to
1371    /// the commit and write it into the given repository.
1372    ///
1373    /// Use `None` in `signature_field` to use the default of `gpgsig`, which is
1374    /// almost certainly what you want.
1375    ///
1376    /// Returns the resulting (signed) commit id.
1377    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    /// Extract the signature from a commit
1402    ///
1403    /// Returns a tuple containing the signature in the first value and the
1404    /// signed data in the second.
1405    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    /// Lookup a reference to one of the commits in a repository.
1426    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    /// Lookup a reference to one of the commits in a repository by short hash.
1435    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    /// Creates an `AnnotatedCommit` from the given commit id.
1449    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    /// Lookup a reference to one of the objects in a repository.
1462    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    /// Lookup a reference to one of the objects by id prefix in a repository.
1476    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    /// Create a new direct reference.
1495    ///
1496    /// This function will return an error if a reference already exists with
1497    /// the given name unless force is true, in which case it will be
1498    /// overwritten.
1499    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    /// Conditionally create new direct reference.
1523    ///
1524    /// A direct reference (also called an object id reference) refers directly
1525    /// to a specific object id (a.k.a. OID or SHA) in the repository.  The id
1526    /// permanently refers to the object (although the reference itself can be
1527    /// moved).  For example, in libgit2 the direct ref "refs/tags/v0.17.0"
1528    /// refers to OID 5b9fac39d8a76b9139667c26a63e6b3f204b3977.
1529    ///
1530    /// The direct reference will be created in the repository and written to
1531    /// the disk.
1532    ///
1533    /// Valid reference names must follow one of two patterns:
1534    ///
1535    /// 1. Top-level names must contain only capital letters and underscores,
1536    ///    and must begin and end with a letter.  (e.g.  "HEAD", "ORIG_HEAD").
1537    /// 2. Names prefixed with "refs/" can be almost anything.  You must avoid
1538    ///    the characters `~`, `^`, `:`, `\\`, `?`, `[`, and `*`, and the
1539    ///    sequences ".." and "@{" which have special meaning to revparse.
1540    ///
1541    /// This function will return an error if a reference already exists with
1542    /// the given name unless `force` is true, in which case it will be
1543    /// overwritten.
1544    ///
1545    /// The message for the reflog will be ignored if the reference does not
1546    /// belong in the standard set (HEAD, branches and remote-tracking
1547    /// branches) and it does not have a reflog.
1548    ///
1549    /// It will return GIT_EMODIFIED if the reference's value at the time of
1550    /// updating does not match the one passed through `current_id` (i.e. if the
1551    /// ref has changed since the user read it).
1552    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    /// Create a new symbolic reference.
1578    ///
1579    /// A symbolic reference is a reference name that refers to another
1580    /// reference name.  If the other name moves, the symbolic name will move,
1581    /// too.  As a simple example, the "HEAD" reference might refer to
1582    /// "refs/heads/master" while on the "master" branch of a repository.
1583    ///
1584    /// Valid reference names must follow one of two patterns:
1585    ///
1586    /// 1. Top-level names must contain only capital letters and underscores,
1587    ///    and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
1588    /// 2. Names prefixed with "refs/" can be almost anything.  You must avoid
1589    ///    the characters '~', '^', ':', '\\', '?', '[', and '*', and the
1590    ///    sequences ".." and "@{" which have special meaning to revparse.
1591    ///
1592    /// This function will return an error if a reference already exists with
1593    /// the given name unless force is true, in which case it will be
1594    /// overwritten.
1595    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    /// Create a new symbolic reference.
1620    ///
1621    /// This function will return an error if a reference already exists with
1622    /// the given name unless force is true, in which case it will be
1623    /// overwritten.
1624    ///
1625    /// It will return GIT_EMODIFIED if the reference's value at the time of
1626    /// updating does not match the one passed through current_value (i.e. if
1627    /// the ref has changed since the user read it).
1628    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    /// Lookup a reference to one of the objects in a repository.
1656    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    /// Lookup a reference to one of the objects in a repository.
1666    /// `Repository::find_reference` with teeth; give the method your reference in
1667    /// human-readable format e.g. 'main' instead of 'refs/heads/main', and it
1668    /// will do-what-you-mean, returning the `Reference`.
1669    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    /// Lookup a reference by name and resolve immediately to OID.
1679    ///
1680    /// This function provides a quick way to resolve a reference name straight
1681    /// through to the object id that it refers to. This avoids having to
1682    /// allocate or free any `Reference` objects for simple situations.
1683    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    /// Creates a git_annotated_commit from the given reference.
1695    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    /// Creates a git_annotated_commit from FETCH_HEAD.
1711    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    /// Create a new action signature with default user and now timestamp.
1734    ///
1735    /// This looks up the user.name and user.email from the configuration and
1736    /// uses the current time as the timestamp, and creates a new signature
1737    /// based on that information. It will return `NotFound` if either the
1738    /// user.name or user.email are not set.
1739    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    /// Set up a new git submodule for checkout.
1748    ///
1749    /// This does "git submodule add" up to the fetch and checkout of the
1750    /// submodule contents. It preps a new submodule, creates an entry in
1751    /// `.gitmodules` and creates an empty initialized repository either at the
1752    /// given path in the working directory or in `.git/modules` with a gitlink
1753    /// from the working directory to the new repo.
1754    ///
1755    /// To fully emulate "git submodule add" call this function, then `open()`
1756    /// the submodule repo and perform the clone step as needed. Lastly, call
1757    /// `add_finalize()` to wrap up adding the new submodule and `.gitmodules`
1758    /// to the index to be ready to commit.
1759    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    /// Lookup submodule information by name or path.
1781    ///
1782    /// Given either the submodule name or path (they are usually the same),
1783    /// this returns a structure describing the submodule.
1784    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    /// Get the status for a submodule.
1794    ///
1795    /// This looks at a submodule and tries to determine the status.  It
1796    /// will return a combination of the `SubmoduleStatus` values.
1797    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    /// Set the ignore rule for the submodule in the configuration
1811    ///
1812    /// This does not affect any currently-loaded instances.
1813    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    /// Set the update rule for the submodule in the configuration
1826    ///
1827    /// This setting won't affect any existing instances.
1828    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    /// Set the URL for the submodule in the configuration
1841    ///
1842    /// After calling this, you may wish to call [`Submodule::sync`] to write
1843    /// the changes to the checked out submodule repository.
1844    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    /// Set the branch for the submodule in the configuration
1854    ///
1855    /// After calling this, you may wish to call [`Submodule::sync`] to write
1856    /// the changes to the checked out submodule repository.
1857    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    /// Lookup a reference to one of the objects in a repository.
1867    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    /// Create a new TreeBuilder, optionally initialized with the
1876    /// entries of the given Tree.
1877    ///
1878    /// The tree builder can be used to create or modify trees in memory and
1879    /// write them as tree objects to the database.
1880    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    /// Create a new tag in the repository from an object
1893    ///
1894    /// A new reference will also be created pointing to this tag object. If
1895    /// `force` is true and a reference already exists with the given name,
1896    /// it'll be replaced.
1897    ///
1898    /// The message will not be cleaned up.
1899    ///
1900    /// The tag name will be checked for validity. You must avoid the characters
1901    /// '~', '^', ':', ' \ ', '?', '[', and '*', and the sequences ".." and " @
1902    /// {" which have special meaning to revparse.
1903    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    /// Create a new tag in the repository from an object without creating a reference.
1931    ///
1932    /// The message will not be cleaned up.
1933    ///
1934    /// The tag name will be checked for validity. You must avoid the characters
1935    /// '~', '^', ':', ' \ ', '?', '[', and '*', and the sequences ".." and " @
1936    /// {" which have special meaning to revparse.
1937    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    /// Create a new lightweight tag pointing at a target object
1963    ///
1964    /// A new direct reference will be created pointing to this target object.
1965    /// If force is true and a reference already exists with the given name,
1966    /// it'll be replaced.
1967    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    /// Lookup a tag object from the repository.
1990    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    /// Lookup a tag object by prefix hash from the repository.
1999    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    /// Delete an existing tag reference.
2013    ///
2014    /// The tag name will be checked for validity, see `tag` for some rules
2015    /// about valid names.
2016    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    /// Get a list with all the tags in the repository.
2025    ///
2026    /// An optional fnmatch pattern can also be specified.
2027    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    /// iterate over all tags calling `cb` on each.
2047    /// the callback is provided the tag id and name
2048    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    /// Updates files in the index and the working tree to match the content of
2067    /// the commit pointed at by HEAD.
2068    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    /// Updates files in the working tree to match the content of the index.
2085    ///
2086    /// If the index is `None`, the repository's index will be used.
2087    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    /// Updates files in the index and working tree to match the content of the
2112    /// tree pointed at by the treeish.
2113    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    /// Merges the given commit(s) into HEAD, writing the results into the
2134    /// working directory. Any changes are staged for commit and any conflicts
2135    /// are written to the index. Callers should inspect the repository's index
2136    /// after this completes, resolve any conflicts and prepare a commit.
2137    ///
2138    /// For compatibility with git, the repository is put into a merging state.
2139    /// Once the commit is done (or if the user wishes to abort), you should
2140    /// clear this state by calling [`cleanup_state()`][Repository::cleanup_state].
2141    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    /// Merge two commits, producing an index that reflects the result of
2174    /// the merge. The index may be written as-is to the working directory or
2175    /// checked out. If the index is to be converted to a tree, the caller
2176    /// should resolve any conflicts that arose as part of the merge.
2177    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    /// Merge two trees, producing an index that reflects the result of
2197    /// the merge. The index may be written as-is to the working directory or
2198    /// checked out. If the index is to be converted to a tree, the caller
2199    /// should resolve any conflicts that arose as part of the merge.
2200    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    /// Remove all the metadata associated with an ongoing command like merge,
2222    /// revert, cherry-pick, etc. For example: MERGE_HEAD, MERGE_MSG, etc.
2223    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    /// Analyzes the given branch(es) and determines the opportunities for
2231    /// merging them into the HEAD of the repository.
2232    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    /// Analyzes the given branch(es) and determines the opportunities for
2258    /// merging them into a reference.
2259    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    /// Initializes a rebase operation to rebase the changes in `branch`
2287    /// relative to `upstream` onto another branch. To begin the rebase process,
2288    /// call `next()`.
2289    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    /// Opens an existing rebase that was previously started by either an
2312    /// invocation of `rebase()` or by another client.
2313    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    /// Add a note for an object
2326    ///
2327    /// The `notes_ref` argument is the canonical name of the reference to use,
2328    /// defaulting to "refs/notes/commits". If `force` is specified then
2329    /// previous notes are overwritten.
2330    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    /// Get the default notes reference for this repository
2360    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    /// Creates a new iterator for notes in this repository.
2369    ///
2370    /// The `notes_ref` argument is the canonical name of the reference to use,
2371    /// defaulting to "refs/notes/commits".
2372    ///
2373    /// The iterator returned yields pairs of (Oid, Oid) where the first element
2374    /// is the id of the note and the second id is the id the note is
2375    /// annotating.
2376    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    /// Read the note for an object.
2386    ///
2387    /// The `notes_ref` argument is the canonical name of the reference to use,
2388    /// defaulting to "refs/notes/commits".
2389    ///
2390    /// The id specified is the Oid of the git object to read the note from.
2391    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    /// Remove the note for an object.
2401    ///
2402    /// The `notes_ref` argument is the canonical name of the reference to use,
2403    /// defaulting to "refs/notes/commits".
2404    ///
2405    /// The id specified is the Oid of the git object to remove the note from.
2406    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    /// Create a revwalk that can be used to traverse the commit graph.
2427    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    /// Get the blame for a single file.
2436    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    /// Find a merge base between two commits
2456    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    /// Find a merge base given a list of commits
2472    ///
2473    /// This behaves similar to [`git merge-base`](https://git-scm.com/docs/git-merge-base#_discussion).
2474    /// Given three commits `a`, `b`, and `c`, `merge_base_many(&[a, b, c])`
2475    /// will compute a hypothetical commit `m`, which is a merge between `b`
2476    /// and `c`.
2477    ///
2478    /// For example, with the following topology:
2479    /// ```text
2480    ///        o---o---o---o---C
2481    ///       /
2482    ///      /   o---o---o---B
2483    ///     /   /
2484    /// ---2---1---o---o---o---A
2485    /// ```
2486    ///
2487    /// the result of `merge_base_many(&[a, b, c])` is 1. This is because the
2488    /// equivalent topology with a merge commit `m` between `b` and `c` would
2489    /// is:
2490    /// ```text
2491    ///        o---o---o---o---o
2492    ///       /                 \
2493    ///      /   o---o---o---o---M
2494    ///     /   /
2495    /// ---2---1---o---o---o---A
2496    /// ```
2497    ///
2498    /// and the result of `merge_base_many(&[a, m])` is 1.
2499    ///
2500    /// ---
2501    ///
2502    /// If you're looking to recieve the common merge base between all the
2503    /// given commits, use [`Self::merge_base_octopus`].
2504    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    /// Find a common merge base between all given a list of commits
2521    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    /// Find all merge bases between two commits
2538    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    /// Find all merge bases given a list of commits
2555    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    /// Merge two files as they exist in the index, using the given common ancestor
2572    /// as the baseline.
2573    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    /// Count the number of unique commits between two commit objects
2599    ///
2600    /// There is no need for branches containing the commits to have any
2601    /// upstream relationship, but it helps to think of one as a branch and the
2602    /// other as its upstream, the ahead and behind values will be what git
2603    /// would report for the branches.
2604    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    /// Determine if a commit is the descendant of another commit
2620    ///
2621    /// Note that a commit is not considered a descendant of itself, in contrast
2622    /// to `git merge-base --is-ancestor`.
2623    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    /// Read the reflog for the given reference
2635    ///
2636    /// If there is no reflog file for the given reference yet, an empty reflog
2637    /// object will be returned.
2638    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    /// Delete the reflog for the given reference
2648    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    /// Rename a reflog
2657    ///
2658    /// The reflog to be renamed is expected to already exist.
2659    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    /// Check if the given reference has a reflog.
2669    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    /// Ensure that the given reference has a reflog.
2676    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    /// Describes a commit
2685    ///
2686    /// Performs a describe operation on the current commit and the worktree.
2687    /// After performing a describe on HEAD, a status is run and description is
2688    /// considered to be dirty if there are.
2689    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    /// Directly run a diff on two blobs.
2698    ///
2699    /// Compared to a file, a blob lacks some contextual information. As such, the
2700    /// `DiffFile` given to the callback will have some fake data; i.e. mode will be
2701    /// 0 and path will be `None`.
2702    ///
2703    /// `None` is allowed for either `old_blob` or `new_blob` and will be treated
2704    /// as an empty blob, with the oid set to zero in the `DiffFile`. Passing `None`
2705    /// for both blobs is a noop; no callbacks will be made at all.
2706    ///
2707    /// We do run a binary content check on the blob content and if either blob looks
2708    /// like binary data, the `DiffFile` binary attribute will be set to 1 and no call to
2709    /// the `hunk_cb` nor `line_cb` will be made (unless you set the `force_text`
2710    /// option).
2711    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    /// Create a diff with the difference between two tree objects.
2770    ///
2771    /// This is equivalent to `git diff <old-tree> <new-tree>`
2772    ///
2773    /// The first tree will be used for the "old_file" side of the delta and the
2774    /// second tree will be used for the "new_file" side of the delta.  You can
2775    /// pass `None` to indicate an empty tree, although it is an error to pass
2776    /// `None` for both the `old_tree` and `new_tree`.
2777    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    /// Create a diff between a tree and repository index.
2797    ///
2798    /// This is equivalent to `git diff --cached <treeish>` or if you pass
2799    /// the HEAD tree, then like `git diff --cached`.
2800    ///
2801    /// The tree you pass will be used for the "old_file" side of the delta, and
2802    /// the index will be used for the "new_file" side of the delta.
2803    ///
2804    /// If you pass `None` for the index, then the existing index of the `repo`
2805    /// will be used.  In this case, the index will be refreshed from disk
2806    /// (if it has changed) before the diff is generated.
2807    ///
2808    /// If the tree is `None`, then it is considered an empty tree.
2809    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    /// Create a diff between two index objects.
2829    ///
2830    /// The first index will be used for the "old_file" side of the delta, and
2831    /// the second index will be used for the "new_file" side of the delta.
2832    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    /// Create a diff between the repository index and the workdir directory.
2852    ///
2853    /// This matches the `git diff` command.  See the note below on
2854    /// `tree_to_workdir` for a discussion of the difference between
2855    /// `git diff` and `git diff HEAD` and how to emulate a `git diff <treeish>`
2856    /// using libgit2.
2857    ///
2858    /// The index will be used for the "old_file" side of the delta, and the
2859    /// working directory will be used for the "new_file" side of the delta.
2860    ///
2861    /// If you pass `None` for the index, then the existing index of the `repo`
2862    /// will be used.  In this case, the index will be refreshed from disk
2863    /// (if it has changed) before the diff is generated.
2864    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    /// Create a diff between a tree and the working directory.
2882    ///
2883    /// The tree you provide will be used for the "old_file" side of the delta,
2884    /// and the working directory will be used for the "new_file" side.
2885    ///
2886    /// This is not the same as `git diff <treeish>` or `git diff-index
2887    /// <treeish>`.  Those commands use information from the index, whereas this
2888    /// function strictly returns the differences between the tree and the files
2889    /// in the working directory, regardless of the state of the index.  Use
2890    /// `tree_to_workdir_with_index` to emulate those commands.
2891    ///
2892    /// To see difference between this and `tree_to_workdir_with_index`,
2893    /// consider the example of a staged file deletion where the file has then
2894    /// been put back into the working dir and further modified.  The
2895    /// tree-to-workdir diff for that file is 'modified', but `git diff` would
2896    /// show status 'deleted' since there is a staged delete.
2897    ///
2898    /// If `None` is passed for `tree`, then an empty tree is used.
2899    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    /// Create a diff between a tree and the working directory using index data
2917    /// to account for staged deletes, tracked files, etc.
2918    ///
2919    /// This emulates `git diff <tree>` by diffing the tree to the index and
2920    /// the index to the working directory and blending the results into a
2921    /// single diff that includes staged deleted, etc.
2922    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    /// Create a PackBuilder
2940    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    /// Save the local modifications to a new stash.
2949    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    /// Save the local modifications to a new stash.
2959    /// unlike `stash_save` it allows to pass a null `message`
2960    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    /// Like `stash_save` but with more options like selective statshing via path patterns.
2984    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    /// Apply a single stashed state from the stash list.
3003    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    /// Loop over all the stashed states and issue a callback for each one.
3016    ///
3017    /// Return `true` to continue iterating or `false` to stop.
3018    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    /// Remove a single stashed state from the stash list.
3037    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    /// Apply a single stashed state from the stash list and remove it from the list if successful.
3045    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    /// Add ignore rules for a repository.
3058    ///
3059    /// The format of the rules is the same one of the .gitignore file.
3060    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    /// Clear ignore rules that were explicitly added.
3069    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    /// Test if the ignore rules apply to a given path.
3077    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    /// Perform a cherrypick
3091    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    /// Create an index of uncommitted changes, representing the result of
3109    /// cherry-picking.
3110    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    /// Find the remote name of a remote-tracking branch
3132    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    /// Retrieves the name of the reference supporting the remote tracking branch,
3142    /// given the name of a local branch reference.
3143    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    /// Retrieve the name of the upstream remote of a local branch.
3153    ///
3154    /// `refname` must be in the form `refs/heads/{branch_name}`
3155    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    /// Retrieve the upstream merge of a local branch,
3169    /// configured in "branch.*.merge"
3170    ///
3171    /// `refname` must be in the form `refs/heads/{branch_name}`
3172    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    /// Apply a Diff to the given repo, making changes directly in the working directory, the index, or both.
3182    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    /// Apply a Diff to the provided tree, and return the resulting Index.
3201    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    /// Reverts the given commit, producing changes in the index and working directory.
3221    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    /// Reverts the given commit against the given "our" commit,
3238    /// producing an index that reflects the result of the revert.
3239    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    /// Lists all the worktrees for the repository
3261    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    /// Opens a worktree by name for the given repository
3273    ///
3274    /// This can open any worktree that the worktrees method returns.
3275    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    /// Creates a new worktree for the repository
3285    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    /// Create a new transaction
3308    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    /// Gets this repository's mailmap.
3317    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    ///  If a merge is in progress, invoke 'callback' for each commit ID in the
3326    ///  MERGE_HEAD file.
3327    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    /// Invoke 'callback' for each entry in the given FETCH_HEAD file.
3346    ///
3347    /// `callback` will be called with with following arguments:
3348    ///
3349    /// - `&str`: the reference name
3350    /// - `&[u8]`: the remote URL
3351    /// - `&Oid`: the reference target OID
3352    /// - `bool`: was the reference the result of a merge
3353    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    /// Creates a default set of initialization options.
3390    ///
3391    /// By default this will set flags for creating all necessary directories
3392    /// and initializing a directory from the user-configured templates path.
3393    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    /// Create a bare repository with no working directory.
3408    ///
3409    /// Defaults to false.
3410    pub fn bare(&mut self, bare: bool) -> &mut RepositoryInitOptions {
3411        self.flag(raw::GIT_REPOSITORY_INIT_BARE, bare)
3412    }
3413
3414    /// Return an error if the repository path appears to already be a git
3415    /// repository.
3416    ///
3417    /// Defaults to false.
3418    pub fn no_reinit(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3419        self.flag(raw::GIT_REPOSITORY_INIT_NO_REINIT, enabled)
3420    }
3421
3422    /// Normally a '/.git/' will be appended to the repo path for non-bare repos
3423    /// (if it is not already there), but passing this flag prevents that
3424    /// behavior.
3425    ///
3426    /// Defaults to false.
3427    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    /// Make the repo path (and workdir path) as needed. The ".git" directory
3432    /// will always be created regardless of this flag.
3433    ///
3434    /// Defaults to true.
3435    pub fn mkdir(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3436        self.flag(raw::GIT_REPOSITORY_INIT_MKDIR, enabled)
3437    }
3438
3439    /// Recursively make all components of the repo and workdir path as
3440    /// necessary.
3441    ///
3442    /// Defaults to true.
3443    pub fn mkpath(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3444        self.flag(raw::GIT_REPOSITORY_INIT_MKPATH, enabled)
3445    }
3446
3447    /// Set to one of the `RepositoryInit` constants, or a custom value.
3448    pub fn mode(&mut self, mode: RepositoryInitMode) -> &mut RepositoryInitOptions {
3449        self.mode = mode.bits();
3450        self
3451    }
3452
3453    /// Enable or disable using external templates.
3454    ///
3455    /// If enabled, then the `template_path` option will be queried first, then
3456    /// `init.templatedir` from the global config, and finally
3457    /// `/usr/share/git-core-templates` will be used (if it exists).
3458    ///
3459    /// Defaults to true.
3460    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    /// The path to the working directory.
3478    ///
3479    /// If this is a relative path it will be evaluated relative to the repo
3480    /// path. If this is not the "natural" working directory, a .git gitlink
3481    /// file will be created here linking to the repo path.
3482    pub fn workdir_path(&mut self, path: &Path) -> &mut RepositoryInitOptions {
3483        // Normal file path OK (does not need Windows conversion).
3484        self.workdir_path = Some(path.into_c_string().unwrap());
3485        self
3486    }
3487
3488    /// If set, this will be used to initialize the "description" file in the
3489    /// repository instead of using the template content.
3490    pub fn description(&mut self, desc: &str) -> &mut RepositoryInitOptions {
3491        self.description = Some(CString::new(desc).unwrap());
3492        self
3493    }
3494
3495    /// When the `external_template` option is set, this is the first location
3496    /// to check for the template directory.
3497    ///
3498    /// If this is not configured, then the default locations will be searched
3499    /// instead.
3500    pub fn template_path(&mut self, path: &Path) -> &mut RepositoryInitOptions {
3501        // Normal file path OK (does not need Windows conversion).
3502        self.template_path = Some(path.into_c_string().unwrap());
3503        self
3504    }
3505
3506    /// The name of the head to point HEAD at.
3507    ///
3508    /// If not configured, this will be taken from your git configuration.
3509    /// If this begins with `refs/` it will be used verbatim;
3510    /// otherwise `refs/heads/` will be prefixed
3511    pub fn initial_head(&mut self, head: &str) -> &mut RepositoryInitOptions {
3512        self.initial_head = Some(CString::new(head).unwrap());
3513        self
3514    }
3515
3516    /// If set, then after the rest of the repository initialization is
3517    /// completed an `origin` remote will be added pointing to this URL.
3518    pub fn origin_url(&mut self, url: &str) -> &mut RepositoryInitOptions {
3519        self.origin_url = Some(CString::new(url).unwrap());
3520        self
3521    }
3522
3523    /// Creates a set of raw init options to be used with
3524    /// `git_repository_init_ext`.
3525    ///
3526    /// This method is unsafe as the returned value may have pointers to the
3527    /// interior of this structure.
3528    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    /// create the following:
3830    ///    /---o4
3831    ///   /---o3
3832    /// o1---o2
3833    #[test]
3834    fn smoke_merge_base() {
3835        let (_td, repo) = graph_repo_init();
3836        let sig = repo.signature().unwrap();
3837
3838        // let oid1 = head
3839        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        // create commit oid2 on branch_a
3848        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        // create commit oid3 on branch_b
3871        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        // create commit oid4 on branch_c
3893        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        // the merge base of (oid2,oid3) should be oid1
3913        let merge_base = repo.merge_base(oid2, oid3).unwrap();
3914        assert_eq!(merge_base, oid1);
3915
3916        // the merge base of (oid2,oid3,oid4) should be oid1
3917        let merge_base = repo.merge_base_many(&[oid2, oid3, oid4]).unwrap();
3918        assert_eq!(merge_base, oid1);
3919
3920        // the octopus merge base of (oid2,oid3,oid4) should be oid1
3921        let merge_base = repo.merge_base_octopus(&[oid2, oid3, oid4]).unwrap();
3922        assert_eq!(merge_base, oid1);
3923    }
3924
3925    /// create an octopus:
3926    ///   /---o2-o4
3927    /// o1      X
3928    ///   \---o3-o5
3929    /// and checks that the merge bases of (o4,o5) are (o2,o3)
3930    #[test]
3931    fn smoke_merge_bases() {
3932        let (_td, repo) = graph_repo_init();
3933        let sig = repo.signature().unwrap();
3934
3935        // let oid1 = head
3936        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        // create commit oid2 on branchA
3944        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        // create commit oid3 on branchB
3967        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        // create merge commit oid4 on branchA with parents oid2 and oid3
3987        //let mut index4 = repo.merge_commits(&commit2, &commit3, None).unwrap();
3988        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        //index4.write_tree_to(&repo).unwrap();
4001        println!("created oid4 {:?}", oid4);
4002
4003        // create merge commit oid5 on branchB with parents oid2 and oid3
4004        //let mut index5 = repo.merge_commits(&commit3, &commit2, None).unwrap();
4005        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        //index5.write_tree_to(&repo).unwrap();
4018        println!("created oid5 {:?}", oid5);
4019
4020        // merge bases of (oid4,oid5) should be (oid2,oid3)
4021        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        // merge bases of (oid4,oid5) should be (oid2,oid3)
4039        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        // Add 2 commits on top of the initial one in branch_a
4205        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        // cherry-pick commit3 on top of commit1 in branch b
4247        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        // should have file from commit3, but not the file from commit2
4259        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        // reverting once removes `foo` file
4282        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        // reverting twice restores `foo` file
4292        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        // Set up this repo state:
4318        // * second (their-branch)
4319        // * initial (HEAD -> main)
4320        //
4321        // We expect that their-branch can be fast-forward merged into main.
4322
4323        // git checkout --detach HEAD
4324        let head_commit = repo.head()?.peel_to_commit()?;
4325        repo.set_head_detached(head_commit.id())?;
4326
4327        // git branch their-branch HEAD
4328        let their_branch = repo.branch("their-branch", &head_commit, false)?;
4329
4330        // git branch -f main HEAD~
4331        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        // git checkout main
4338        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        // update strategy
4365        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        // ignore rule
4377        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        // url
4389        repo2.submodule_set_url(name, "fake-url")?;
4390        assert_eq!(repo2.find_submodule(name)?.url(), Some("fake-url"));
4391
4392        // branch
4393        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        // This is our baseline for HEAD.
4409        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        // There is no .mailmap file in the test repo so all signature identities are equal.
4417        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            // - Add a .mailmap file to the repository.
4427            // - Commit with a signature identity different from the author's.
4428            // - Include entries for both author and committer to prove we call
4429            //   the right raw functions.
4430            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        // Sanity check that we're working with the right commit and that its
4455        // author and committer identities differ.
4456        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        // Fetch the newly added .mailmap from the repository.
4466        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        // Mailmap Signature lifetime is independent of Commit lifetime.
4474        drop(author);
4475        drop(committer);
4476        drop(commit);
4477
4478        // author_with_mailmap() + committer_with_mailmap() work
4479        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        // resolve_signature() works
4485        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}