[go: up one dir, main page]

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, MergeOptions, MergePreference, SubmoduleIgnore,
28    SubmoduleStatus, SubmoduleUpdate,
29};
30use crate::{ApplyLocation, ApplyOptions, Rebase, RebaseOptions};
31use crate::{Blame, BlameOptions, Reference, References, ResetType, Signature, Submodule};
32use crate::{Blob, BlobWriter, Branch, BranchType, Branches, Commit, Config, Index, Oid, Tree};
33use crate::{Describe, IntoCString, Reflog, RepositoryInitMode, RevparseMode};
34use crate::{DescribeOptions, Diff, DiffOptions, Odb, PackBuilder, TreeBuilder};
35use crate::{Note, Notes, ObjectType, Revwalk, Status, StatusOptions, Statuses, Tag, Transaction};
36
37type MergeheadForeachCb<'a> = dyn FnMut(&Oid) -> bool + 'a;
38type FetchheadForeachCb<'a> = dyn FnMut(&str, &[u8], &Oid, bool) -> bool + 'a;
39
40struct FetchheadForeachCbData<'a> {
41    callback: &'a mut FetchheadForeachCb<'a>,
42}
43
44struct MergeheadForeachCbData<'a> {
45    callback: &'a mut MergeheadForeachCb<'a>,
46}
47
48extern "C" fn mergehead_foreach_cb(oid: *const raw::git_oid, payload: *mut c_void) -> c_int {
49    panic::wrap(|| unsafe {
50        let data = &mut *(payload as *mut MergeheadForeachCbData<'_>);
51        let res = {
52            let callback = &mut data.callback;
53            callback(&Binding::from_raw(oid))
54        };
55
56        if res {
57            0
58        } else {
59            1
60        }
61    })
62    .unwrap_or(1)
63}
64
65extern "C" fn fetchhead_foreach_cb(
66    ref_name: *const c_char,
67    remote_url: *const c_char,
68    oid: *const raw::git_oid,
69    is_merge: c_uint,
70    payload: *mut c_void,
71) -> c_int {
72    panic::wrap(|| unsafe {
73        let data = &mut *(payload as *mut FetchheadForeachCbData<'_>);
74        let res = {
75            let callback = &mut data.callback;
76
77            assert!(!ref_name.is_null());
78            assert!(!remote_url.is_null());
79            assert!(!oid.is_null());
80
81            let ref_name = str::from_utf8(CStr::from_ptr(ref_name).to_bytes()).unwrap();
82            let remote_url = CStr::from_ptr(remote_url).to_bytes();
83            let oid = Binding::from_raw(oid);
84            let is_merge = is_merge == 1;
85
86            callback(&ref_name, remote_url, &oid, is_merge)
87        };
88
89        if res {
90            0
91        } else {
92            1
93        }
94    })
95    .unwrap_or(1)
96}
97
98/// An owned git repository, representing all state associated with the
99/// underlying filesystem.
100///
101/// This structure corresponds to a `git_repository` in libgit2. Many other
102/// types in git2-rs are derivative from this structure and are attached to its
103/// lifetime.
104///
105/// When a repository goes out of scope it is freed in memory but not deleted
106/// from the filesystem.
107pub struct Repository {
108    raw: *mut raw::git_repository,
109}
110
111// It is the current belief that a `Repository` can be sent among threads, or
112// even shared among threads in a mutex.
113unsafe impl Send for Repository {}
114
115/// Options which can be used to configure how a repository is initialized
116pub struct RepositoryInitOptions {
117    flags: u32,
118    mode: u32,
119    workdir_path: Option<CString>,
120    description: Option<CString>,
121    template_path: Option<CString>,
122    initial_head: Option<CString>,
123    origin_url: Option<CString>,
124}
125
126impl Repository {
127    /// Attempt to open an already-existing repository at `path`.
128    ///
129    /// The path can point to either a normal or bare repository.
130    pub fn open<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
131        crate::init();
132        // Normal file path OK (does not need Windows conversion).
133        let path = path.as_ref().into_c_string()?;
134        let mut ret = ptr::null_mut();
135        unsafe {
136            try_call!(raw::git_repository_open(&mut ret, path));
137            Ok(Binding::from_raw(ret))
138        }
139    }
140
141    /// Attempt to open an already-existing bare repository at `path`.
142    ///
143    /// The path can point to only a bare repository.
144    pub fn open_bare<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
145        crate::init();
146        // Normal file path OK (does not need Windows conversion).
147        let path = path.as_ref().into_c_string()?;
148        let mut ret = ptr::null_mut();
149        unsafe {
150            try_call!(raw::git_repository_open_bare(&mut ret, path));
151            Ok(Binding::from_raw(ret))
152        }
153    }
154
155    /// Find and open an existing repository, respecting git environment
156    /// variables.  This acts like `open_ext` with the
157    /// [FROM_ENV](RepositoryOpenFlags::FROM_ENV) flag, but additionally respects `$GIT_DIR`.
158    /// With `$GIT_DIR` unset, this will search for a repository starting in
159    /// the current directory.
160    pub fn open_from_env() -> Result<Repository, Error> {
161        crate::init();
162        let mut ret = ptr::null_mut();
163        let flags = raw::GIT_REPOSITORY_OPEN_FROM_ENV;
164        unsafe {
165            try_call!(raw::git_repository_open_ext(
166                &mut ret,
167                ptr::null(),
168                flags as c_uint,
169                ptr::null()
170            ));
171            Ok(Binding::from_raw(ret))
172        }
173    }
174
175    /// Find and open an existing repository, with additional options.
176    ///
177    /// If flags contains [NO_SEARCH](RepositoryOpenFlags::NO_SEARCH), the path must point
178    /// directly to a repository; otherwise, this may point to a subdirectory
179    /// of a repository, and `open_ext` will search up through parent
180    /// directories.
181    ///
182    /// If flags contains [CROSS_FS](RepositoryOpenFlags::CROSS_FS), the search through parent
183    /// directories will not cross a filesystem boundary (detected when the
184    /// stat st_dev field changes).
185    ///
186    /// If flags contains [BARE](RepositoryOpenFlags::BARE), force opening the repository as
187    /// bare even if it isn't, ignoring any working directory, and defer
188    /// loading the repository configuration for performance.
189    ///
190    /// If flags contains [NO_DOTGIT](RepositoryOpenFlags::NO_DOTGIT), don't try appending
191    /// `/.git` to `path`.
192    ///
193    /// If flags contains [FROM_ENV](RepositoryOpenFlags::FROM_ENV), `open_ext` will ignore
194    /// other flags and `ceiling_dirs`, and respect the same environment
195    /// variables git does. Note, however, that `path` overrides `$GIT_DIR`; to
196    /// respect `$GIT_DIR` as well, use `open_from_env`.
197    ///
198    /// ceiling_dirs specifies a list of paths that the search through parent
199    /// directories will stop before entering.  Use the functions in std::env
200    /// to construct or manipulate such a path list. (You can use `&[] as
201    /// &[&std::ffi::OsStr]` as an argument if there are no ceiling
202    /// directories.)
203    pub fn open_ext<P, O, I>(
204        path: P,
205        flags: RepositoryOpenFlags,
206        ceiling_dirs: I,
207    ) -> Result<Repository, Error>
208    where
209        P: AsRef<Path>,
210        O: AsRef<OsStr>,
211        I: IntoIterator<Item = O>,
212    {
213        crate::init();
214        // Normal file path OK (does not need Windows conversion).
215        let path = path.as_ref().into_c_string()?;
216        let ceiling_dirs_os = env::join_paths(ceiling_dirs)?;
217        let ceiling_dirs = ceiling_dirs_os.into_c_string()?;
218        let mut ret = ptr::null_mut();
219        unsafe {
220            try_call!(raw::git_repository_open_ext(
221                &mut ret,
222                path,
223                flags.bits() as c_uint,
224                ceiling_dirs
225            ));
226            Ok(Binding::from_raw(ret))
227        }
228    }
229
230    /// Attempt to open an already-existing repository from a worktree.
231    pub fn open_from_worktree(worktree: &Worktree) -> Result<Repository, Error> {
232        let mut ret = ptr::null_mut();
233        unsafe {
234            try_call!(raw::git_repository_open_from_worktree(
235                &mut ret,
236                worktree.raw()
237            ));
238            Ok(Binding::from_raw(ret))
239        }
240    }
241
242    /// Attempt to open an already-existing repository at or above `path`
243    ///
244    /// This starts at `path` and looks up the filesystem hierarchy
245    /// until it finds a repository.
246    pub fn discover<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
247        // TODO: this diverges significantly from the libgit2 API
248        crate::init();
249        let buf = Buf::new();
250        // Normal file path OK (does not need Windows conversion).
251        let path = path.as_ref().into_c_string()?;
252        unsafe {
253            try_call!(raw::git_repository_discover(
254                buf.raw(),
255                path,
256                1,
257                ptr::null()
258            ));
259        }
260        Repository::open(util::bytes2path(&*buf))
261    }
262
263    /// Attempt to find the path to a git repo for a given path
264    ///
265    /// This starts at `path` and looks up the filesystem hierarchy
266    /// until it finds a repository, stopping if it finds a member of ceiling_dirs
267    pub fn discover_path<P: AsRef<Path>, I, O>(path: P, ceiling_dirs: I) -> Result<PathBuf, Error>
268    where
269        O: AsRef<OsStr>,
270        I: IntoIterator<Item = O>,
271    {
272        crate::init();
273        let buf = Buf::new();
274        // Normal file path OK (does not need Windows conversion).
275        let path = path.as_ref().into_c_string()?;
276        let ceiling_dirs_os = env::join_paths(ceiling_dirs)?;
277        let ceiling_dirs = ceiling_dirs_os.into_c_string()?;
278        unsafe {
279            try_call!(raw::git_repository_discover(
280                buf.raw(),
281                path,
282                1,
283                ceiling_dirs
284            ));
285        }
286
287        Ok(util::bytes2path(&*buf).to_path_buf())
288    }
289
290    /// Creates a new repository in the specified folder.
291    ///
292    /// This by default will create any necessary directories to create the
293    /// repository, and it will read any user-specified templates when creating
294    /// the repository. This behavior can be configured through `init_opts`.
295    pub fn init<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
296        Repository::init_opts(path, &RepositoryInitOptions::new())
297    }
298
299    /// Creates a new `--bare` repository in the specified folder.
300    ///
301    /// The folder must exist prior to invoking this function.
302    pub fn init_bare<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
303        Repository::init_opts(path, RepositoryInitOptions::new().bare(true))
304    }
305
306    /// Creates a new repository in the specified folder with the given options.
307    ///
308    /// See `RepositoryInitOptions` struct for more information.
309    pub fn init_opts<P: AsRef<Path>>(
310        path: P,
311        opts: &RepositoryInitOptions,
312    ) -> Result<Repository, Error> {
313        crate::init();
314        // Normal file path OK (does not need Windows conversion).
315        let path = path.as_ref().into_c_string()?;
316        let mut ret = ptr::null_mut();
317        unsafe {
318            let mut opts = opts.raw();
319            try_call!(raw::git_repository_init_ext(&mut ret, path, &mut opts));
320            Ok(Binding::from_raw(ret))
321        }
322    }
323
324    /// Clone a remote repository.
325    ///
326    /// See the `RepoBuilder` struct for more information. This function will
327    /// delegate to a fresh `RepoBuilder`
328    pub fn clone<P: AsRef<Path>>(url: &str, into: P) -> Result<Repository, Error> {
329        crate::init();
330        RepoBuilder::new().clone(url, into.as_ref())
331    }
332
333    /// Clone a remote repository, initialize and update its submodules
334    /// recursively.
335    ///
336    /// This is similar to `git clone --recursive`.
337    pub fn clone_recurse<P: AsRef<Path>>(url: &str, into: P) -> Result<Repository, Error> {
338        let repo = Repository::clone(url, into)?;
339        repo.update_submodules()?;
340        Ok(repo)
341    }
342
343    /// Attempt to wrap an object database as a repository.
344    pub fn from_odb(odb: Odb<'_>) -> Result<Repository, Error> {
345        crate::init();
346        let mut ret = ptr::null_mut();
347        unsafe {
348            try_call!(raw::git_repository_wrap_odb(&mut ret, odb.raw()));
349            Ok(Binding::from_raw(ret))
350        }
351    }
352
353    /// Update submodules recursively.
354    ///
355    /// Uninitialized submodules will be initialized.
356    fn update_submodules(&self) -> Result<(), Error> {
357        fn add_subrepos(repo: &Repository, list: &mut Vec<Repository>) -> Result<(), Error> {
358            for mut subm in repo.submodules()? {
359                subm.update(true, None)?;
360                list.push(subm.open()?);
361            }
362            Ok(())
363        }
364
365        let mut repos = Vec::new();
366        add_subrepos(self, &mut repos)?;
367        while let Some(repo) = repos.pop() {
368            add_subrepos(&repo, &mut repos)?;
369        }
370        Ok(())
371    }
372
373    /// Execute a rev-parse operation against the `spec` listed.
374    ///
375    /// The resulting revision specification is returned, or an error is
376    /// returned if one occurs.
377    pub fn revparse(&self, spec: &str) -> Result<Revspec<'_>, Error> {
378        let mut raw = raw::git_revspec {
379            from: ptr::null_mut(),
380            to: ptr::null_mut(),
381            flags: 0,
382        };
383        let spec = CString::new(spec)?;
384        unsafe {
385            try_call!(raw::git_revparse(&mut raw, self.raw, spec));
386            let to = Binding::from_raw_opt(raw.to);
387            let from = Binding::from_raw_opt(raw.from);
388            let mode = RevparseMode::from_bits_truncate(raw.flags as u32);
389            Ok(Revspec::from_objects(from, to, mode))
390        }
391    }
392
393    /// Find a single object, as specified by a revision string.
394    pub fn revparse_single(&self, spec: &str) -> Result<Object<'_>, Error> {
395        let spec = CString::new(spec)?;
396        let mut obj = ptr::null_mut();
397        unsafe {
398            try_call!(raw::git_revparse_single(&mut obj, self.raw, spec));
399            assert!(!obj.is_null());
400            Ok(Binding::from_raw(obj))
401        }
402    }
403
404    /// Find a single object and intermediate reference by a revision string.
405    ///
406    /// See `man gitrevisions`, or
407    /// <http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions> for
408    /// information on the syntax accepted.
409    ///
410    /// In some cases (`@{<-n>}` or `<branchname>@{upstream}`), the expression
411    /// may point to an intermediate reference. When such expressions are being
412    /// passed in, this intermediate reference is returned.
413    pub fn revparse_ext(&self, spec: &str) -> Result<(Object<'_>, Option<Reference<'_>>), Error> {
414        let spec = CString::new(spec)?;
415        let mut git_obj = ptr::null_mut();
416        let mut git_ref = ptr::null_mut();
417        unsafe {
418            try_call!(raw::git_revparse_ext(
419                &mut git_obj,
420                &mut git_ref,
421                self.raw,
422                spec
423            ));
424            assert!(!git_obj.is_null());
425            Ok((Binding::from_raw(git_obj), Binding::from_raw_opt(git_ref)))
426        }
427    }
428
429    /// Tests whether this repository is a bare repository or not.
430    pub fn is_bare(&self) -> bool {
431        unsafe { raw::git_repository_is_bare(self.raw) == 1 }
432    }
433
434    /// Tests whether this repository is a shallow clone.
435    pub fn is_shallow(&self) -> bool {
436        unsafe { raw::git_repository_is_shallow(self.raw) == 1 }
437    }
438
439    /// Tests whether this repository is a worktree.
440    pub fn is_worktree(&self) -> bool {
441        unsafe { raw::git_repository_is_worktree(self.raw) == 1 }
442    }
443
444    /// Tests whether this repository is empty.
445    pub fn is_empty(&self) -> Result<bool, Error> {
446        let empty = unsafe { try_call!(raw::git_repository_is_empty(self.raw)) };
447        Ok(empty == 1)
448    }
449
450    /// Returns the path to the `.git` folder for normal repositories or the
451    /// repository itself for bare repositories.
452    pub fn path(&self) -> &Path {
453        unsafe {
454            let ptr = raw::git_repository_path(self.raw);
455            util::bytes2path(crate::opt_bytes(self, ptr).unwrap())
456        }
457    }
458
459    /// Returns the path of the shared common directory for this repository.
460    ///
461    /// If the repository is bare, it is the root directory for the repository.
462    /// If the repository is a worktree, it is the parent repo's gitdir.
463    /// Otherwise, it is the gitdir.
464    pub fn commondir(&self) -> &Path {
465        unsafe {
466            let ptr = raw::git_repository_commondir(self.raw);
467            util::bytes2path(crate::opt_bytes(self, ptr).unwrap())
468        }
469    }
470
471    /// Returns the current state of this repository
472    pub fn state(&self) -> RepositoryState {
473        let state = unsafe { raw::git_repository_state(self.raw) };
474        macro_rules! check( ($($raw:ident => $real:ident),*) => (
475            $(if state == raw::$raw as c_int {
476                super::RepositoryState::$real
477            }) else *
478            else {
479                panic!("unknown repository state: {}", state)
480            }
481        ) );
482
483        check!(
484            GIT_REPOSITORY_STATE_NONE => Clean,
485            GIT_REPOSITORY_STATE_MERGE => Merge,
486            GIT_REPOSITORY_STATE_REVERT => Revert,
487            GIT_REPOSITORY_STATE_REVERT_SEQUENCE => RevertSequence,
488            GIT_REPOSITORY_STATE_CHERRYPICK => CherryPick,
489            GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE => CherryPickSequence,
490            GIT_REPOSITORY_STATE_BISECT => Bisect,
491            GIT_REPOSITORY_STATE_REBASE => Rebase,
492            GIT_REPOSITORY_STATE_REBASE_INTERACTIVE => RebaseInteractive,
493            GIT_REPOSITORY_STATE_REBASE_MERGE => RebaseMerge,
494            GIT_REPOSITORY_STATE_APPLY_MAILBOX => ApplyMailbox,
495            GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE => ApplyMailboxOrRebase
496        )
497    }
498
499    /// Get the path of the working directory for this repository.
500    ///
501    /// If this repository is bare, then `None` is returned.
502    pub fn workdir(&self) -> Option<&Path> {
503        unsafe {
504            let ptr = raw::git_repository_workdir(self.raw);
505            if ptr.is_null() {
506                None
507            } else {
508                Some(util::bytes2path(CStr::from_ptr(ptr).to_bytes()))
509            }
510        }
511    }
512
513    /// Set the path to the working directory for this repository.
514    ///
515    /// If `update_link` is true, create/update the gitlink file in the workdir
516    /// and set config "core.worktree" (if workdir is not the parent of the .git
517    /// directory).
518    pub fn set_workdir(&self, path: &Path, update_gitlink: bool) -> Result<(), Error> {
519        // Normal file path OK (does not need Windows conversion).
520        let path = path.into_c_string()?;
521        unsafe {
522            try_call!(raw::git_repository_set_workdir(
523                self.raw(),
524                path,
525                update_gitlink
526            ));
527        }
528        Ok(())
529    }
530
531    /// Get the currently active namespace for this repository.
532    ///
533    /// If there is no namespace, or the namespace is not a valid utf8 string,
534    /// `None` is returned.
535    pub fn namespace(&self) -> Option<&str> {
536        self.namespace_bytes().and_then(|s| str::from_utf8(s).ok())
537    }
538
539    /// Get the currently active namespace for this repository as a byte array.
540    ///
541    /// If there is no namespace, `None` is returned.
542    pub fn namespace_bytes(&self) -> Option<&[u8]> {
543        unsafe { crate::opt_bytes(self, raw::git_repository_get_namespace(self.raw)) }
544    }
545
546    /// Set the active namespace for this repository.
547    pub fn set_namespace(&self, namespace: &str) -> Result<(), Error> {
548        self.set_namespace_bytes(namespace.as_bytes())
549    }
550
551    /// Set the active namespace for this repository as a byte array.
552    pub fn set_namespace_bytes(&self, namespace: &[u8]) -> Result<(), Error> {
553        unsafe {
554            let namespace = CString::new(namespace)?;
555            try_call!(raw::git_repository_set_namespace(self.raw, namespace));
556            Ok(())
557        }
558    }
559
560    /// Remove the active namespace for this repository.
561    pub fn remove_namespace(&self) -> Result<(), Error> {
562        unsafe {
563            try_call!(raw::git_repository_set_namespace(self.raw, ptr::null()));
564            Ok(())
565        }
566    }
567
568    /// Retrieves the Git merge message.
569    /// Remember to remove the message when finished.
570    pub fn message(&self) -> Result<String, Error> {
571        unsafe {
572            let buf = Buf::new();
573            try_call!(raw::git_repository_message(buf.raw(), self.raw));
574            Ok(str::from_utf8(&buf).unwrap().to_string())
575        }
576    }
577
578    /// Remove the Git merge message.
579    pub fn remove_message(&self) -> Result<(), Error> {
580        unsafe {
581            try_call!(raw::git_repository_message_remove(self.raw));
582            Ok(())
583        }
584    }
585
586    /// List all remotes for a given repository
587    pub fn remotes(&self) -> Result<StringArray, Error> {
588        let mut arr = raw::git_strarray {
589            strings: ptr::null_mut(),
590            count: 0,
591        };
592        unsafe {
593            try_call!(raw::git_remote_list(&mut arr, self.raw));
594            Ok(Binding::from_raw(arr))
595        }
596    }
597
598    /// Get the information for a particular remote
599    pub fn find_remote(&self, name: &str) -> Result<Remote<'_>, Error> {
600        let mut ret = ptr::null_mut();
601        let name = CString::new(name)?;
602        unsafe {
603            try_call!(raw::git_remote_lookup(&mut ret, self.raw, name));
604            Ok(Binding::from_raw(ret))
605        }
606    }
607
608    /// Add a remote with the default fetch refspec to the repository's
609    /// configuration.
610    pub fn remote(&self, name: &str, url: &str) -> Result<Remote<'_>, Error> {
611        let mut ret = ptr::null_mut();
612        let name = CString::new(name)?;
613        let url = CString::new(url)?;
614        unsafe {
615            try_call!(raw::git_remote_create(&mut ret, self.raw, name, url));
616            Ok(Binding::from_raw(ret))
617        }
618    }
619
620    /// Add a remote with the provided fetch refspec to the repository's
621    /// configuration.
622    pub fn remote_with_fetch(
623        &self,
624        name: &str,
625        url: &str,
626        fetch: &str,
627    ) -> Result<Remote<'_>, Error> {
628        let mut ret = ptr::null_mut();
629        let name = CString::new(name)?;
630        let url = CString::new(url)?;
631        let fetch = CString::new(fetch)?;
632        unsafe {
633            try_call!(raw::git_remote_create_with_fetchspec(
634                &mut ret, self.raw, name, url, fetch
635            ));
636            Ok(Binding::from_raw(ret))
637        }
638    }
639
640    /// Create an anonymous remote
641    ///
642    /// Create a remote with the given URL and refspec in memory. You can use
643    /// this when you have a URL instead of a remote's name. Note that anonymous
644    /// remotes cannot be converted to persisted remotes.
645    pub fn remote_anonymous(&self, url: &str) -> Result<Remote<'_>, Error> {
646        let mut ret = ptr::null_mut();
647        let url = CString::new(url)?;
648        unsafe {
649            try_call!(raw::git_remote_create_anonymous(&mut ret, self.raw, url));
650            Ok(Binding::from_raw(ret))
651        }
652    }
653
654    /// Give a remote a new name
655    ///
656    /// All remote-tracking branches and configuration settings for the remote
657    /// are updated.
658    ///
659    /// A temporary in-memory remote cannot be given a name with this method.
660    ///
661    /// No loaded instances of the remote with the old name will change their
662    /// name or their list of refspecs.
663    ///
664    /// The returned array of strings is a list of the non-default refspecs
665    /// which cannot be renamed and are returned for further processing by the
666    /// caller.
667    pub fn remote_rename(&self, name: &str, new_name: &str) -> Result<StringArray, Error> {
668        let name = CString::new(name)?;
669        let new_name = CString::new(new_name)?;
670        let mut problems = raw::git_strarray {
671            count: 0,
672            strings: ptr::null_mut(),
673        };
674        unsafe {
675            try_call!(raw::git_remote_rename(
676                &mut problems,
677                self.raw,
678                name,
679                new_name
680            ));
681            Ok(Binding::from_raw(problems))
682        }
683    }
684
685    /// Delete an existing persisted remote.
686    ///
687    /// All remote-tracking branches and configuration settings for the remote
688    /// will be removed.
689    pub fn remote_delete(&self, name: &str) -> Result<(), Error> {
690        let name = CString::new(name)?;
691        unsafe {
692            try_call!(raw::git_remote_delete(self.raw, name));
693        }
694        Ok(())
695    }
696
697    /// Add a fetch refspec to the remote's configuration
698    ///
699    /// Add the given refspec to the fetch list in the configuration. No loaded
700    /// remote instances will be affected.
701    pub fn remote_add_fetch(&self, name: &str, spec: &str) -> Result<(), Error> {
702        let name = CString::new(name)?;
703        let spec = CString::new(spec)?;
704        unsafe {
705            try_call!(raw::git_remote_add_fetch(self.raw, name, spec));
706        }
707        Ok(())
708    }
709
710    /// Add a push refspec to the remote's configuration.
711    ///
712    /// Add the given refspec to the push list in the configuration. No
713    /// loaded remote instances will be affected.
714    pub fn remote_add_push(&self, name: &str, spec: &str) -> Result<(), Error> {
715        let name = CString::new(name)?;
716        let spec = CString::new(spec)?;
717        unsafe {
718            try_call!(raw::git_remote_add_push(self.raw, name, spec));
719        }
720        Ok(())
721    }
722
723    /// Set the remote's URL in the configuration
724    ///
725    /// Remote objects already in memory will not be affected. This assumes
726    /// the common case of a single-url remote and will otherwise return an
727    /// error.
728    pub fn remote_set_url(&self, name: &str, url: &str) -> Result<(), Error> {
729        let name = CString::new(name)?;
730        let url = CString::new(url)?;
731        unsafe {
732            try_call!(raw::git_remote_set_url(self.raw, name, url));
733        }
734        Ok(())
735    }
736
737    /// Set the remote's URL for pushing in the configuration.
738    ///
739    /// Remote objects already in memory will not be affected. This assumes
740    /// the common case of a single-url remote and will otherwise return an
741    /// error.
742    ///
743    /// `None` indicates that it should be cleared.
744    pub fn remote_set_pushurl(&self, name: &str, pushurl: Option<&str>) -> Result<(), Error> {
745        let name = CString::new(name)?;
746        let pushurl = crate::opt_cstr(pushurl)?;
747        unsafe {
748            try_call!(raw::git_remote_set_pushurl(self.raw, name, pushurl));
749        }
750        Ok(())
751    }
752
753    /// Sets the current head to the specified object and optionally resets
754    /// the index and working tree to match.
755    ///
756    /// A soft reset means the head will be moved to the commit.
757    ///
758    /// A mixed reset will trigger a soft reset, plus the index will be
759    /// replaced with the content of the commit tree.
760    ///
761    /// A hard reset will trigger a mixed reset and the working directory will
762    /// be replaced with the content of the index. (Untracked and ignored files
763    /// will be left alone, however.)
764    ///
765    /// The `target` is a commit-ish to which the head should be moved to. The
766    /// object can either be a commit or a tag, but tags must be dereferenceable
767    /// to a commit.
768    ///
769    /// The `checkout` options will only be used for a hard reset.
770    pub fn reset(
771        &self,
772        target: &Object<'_>,
773        kind: ResetType,
774        checkout: Option<&mut CheckoutBuilder<'_>>,
775    ) -> Result<(), Error> {
776        unsafe {
777            let mut opts: raw::git_checkout_options = mem::zeroed();
778            try_call!(raw::git_checkout_init_options(
779                &mut opts,
780                raw::GIT_CHECKOUT_OPTIONS_VERSION
781            ));
782            let opts = checkout.map(|c| {
783                c.configure(&mut opts);
784                &mut opts
785            });
786            try_call!(raw::git_reset(self.raw, target.raw(), kind, opts));
787        }
788        Ok(())
789    }
790
791    /// Updates some entries in the index from the target commit tree.
792    ///
793    /// The scope of the updated entries is determined by the paths being
794    /// in the iterator provided.
795    ///
796    /// Passing a `None` target will result in removing entries in the index
797    /// matching the provided pathspecs.
798    pub fn reset_default<T, I>(&self, target: Option<&Object<'_>>, paths: I) -> Result<(), Error>
799    where
800        T: IntoCString,
801        I: IntoIterator<Item = T>,
802    {
803        let (_a, _b, mut arr) = crate::util::iter2cstrs_paths(paths)?;
804        let target = target.map(|t| t.raw());
805        unsafe {
806            try_call!(raw::git_reset_default(self.raw, target, &mut arr));
807        }
808        Ok(())
809    }
810
811    /// Retrieve and resolve the reference pointed at by HEAD.
812    pub fn head(&self) -> Result<Reference<'_>, Error> {
813        let mut ret = ptr::null_mut();
814        unsafe {
815            try_call!(raw::git_repository_head(&mut ret, self.raw));
816            Ok(Binding::from_raw(ret))
817        }
818    }
819
820    /// Make the repository HEAD point to the specified reference.
821    ///
822    /// If the provided reference points to a tree or a blob, the HEAD is
823    /// unaltered and an error is returned.
824    ///
825    /// If the provided reference points to a branch, the HEAD will point to
826    /// that branch, staying attached, or become attached if it isn't yet. If
827    /// the branch doesn't exist yet, no error will be returned. The HEAD will
828    /// then be attached to an unborn branch.
829    ///
830    /// Otherwise, the HEAD will be detached and will directly point to the
831    /// commit.
832    pub fn set_head(&self, refname: &str) -> Result<(), Error> {
833        self.set_head_bytes(refname.as_bytes())
834    }
835
836    /// Make the repository HEAD point to the specified reference as a byte array.
837    ///
838    /// If the provided reference points to a tree or a blob, the HEAD is
839    /// unaltered and an error is returned.
840    ///
841    /// If the provided reference points to a branch, the HEAD will point to
842    /// that branch, staying attached, or become attached if it isn't yet. If
843    /// the branch doesn't exist yet, no error will be returned. The HEAD will
844    /// then be attached to an unborn branch.
845    ///
846    /// Otherwise, the HEAD will be detached and will directly point to the
847    /// commit.
848    pub fn set_head_bytes(&self, refname: &[u8]) -> Result<(), Error> {
849        let refname = CString::new(refname)?;
850        unsafe {
851            try_call!(raw::git_repository_set_head(self.raw, refname));
852        }
853        Ok(())
854    }
855
856    /// Determines whether the repository HEAD is detached.
857    pub fn head_detached(&self) -> Result<bool, Error> {
858        unsafe {
859            let value = raw::git_repository_head_detached(self.raw);
860            match value {
861                0 => Ok(false),
862                1 => Ok(true),
863                _ => Err(Error::last_error(value)),
864            }
865        }
866    }
867
868    /// Make the repository HEAD directly point to the commit.
869    ///
870    /// If the provided commitish cannot be found in the repository, the HEAD
871    /// is unaltered and an error is returned.
872    ///
873    /// If the provided commitish cannot be peeled into a commit, the HEAD is
874    /// unaltered and an error is returned.
875    ///
876    /// Otherwise, the HEAD will eventually be detached and will directly point
877    /// to the peeled commit.
878    pub fn set_head_detached(&self, commitish: Oid) -> Result<(), Error> {
879        unsafe {
880            try_call!(raw::git_repository_set_head_detached(
881                self.raw,
882                commitish.raw()
883            ));
884        }
885        Ok(())
886    }
887
888    /// Make the repository HEAD directly point to the commit.
889    ///
890    /// If the provided commitish cannot be found in the repository, the HEAD
891    /// is unaltered and an error is returned.
892    /// If the provided commitish cannot be peeled into a commit, the HEAD is
893    /// unaltered and an error is returned.
894    /// Otherwise, the HEAD will eventually be detached and will directly point
895    /// to the peeled commit.
896    pub fn set_head_detached_from_annotated(
897        &self,
898        commitish: AnnotatedCommit<'_>,
899    ) -> Result<(), Error> {
900        unsafe {
901            try_call!(raw::git_repository_set_head_detached_from_annotated(
902                self.raw,
903                commitish.raw()
904            ));
905        }
906        Ok(())
907    }
908
909    /// Create an iterator for the repo's references
910    pub fn references(&self) -> Result<References<'_>, Error> {
911        let mut ret = ptr::null_mut();
912        unsafe {
913            try_call!(raw::git_reference_iterator_new(&mut ret, self.raw));
914            Ok(Binding::from_raw(ret))
915        }
916    }
917
918    /// Create an iterator for the repo's references that match the specified
919    /// glob
920    pub fn references_glob(&self, glob: &str) -> Result<References<'_>, Error> {
921        let mut ret = ptr::null_mut();
922        let glob = CString::new(glob)?;
923        unsafe {
924            try_call!(raw::git_reference_iterator_glob_new(
925                &mut ret, self.raw, glob
926            ));
927
928            Ok(Binding::from_raw(ret))
929        }
930    }
931
932    /// Load all submodules for this repository and return them.
933    pub fn submodules(&self) -> Result<Vec<Submodule<'_>>, Error> {
934        struct Data<'a, 'b> {
935            repo: &'b Repository,
936            ret: &'a mut Vec<Submodule<'b>>,
937        }
938        let mut ret = Vec::new();
939
940        unsafe {
941            let mut data = Data {
942                repo: self,
943                ret: &mut ret,
944            };
945            let cb: raw::git_submodule_cb = Some(append);
946            try_call!(raw::git_submodule_foreach(
947                self.raw,
948                cb,
949                &mut data as *mut _ as *mut c_void
950            ));
951        }
952
953        return Ok(ret);
954
955        extern "C" fn append(
956            _repo: *mut raw::git_submodule,
957            name: *const c_char,
958            data: *mut c_void,
959        ) -> c_int {
960            unsafe {
961                let data = &mut *(data as *mut Data<'_, '_>);
962                let mut raw = ptr::null_mut();
963                let rc = raw::git_submodule_lookup(&mut raw, data.repo.raw(), name);
964                assert_eq!(rc, 0);
965                data.ret.push(Binding::from_raw(raw));
966            }
967            0
968        }
969    }
970
971    /// Gather file status information and populate the returned structure.
972    ///
973    /// Note that if a pathspec is given in the options to filter the
974    /// status, then the results from rename detection (if you enable it) may
975    /// not be accurate. To do rename detection properly, this must be called
976    /// with no pathspec so that all files can be considered.
977    pub fn statuses(&self, options: Option<&mut StatusOptions>) -> Result<Statuses<'_>, Error> {
978        let mut ret = ptr::null_mut();
979        unsafe {
980            try_call!(raw::git_status_list_new(
981                &mut ret,
982                self.raw,
983                options.map(|s| s.raw()).unwrap_or(ptr::null())
984            ));
985            Ok(Binding::from_raw(ret))
986        }
987    }
988
989    /// Test if the ignore rules apply to a given file.
990    ///
991    /// This function checks the ignore rules to see if they would apply to the
992    /// given file. This indicates if the file would be ignored regardless of
993    /// whether the file is already in the index or committed to the repository.
994    ///
995    /// One way to think of this is if you were to do "git add ." on the
996    /// directory containing the file, would it be added or not?
997    pub fn status_should_ignore(&self, path: &Path) -> Result<bool, Error> {
998        let mut ret = 0 as c_int;
999        let path = util::cstring_to_repo_path(path)?;
1000        unsafe {
1001            try_call!(raw::git_status_should_ignore(&mut ret, self.raw, path));
1002        }
1003        Ok(ret != 0)
1004    }
1005
1006    /// Get file status for a single file.
1007    ///
1008    /// This tries to get status for the filename that you give. If no files
1009    /// match that name (in either the HEAD, index, or working directory), this
1010    /// returns NotFound.
1011    ///
1012    /// If the name matches multiple files (for example, if the path names a
1013    /// directory or if running on a case- insensitive filesystem and yet the
1014    /// HEAD has two entries that both match the path), then this returns
1015    /// Ambiguous because it cannot give correct results.
1016    ///
1017    /// This does not do any sort of rename detection. Renames require a set of
1018    /// targets and because of the path filtering, there is not enough
1019    /// information to check renames correctly. To check file status with rename
1020    /// detection, there is no choice but to do a full `statuses` and scan
1021    /// through looking for the path that you are interested in.
1022    pub fn status_file(&self, path: &Path) -> Result<Status, Error> {
1023        let mut ret = 0 as c_uint;
1024        let path = path_to_repo_path(path)?;
1025        unsafe {
1026            try_call!(raw::git_status_file(&mut ret, self.raw, path));
1027        }
1028        Ok(Status::from_bits_truncate(ret as u32))
1029    }
1030
1031    /// Create an iterator which loops over the requested branches.
1032    pub fn branches(&self, filter: Option<BranchType>) -> Result<Branches<'_>, Error> {
1033        let mut raw = ptr::null_mut();
1034        unsafe {
1035            try_call!(raw::git_branch_iterator_new(&mut raw, self.raw(), filter));
1036            Ok(Branches::from_raw(raw))
1037        }
1038    }
1039
1040    /// Get the Index file for this repository.
1041    ///
1042    /// If a custom index has not been set, the default index for the repository
1043    /// will be returned (the one located in .git/index).
1044    ///
1045    /// **Caution**: If the [`Repository`] of this index is dropped, then this
1046    /// [`Index`] will become detached, and most methods on it will fail. See
1047    /// [`Index::open`]. Be sure the repository has a binding such as a local
1048    /// variable to keep it alive at least as long as the index.
1049    pub fn index(&self) -> Result<Index, Error> {
1050        let mut raw = ptr::null_mut();
1051        unsafe {
1052            try_call!(raw::git_repository_index(&mut raw, self.raw()));
1053            Ok(Binding::from_raw(raw))
1054        }
1055    }
1056
1057    /// Set the Index file for this repository.
1058    pub fn set_index(&self, index: &mut Index) -> Result<(), Error> {
1059        unsafe {
1060            try_call!(raw::git_repository_set_index(self.raw(), index.raw()));
1061        }
1062        Ok(())
1063    }
1064
1065    /// Get the configuration file for this repository.
1066    ///
1067    /// If a configuration file has not been set, the default config set for the
1068    /// repository will be returned, including global and system configurations
1069    /// (if they are available).
1070    pub fn config(&self) -> Result<Config, Error> {
1071        let mut raw = ptr::null_mut();
1072        unsafe {
1073            try_call!(raw::git_repository_config(&mut raw, self.raw()));
1074            Ok(Binding::from_raw(raw))
1075        }
1076    }
1077
1078    /// Get the value of a git attribute for a path as a string.
1079    ///
1080    /// This function will return a special string if the attribute is set to a special value.
1081    /// Interpreting the special string is discouraged. You should always use
1082    /// [`AttrValue::from_string`](crate::AttrValue::from_string) to interpret the return value
1083    /// and avoid the special string.
1084    ///
1085    /// As such, the return type of this function will probably be changed in the next major version
1086    /// to prevent interpreting the returned string without checking whether it's special.
1087    pub fn get_attr(
1088        &self,
1089        path: &Path,
1090        name: &str,
1091        flags: AttrCheckFlags,
1092    ) -> Result<Option<&str>, Error> {
1093        Ok(self
1094            .get_attr_bytes(path, name, flags)?
1095            .and_then(|a| str::from_utf8(a).ok()))
1096    }
1097
1098    /// Get the value of a git attribute for a path as a byte slice.
1099    ///
1100    /// This function will return a special byte slice if the attribute is set to a special value.
1101    /// Interpreting the special byte slice is discouraged. You should always use
1102    /// [`AttrValue::from_bytes`](crate::AttrValue::from_bytes) to interpret the return value and
1103    /// avoid the special string.
1104    ///
1105    /// As such, the return type of this function will probably be changed in the next major version
1106    /// to prevent interpreting the returned byte slice without checking whether it's special.
1107    pub fn get_attr_bytes(
1108        &self,
1109        path: &Path,
1110        name: &str,
1111        flags: AttrCheckFlags,
1112    ) -> Result<Option<&[u8]>, Error> {
1113        let mut ret = ptr::null();
1114        let path = util::cstring_to_repo_path(path)?;
1115        let name = CString::new(name)?;
1116        unsafe {
1117            try_call!(raw::git_attr_get(
1118                &mut ret,
1119                self.raw(),
1120                flags.bits(),
1121                path,
1122                name
1123            ));
1124            Ok(crate::opt_bytes(self, ret))
1125        }
1126    }
1127
1128    /// Write an in-memory buffer to the ODB as a blob.
1129    ///
1130    /// The Oid returned can in turn be passed to `find_blob` to get a handle to
1131    /// the blob.
1132    pub fn blob(&self, data: &[u8]) -> Result<Oid, Error> {
1133        let mut raw = raw::git_oid {
1134            id: [0; raw::GIT_OID_RAWSZ],
1135        };
1136        unsafe {
1137            let ptr = data.as_ptr() as *const c_void;
1138            let len = data.len() as size_t;
1139            try_call!(raw::git_blob_create_frombuffer(
1140                &mut raw,
1141                self.raw(),
1142                ptr,
1143                len
1144            ));
1145            Ok(Binding::from_raw(&raw as *const _))
1146        }
1147    }
1148
1149    /// Read a file from the filesystem and write its content to the Object
1150    /// Database as a loose blob
1151    ///
1152    /// The Oid returned can in turn be passed to `find_blob` to get a handle to
1153    /// the blob.
1154    pub fn blob_path(&self, path: &Path) -> Result<Oid, Error> {
1155        // Normal file path OK (does not need Windows conversion).
1156        let path = path.into_c_string()?;
1157        let mut raw = raw::git_oid {
1158            id: [0; raw::GIT_OID_RAWSZ],
1159        };
1160        unsafe {
1161            try_call!(raw::git_blob_create_fromdisk(&mut raw, self.raw(), path));
1162            Ok(Binding::from_raw(&raw as *const _))
1163        }
1164    }
1165
1166    /// Create a stream to write blob
1167    ///
1168    /// This function may need to buffer the data on disk and will in general
1169    /// not be the right choice if you know the size of the data to write.
1170    ///
1171    /// Use `BlobWriter::commit()` to commit the write to the object db
1172    /// and get the object id.
1173    ///
1174    /// If the `hintpath` parameter is filled, it will be used to determine
1175    /// what git filters should be applied to the object before it is written
1176    /// to the object database.
1177    pub fn blob_writer(&self, hintpath: Option<&Path>) -> Result<BlobWriter<'_>, Error> {
1178        let path_str = match hintpath {
1179            Some(path) => Some(path.into_c_string()?),
1180            None => None,
1181        };
1182        let path = match path_str {
1183            Some(ref path) => path.as_ptr(),
1184            None => ptr::null(),
1185        };
1186        let mut out = ptr::null_mut();
1187        unsafe {
1188            try_call!(raw::git_blob_create_fromstream(&mut out, self.raw(), path));
1189            Ok(BlobWriter::from_raw(out))
1190        }
1191    }
1192
1193    /// Lookup a reference to one of the objects in a repository.
1194    pub fn find_blob(&self, oid: Oid) -> Result<Blob<'_>, Error> {
1195        let mut raw = ptr::null_mut();
1196        unsafe {
1197            try_call!(raw::git_blob_lookup(&mut raw, self.raw(), oid.raw()));
1198            Ok(Binding::from_raw(raw))
1199        }
1200    }
1201
1202    /// Get the object database for this repository
1203    pub fn odb(&self) -> Result<Odb<'_>, Error> {
1204        let mut odb = ptr::null_mut();
1205        unsafe {
1206            try_call!(raw::git_repository_odb(&mut odb, self.raw()));
1207            Ok(Odb::from_raw(odb))
1208        }
1209    }
1210
1211    /// Override the object database for this repository
1212    pub fn set_odb(&self, odb: &Odb<'_>) -> Result<(), Error> {
1213        unsafe {
1214            try_call!(raw::git_repository_set_odb(self.raw(), odb.raw()));
1215        }
1216        Ok(())
1217    }
1218
1219    /// Create a new branch pointing at a target commit
1220    ///
1221    /// A new direct reference will be created pointing to this target commit.
1222    /// If `force` is true and a reference already exists with the given name,
1223    /// it'll be replaced.
1224    pub fn branch(
1225        &self,
1226        branch_name: &str,
1227        target: &Commit<'_>,
1228        force: bool,
1229    ) -> Result<Branch<'_>, Error> {
1230        let branch_name = CString::new(branch_name)?;
1231        let mut raw = ptr::null_mut();
1232        unsafe {
1233            try_call!(raw::git_branch_create(
1234                &mut raw,
1235                self.raw(),
1236                branch_name,
1237                target.raw(),
1238                force
1239            ));
1240            Ok(Branch::wrap(Binding::from_raw(raw)))
1241        }
1242    }
1243
1244    /// Create a new branch pointing at a target commit
1245    ///
1246    /// This behaves like `Repository::branch()` but takes
1247    /// an annotated commit, which lets you specify which
1248    /// extended SHA syntax string was specified by a user,
1249    /// allowing for more exact reflog messages.
1250    ///
1251    /// See the documentation for `Repository::branch()`
1252    pub fn branch_from_annotated_commit(
1253        &self,
1254        branch_name: &str,
1255        target: &AnnotatedCommit<'_>,
1256        force: bool,
1257    ) -> Result<Branch<'_>, Error> {
1258        let branch_name = CString::new(branch_name)?;
1259        let mut raw = ptr::null_mut();
1260        unsafe {
1261            try_call!(raw::git_branch_create_from_annotated(
1262                &mut raw,
1263                self.raw(),
1264                branch_name,
1265                target.raw(),
1266                force
1267            ));
1268            Ok(Branch::wrap(Binding::from_raw(raw)))
1269        }
1270    }
1271
1272    /// Lookup a branch by its name in a repository.
1273    pub fn find_branch(&self, name: &str, branch_type: BranchType) -> Result<Branch<'_>, Error> {
1274        let name = CString::new(name)?;
1275        let mut ret = ptr::null_mut();
1276        unsafe {
1277            try_call!(raw::git_branch_lookup(
1278                &mut ret,
1279                self.raw(),
1280                name,
1281                branch_type
1282            ));
1283            Ok(Branch::wrap(Binding::from_raw(ret)))
1284        }
1285    }
1286
1287    /// Create new commit in the repository
1288    ///
1289    /// If the `update_ref` is not `None`, name of the reference that will be
1290    /// updated to point to this commit. If the reference is not direct, it will
1291    /// be resolved to a direct reference. Use "HEAD" to update the HEAD of the
1292    /// current branch and make it point to this commit. If the reference
1293    /// doesn't exist yet, it will be created. If it does exist, the first
1294    /// parent must be the tip of this branch.
1295    pub fn commit(
1296        &self,
1297        update_ref: Option<&str>,
1298        author: &Signature<'_>,
1299        committer: &Signature<'_>,
1300        message: &str,
1301        tree: &Tree<'_>,
1302        parents: &[&Commit<'_>],
1303    ) -> Result<Oid, Error> {
1304        let update_ref = crate::opt_cstr(update_ref)?;
1305        let mut parent_ptrs = parents
1306            .iter()
1307            .map(|p| p.raw() as *const raw::git_commit)
1308            .collect::<Vec<_>>();
1309        let message = CString::new(message)?;
1310        let mut raw = raw::git_oid {
1311            id: [0; raw::GIT_OID_RAWSZ],
1312        };
1313        unsafe {
1314            try_call!(raw::git_commit_create(
1315                &mut raw,
1316                self.raw(),
1317                update_ref,
1318                author.raw(),
1319                committer.raw(),
1320                ptr::null(),
1321                message,
1322                tree.raw(),
1323                parents.len() as size_t,
1324                parent_ptrs.as_mut_ptr()
1325            ));
1326            Ok(Binding::from_raw(&raw as *const _))
1327        }
1328    }
1329
1330    /// Create a commit object and return that as a Buf.
1331    ///
1332    /// That can be converted to a string like this `str::from_utf8(&buf).unwrap().to_string()`.
1333    /// And that string can be passed to the `commit_signed` function,
1334    /// the arguments behave the same as in the `commit` function.
1335    pub fn commit_create_buffer(
1336        &self,
1337        author: &Signature<'_>,
1338        committer: &Signature<'_>,
1339        message: &str,
1340        tree: &Tree<'_>,
1341        parents: &[&Commit<'_>],
1342    ) -> Result<Buf, Error> {
1343        let mut parent_ptrs = parents
1344            .iter()
1345            .map(|p| p.raw() as *const raw::git_commit)
1346            .collect::<Vec<_>>();
1347        let message = CString::new(message)?;
1348        let buf = Buf::new();
1349        unsafe {
1350            try_call!(raw::git_commit_create_buffer(
1351                buf.raw(),
1352                self.raw(),
1353                author.raw(),
1354                committer.raw(),
1355                ptr::null(),
1356                message,
1357                tree.raw(),
1358                parents.len() as size_t,
1359                parent_ptrs.as_mut_ptr()
1360            ));
1361            Ok(buf)
1362        }
1363    }
1364
1365    /// Create a commit object from the given buffer and signature
1366    ///
1367    /// Given the unsigned commit object's contents, its signature and the
1368    /// header field in which to store the signature, attach the signature to
1369    /// the commit and write it into the given repository.
1370    ///
1371    /// Use `None` in `signature_field` to use the default of `gpgsig`, which is
1372    /// almost certainly what you want.
1373    ///
1374    /// Returns the resulting (signed) commit id.
1375    pub fn commit_signed(
1376        &self,
1377        commit_content: &str,
1378        signature: &str,
1379        signature_field: Option<&str>,
1380    ) -> Result<Oid, Error> {
1381        let commit_content = CString::new(commit_content)?;
1382        let signature = CString::new(signature)?;
1383        let signature_field = crate::opt_cstr(signature_field)?;
1384        let mut raw = raw::git_oid {
1385            id: [0; raw::GIT_OID_RAWSZ],
1386        };
1387        unsafe {
1388            try_call!(raw::git_commit_create_with_signature(
1389                &mut raw,
1390                self.raw(),
1391                commit_content,
1392                signature,
1393                signature_field
1394            ));
1395            Ok(Binding::from_raw(&raw as *const _))
1396        }
1397    }
1398
1399    /// Extract the signature from a commit
1400    ///
1401    /// Returns a tuple containing the signature in the first value and the
1402    /// signed data in the second.
1403    pub fn extract_signature(
1404        &self,
1405        commit_id: &Oid,
1406        signature_field: Option<&str>,
1407    ) -> Result<(Buf, Buf), Error> {
1408        let signature_field = crate::opt_cstr(signature_field)?;
1409        let signature = Buf::new();
1410        let content = Buf::new();
1411        unsafe {
1412            try_call!(raw::git_commit_extract_signature(
1413                signature.raw(),
1414                content.raw(),
1415                self.raw(),
1416                commit_id.raw() as *mut _,
1417                signature_field
1418            ));
1419            Ok((signature, content))
1420        }
1421    }
1422
1423    /// Lookup a reference to one of the commits in a repository.
1424    pub fn find_commit(&self, oid: Oid) -> Result<Commit<'_>, Error> {
1425        let mut raw = ptr::null_mut();
1426        unsafe {
1427            try_call!(raw::git_commit_lookup(&mut raw, self.raw(), oid.raw()));
1428            Ok(Binding::from_raw(raw))
1429        }
1430    }
1431
1432    /// Lookup a reference to one of the commits in a repository by short hash.
1433    pub fn find_commit_by_prefix(&self, prefix_hash: &str) -> Result<Commit<'_>, Error> {
1434        let mut raw = ptr::null_mut();
1435        unsafe {
1436            try_call!(raw::git_commit_lookup_prefix(
1437                &mut raw,
1438                self.raw(),
1439                Oid::from_str(prefix_hash)?.raw(),
1440                prefix_hash.len()
1441            ));
1442            Ok(Binding::from_raw(raw))
1443        }
1444    }
1445
1446    /// Creates an `AnnotatedCommit` from the given commit id.
1447    pub fn find_annotated_commit(&self, id: Oid) -> Result<AnnotatedCommit<'_>, Error> {
1448        unsafe {
1449            let mut raw = ptr::null_mut();
1450            try_call!(raw::git_annotated_commit_lookup(
1451                &mut raw,
1452                self.raw(),
1453                id.raw()
1454            ));
1455            Ok(Binding::from_raw(raw))
1456        }
1457    }
1458
1459    /// Lookup a reference to one of the objects in a repository.
1460    pub fn find_object(&self, oid: Oid, kind: Option<ObjectType>) -> Result<Object<'_>, Error> {
1461        let mut raw = ptr::null_mut();
1462        unsafe {
1463            try_call!(raw::git_object_lookup(
1464                &mut raw,
1465                self.raw(),
1466                oid.raw(),
1467                kind
1468            ));
1469            Ok(Binding::from_raw(raw))
1470        }
1471    }
1472
1473    /// Lookup a reference to one of the objects by id prefix in a repository.
1474    pub fn find_object_by_prefix(
1475        &self,
1476        prefix_hash: &str,
1477        kind: Option<ObjectType>,
1478    ) -> Result<Object<'_>, Error> {
1479        let mut raw = ptr::null_mut();
1480        unsafe {
1481            try_call!(raw::git_object_lookup_prefix(
1482                &mut raw,
1483                self.raw(),
1484                Oid::from_str(prefix_hash)?.raw(),
1485                prefix_hash.len(),
1486                kind
1487            ));
1488            Ok(Binding::from_raw(raw))
1489        }
1490    }
1491
1492    /// Create a new direct reference.
1493    ///
1494    /// This function will return an error if a reference already exists with
1495    /// the given name unless force is true, in which case it will be
1496    /// overwritten.
1497    pub fn reference(
1498        &self,
1499        name: &str,
1500        id: Oid,
1501        force: bool,
1502        log_message: &str,
1503    ) -> Result<Reference<'_>, Error> {
1504        let name = CString::new(name)?;
1505        let log_message = CString::new(log_message)?;
1506        let mut raw = ptr::null_mut();
1507        unsafe {
1508            try_call!(raw::git_reference_create(
1509                &mut raw,
1510                self.raw(),
1511                name,
1512                id.raw(),
1513                force,
1514                log_message
1515            ));
1516            Ok(Binding::from_raw(raw))
1517        }
1518    }
1519
1520    /// Conditionally create new direct reference.
1521    ///
1522    /// A direct reference (also called an object id reference) refers directly
1523    /// to a specific object id (a.k.a. OID or SHA) in the repository.  The id
1524    /// permanently refers to the object (although the reference itself can be
1525    /// moved).  For example, in libgit2 the direct ref "refs/tags/v0.17.0"
1526    /// refers to OID 5b9fac39d8a76b9139667c26a63e6b3f204b3977.
1527    ///
1528    /// The direct reference will be created in the repository and written to
1529    /// the disk.
1530    ///
1531    /// Valid reference names must follow one of two patterns:
1532    ///
1533    /// 1. Top-level names must contain only capital letters and underscores,
1534    ///    and must begin and end with a letter.  (e.g.  "HEAD", "ORIG_HEAD").
1535    /// 2. Names prefixed with "refs/" can be almost anything.  You must avoid
1536    ///    the characters `~`, `^`, `:`, `\\`, `?`, `[`, and `*`, and the
1537    ///    sequences ".." and "@{" which have special meaning to revparse.
1538    ///
1539    /// This function will return an error if a reference already exists with
1540    /// the given name unless `force` is true, in which case it will be
1541    /// overwritten.
1542    ///
1543    /// The message for the reflog will be ignored if the reference does not
1544    /// belong in the standard set (HEAD, branches and remote-tracking
1545    /// branches) and it does not have a reflog.
1546    ///
1547    /// It will return GIT_EMODIFIED if the reference's value at the time of
1548    /// updating does not match the one passed through `current_id` (i.e. if the
1549    /// ref has changed since the user read it).
1550    pub fn reference_matching(
1551        &self,
1552        name: &str,
1553        id: Oid,
1554        force: bool,
1555        current_id: Oid,
1556        log_message: &str,
1557    ) -> Result<Reference<'_>, Error> {
1558        let name = CString::new(name)?;
1559        let log_message = CString::new(log_message)?;
1560        let mut raw = ptr::null_mut();
1561        unsafe {
1562            try_call!(raw::git_reference_create_matching(
1563                &mut raw,
1564                self.raw(),
1565                name,
1566                id.raw(),
1567                force,
1568                current_id.raw(),
1569                log_message
1570            ));
1571            Ok(Binding::from_raw(raw))
1572        }
1573    }
1574
1575    /// Create a new symbolic reference.
1576    ///
1577    /// A symbolic reference is a reference name that refers to another
1578    /// reference name.  If the other name moves, the symbolic name will move,
1579    /// too.  As a simple example, the "HEAD" reference might refer to
1580    /// "refs/heads/master" while on the "master" branch of a repository.
1581    ///
1582    /// Valid reference names must follow one of two patterns:
1583    ///
1584    /// 1. Top-level names must contain only capital letters and underscores,
1585    ///    and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
1586    /// 2. Names prefixed with "refs/" can be almost anything.  You must avoid
1587    ///    the characters '~', '^', ':', '\\', '?', '[', and '*', and the
1588    ///    sequences ".." and "@{" which have special meaning to revparse.
1589    ///
1590    /// This function will return an error if a reference already exists with
1591    /// the given name unless force is true, in which case it will be
1592    /// overwritten.
1593    pub fn reference_symbolic(
1594        &self,
1595        name: &str,
1596        target: &str,
1597        force: bool,
1598        log_message: &str,
1599    ) -> Result<Reference<'_>, Error> {
1600        let name = CString::new(name)?;
1601        let target = CString::new(target)?;
1602        let log_message = CString::new(log_message)?;
1603        let mut raw = ptr::null_mut();
1604        unsafe {
1605            try_call!(raw::git_reference_symbolic_create(
1606                &mut raw,
1607                self.raw(),
1608                name,
1609                target,
1610                force,
1611                log_message
1612            ));
1613            Ok(Binding::from_raw(raw))
1614        }
1615    }
1616
1617    /// Create a new symbolic reference.
1618    ///
1619    /// This function will return an error if a reference already exists with
1620    /// the given name unless force is true, in which case it will be
1621    /// overwritten.
1622    ///
1623    /// It will return GIT_EMODIFIED if the reference's value at the time of
1624    /// updating does not match the one passed through current_value (i.e. if
1625    /// the ref has changed since the user read it).
1626    pub fn reference_symbolic_matching(
1627        &self,
1628        name: &str,
1629        target: &str,
1630        force: bool,
1631        current_value: &str,
1632        log_message: &str,
1633    ) -> Result<Reference<'_>, Error> {
1634        let name = CString::new(name)?;
1635        let target = CString::new(target)?;
1636        let current_value = CString::new(current_value)?;
1637        let log_message = CString::new(log_message)?;
1638        let mut raw = ptr::null_mut();
1639        unsafe {
1640            try_call!(raw::git_reference_symbolic_create_matching(
1641                &mut raw,
1642                self.raw(),
1643                name,
1644                target,
1645                force,
1646                current_value,
1647                log_message
1648            ));
1649            Ok(Binding::from_raw(raw))
1650        }
1651    }
1652
1653    /// Lookup a reference to one of the objects in a repository.
1654    pub fn find_reference(&self, name: &str) -> Result<Reference<'_>, Error> {
1655        let name = CString::new(name)?;
1656        let mut raw = ptr::null_mut();
1657        unsafe {
1658            try_call!(raw::git_reference_lookup(&mut raw, self.raw(), name));
1659            Ok(Binding::from_raw(raw))
1660        }
1661    }
1662
1663    /// Lookup a reference to one of the objects in a repository.
1664    /// `Repository::find_reference` with teeth; give the method your reference in
1665    /// human-readable format e.g. 'main' instead of 'refs/heads/main', and it
1666    /// will do-what-you-mean, returning the `Reference`.
1667    pub fn resolve_reference_from_short_name(&self, refname: &str) -> Result<Reference<'_>, Error> {
1668        let refname = CString::new(refname)?;
1669        let mut raw = ptr::null_mut();
1670        unsafe {
1671            try_call!(raw::git_reference_dwim(&mut raw, self.raw(), refname));
1672            Ok(Binding::from_raw(raw))
1673        }
1674    }
1675
1676    /// Lookup a reference by name and resolve immediately to OID.
1677    ///
1678    /// This function provides a quick way to resolve a reference name straight
1679    /// through to the object id that it refers to. This avoids having to
1680    /// allocate or free any `Reference` objects for simple situations.
1681    pub fn refname_to_id(&self, name: &str) -> Result<Oid, Error> {
1682        let name = CString::new(name)?;
1683        let mut ret = raw::git_oid {
1684            id: [0; raw::GIT_OID_RAWSZ],
1685        };
1686        unsafe {
1687            try_call!(raw::git_reference_name_to_id(&mut ret, self.raw(), name));
1688            Ok(Binding::from_raw(&ret as *const _))
1689        }
1690    }
1691
1692    /// Creates a git_annotated_commit from the given reference.
1693    pub fn reference_to_annotated_commit(
1694        &self,
1695        reference: &Reference<'_>,
1696    ) -> Result<AnnotatedCommit<'_>, Error> {
1697        let mut ret = ptr::null_mut();
1698        unsafe {
1699            try_call!(raw::git_annotated_commit_from_ref(
1700                &mut ret,
1701                self.raw(),
1702                reference.raw()
1703            ));
1704            Ok(AnnotatedCommit::from_raw(ret))
1705        }
1706    }
1707
1708    /// Creates a git_annotated_commit from FETCH_HEAD.
1709    pub fn annotated_commit_from_fetchhead(
1710        &self,
1711        branch_name: &str,
1712        remote_url: &str,
1713        id: &Oid,
1714    ) -> Result<AnnotatedCommit<'_>, Error> {
1715        let branch_name = CString::new(branch_name)?;
1716        let remote_url = CString::new(remote_url)?;
1717
1718        let mut ret = ptr::null_mut();
1719        unsafe {
1720            try_call!(raw::git_annotated_commit_from_fetchhead(
1721                &mut ret,
1722                self.raw(),
1723                branch_name,
1724                remote_url,
1725                id.raw()
1726            ));
1727            Ok(AnnotatedCommit::from_raw(ret))
1728        }
1729    }
1730
1731    /// Create a new action signature with default user and now timestamp.
1732    ///
1733    /// This looks up the user.name and user.email from the configuration and
1734    /// uses the current time as the timestamp, and creates a new signature
1735    /// based on that information. It will return `NotFound` if either the
1736    /// user.name or user.email are not set.
1737    pub fn signature(&self) -> Result<Signature<'static>, Error> {
1738        let mut ret = ptr::null_mut();
1739        unsafe {
1740            try_call!(raw::git_signature_default(&mut ret, self.raw()));
1741            Ok(Binding::from_raw(ret))
1742        }
1743    }
1744
1745    /// Set up a new git submodule for checkout.
1746    ///
1747    /// This does "git submodule add" up to the fetch and checkout of the
1748    /// submodule contents. It preps a new submodule, creates an entry in
1749    /// `.gitmodules` and creates an empty initialized repository either at the
1750    /// given path in the working directory or in `.git/modules` with a gitlink
1751    /// from the working directory to the new repo.
1752    ///
1753    /// To fully emulate "git submodule add" call this function, then `open()`
1754    /// the submodule repo and perform the clone step as needed. Lastly, call
1755    /// `add_finalize()` to wrap up adding the new submodule and `.gitmodules`
1756    /// to the index to be ready to commit.
1757    pub fn submodule(
1758        &self,
1759        url: &str,
1760        path: &Path,
1761        use_gitlink: bool,
1762    ) -> Result<Submodule<'_>, Error> {
1763        let url = CString::new(url)?;
1764        let path = path_to_repo_path(path)?;
1765        let mut raw = ptr::null_mut();
1766        unsafe {
1767            try_call!(raw::git_submodule_add_setup(
1768                &mut raw,
1769                self.raw(),
1770                url,
1771                path,
1772                use_gitlink
1773            ));
1774            Ok(Binding::from_raw(raw))
1775        }
1776    }
1777
1778    /// Lookup submodule information by name or path.
1779    ///
1780    /// Given either the submodule name or path (they are usually the same),
1781    /// this returns a structure describing the submodule.
1782    pub fn find_submodule(&self, name: &str) -> Result<Submodule<'_>, Error> {
1783        let name = CString::new(name)?;
1784        let mut raw = ptr::null_mut();
1785        unsafe {
1786            try_call!(raw::git_submodule_lookup(&mut raw, self.raw(), name));
1787            Ok(Binding::from_raw(raw))
1788        }
1789    }
1790
1791    /// Get the status for a submodule.
1792    ///
1793    /// This looks at a submodule and tries to determine the status.  It
1794    /// will return a combination of the `SubmoduleStatus` values.
1795    pub fn submodule_status(
1796        &self,
1797        name: &str,
1798        ignore: SubmoduleIgnore,
1799    ) -> Result<SubmoduleStatus, Error> {
1800        let mut ret = 0;
1801        let name = CString::new(name)?;
1802        unsafe {
1803            try_call!(raw::git_submodule_status(&mut ret, self.raw, name, ignore));
1804        }
1805        Ok(SubmoduleStatus::from_bits_truncate(ret as u32))
1806    }
1807
1808    /// Set the ignore rule for the submodule in the configuration
1809    ///
1810    /// This does not affect any currently-loaded instances.
1811    pub fn submodule_set_ignore(
1812        &mut self,
1813        name: &str,
1814        ignore: SubmoduleIgnore,
1815    ) -> Result<(), Error> {
1816        let name = CString::new(name)?;
1817        unsafe {
1818            try_call!(raw::git_submodule_set_ignore(self.raw(), name, ignore));
1819        }
1820        Ok(())
1821    }
1822
1823    /// Set the update rule for the submodule in the configuration
1824    ///
1825    /// This setting won't affect any existing instances.
1826    pub fn submodule_set_update(
1827        &mut self,
1828        name: &str,
1829        update: SubmoduleUpdate,
1830    ) -> Result<(), Error> {
1831        let name = CString::new(name)?;
1832        unsafe {
1833            try_call!(raw::git_submodule_set_update(self.raw(), name, update));
1834        }
1835        Ok(())
1836    }
1837
1838    /// Set the URL for the submodule in the configuration
1839    ///
1840    /// After calling this, you may wish to call [`Submodule::sync`] to write
1841    /// the changes to the checked out submodule repository.
1842    pub fn submodule_set_url(&mut self, name: &str, url: &str) -> Result<(), Error> {
1843        let name = CString::new(name)?;
1844        let url = CString::new(url)?;
1845        unsafe {
1846            try_call!(raw::git_submodule_set_url(self.raw(), name, url));
1847        }
1848        Ok(())
1849    }
1850
1851    /// Set the branch for the submodule in the configuration
1852    ///
1853    /// After calling this, you may wish to call [`Submodule::sync`] to write
1854    /// the changes to the checked out submodule repository.
1855    pub fn submodule_set_branch(&mut self, name: &str, branch_name: &str) -> Result<(), Error> {
1856        let name = CString::new(name)?;
1857        let branch_name = CString::new(branch_name)?;
1858        unsafe {
1859            try_call!(raw::git_submodule_set_branch(self.raw(), name, branch_name));
1860        }
1861        Ok(())
1862    }
1863
1864    /// Lookup a reference to one of the objects in a repository.
1865    pub fn find_tree(&self, oid: Oid) -> Result<Tree<'_>, Error> {
1866        let mut raw = ptr::null_mut();
1867        unsafe {
1868            try_call!(raw::git_tree_lookup(&mut raw, self.raw(), oid.raw()));
1869            Ok(Binding::from_raw(raw))
1870        }
1871    }
1872
1873    /// Create a new TreeBuilder, optionally initialized with the
1874    /// entries of the given Tree.
1875    ///
1876    /// The tree builder can be used to create or modify trees in memory and
1877    /// write them as tree objects to the database.
1878    pub fn treebuilder(&self, tree: Option<&Tree<'_>>) -> Result<TreeBuilder<'_>, Error> {
1879        unsafe {
1880            let mut ret = ptr::null_mut();
1881            let tree = match tree {
1882                Some(tree) => tree.raw(),
1883                None => ptr::null_mut(),
1884            };
1885            try_call!(raw::git_treebuilder_new(&mut ret, self.raw, tree));
1886            Ok(Binding::from_raw(ret))
1887        }
1888    }
1889
1890    /// Create a new tag in the repository from an object
1891    ///
1892    /// A new reference will also be created pointing to this tag object. If
1893    /// `force` is true and a reference already exists with the given name,
1894    /// it'll be replaced.
1895    ///
1896    /// The message will not be cleaned up.
1897    ///
1898    /// The tag name will be checked for validity. You must avoid the characters
1899    /// '~', '^', ':', ' \ ', '?', '[', and '*', and the sequences ".." and " @
1900    /// {" which have special meaning to revparse.
1901    pub fn tag(
1902        &self,
1903        name: &str,
1904        target: &Object<'_>,
1905        tagger: &Signature<'_>,
1906        message: &str,
1907        force: bool,
1908    ) -> Result<Oid, Error> {
1909        let name = CString::new(name)?;
1910        let message = CString::new(message)?;
1911        let mut raw = raw::git_oid {
1912            id: [0; raw::GIT_OID_RAWSZ],
1913        };
1914        unsafe {
1915            try_call!(raw::git_tag_create(
1916                &mut raw,
1917                self.raw,
1918                name,
1919                target.raw(),
1920                tagger.raw(),
1921                message,
1922                force
1923            ));
1924            Ok(Binding::from_raw(&raw as *const _))
1925        }
1926    }
1927
1928    /// Create a new tag in the repository from an object without creating a reference.
1929    ///
1930    /// The message will not be cleaned up.
1931    ///
1932    /// The tag name will be checked for validity. You must avoid the characters
1933    /// '~', '^', ':', ' \ ', '?', '[', and '*', and the sequences ".." and " @
1934    /// {" which have special meaning to revparse.
1935    pub fn tag_annotation_create(
1936        &self,
1937        name: &str,
1938        target: &Object<'_>,
1939        tagger: &Signature<'_>,
1940        message: &str,
1941    ) -> Result<Oid, Error> {
1942        let name = CString::new(name)?;
1943        let message = CString::new(message)?;
1944        let mut raw_oid = raw::git_oid {
1945            id: [0; raw::GIT_OID_RAWSZ],
1946        };
1947        unsafe {
1948            try_call!(raw::git_tag_annotation_create(
1949                &mut raw_oid,
1950                self.raw,
1951                name,
1952                target.raw(),
1953                tagger.raw(),
1954                message
1955            ));
1956            Ok(Binding::from_raw(&raw_oid as *const _))
1957        }
1958    }
1959
1960    /// Create a new lightweight tag pointing at a target object
1961    ///
1962    /// A new direct reference will be created pointing to this target object.
1963    /// If force is true and a reference already exists with the given name,
1964    /// it'll be replaced.
1965    pub fn tag_lightweight(
1966        &self,
1967        name: &str,
1968        target: &Object<'_>,
1969        force: bool,
1970    ) -> Result<Oid, Error> {
1971        let name = CString::new(name)?;
1972        let mut raw = raw::git_oid {
1973            id: [0; raw::GIT_OID_RAWSZ],
1974        };
1975        unsafe {
1976            try_call!(raw::git_tag_create_lightweight(
1977                &mut raw,
1978                self.raw,
1979                name,
1980                target.raw(),
1981                force
1982            ));
1983            Ok(Binding::from_raw(&raw as *const _))
1984        }
1985    }
1986
1987    /// Lookup a tag object from the repository.
1988    pub fn find_tag(&self, id: Oid) -> Result<Tag<'_>, Error> {
1989        let mut raw = ptr::null_mut();
1990        unsafe {
1991            try_call!(raw::git_tag_lookup(&mut raw, self.raw, id.raw()));
1992            Ok(Binding::from_raw(raw))
1993        }
1994    }
1995
1996    /// Lookup a tag object by prefix hash from the repository.
1997    pub fn find_tag_by_prefix(&self, prefix_hash: &str) -> Result<Tag<'_>, Error> {
1998        let mut raw = ptr::null_mut();
1999        unsafe {
2000            try_call!(raw::git_tag_lookup_prefix(
2001                &mut raw,
2002                self.raw,
2003                Oid::from_str(prefix_hash)?.raw(),
2004                prefix_hash.len()
2005            ));
2006            Ok(Binding::from_raw(raw))
2007        }
2008    }
2009
2010    /// Delete an existing tag reference.
2011    ///
2012    /// The tag name will be checked for validity, see `tag` for some rules
2013    /// about valid names.
2014    pub fn tag_delete(&self, name: &str) -> Result<(), Error> {
2015        let name = CString::new(name)?;
2016        unsafe {
2017            try_call!(raw::git_tag_delete(self.raw, name));
2018            Ok(())
2019        }
2020    }
2021
2022    /// Get a list with all the tags in the repository.
2023    ///
2024    /// An optional fnmatch pattern can also be specified.
2025    pub fn tag_names(&self, pattern: Option<&str>) -> Result<StringArray, Error> {
2026        let mut arr = raw::git_strarray {
2027            strings: ptr::null_mut(),
2028            count: 0,
2029        };
2030        unsafe {
2031            match pattern {
2032                Some(s) => {
2033                    let s = CString::new(s)?;
2034                    try_call!(raw::git_tag_list_match(&mut arr, s, self.raw));
2035                }
2036                None => {
2037                    try_call!(raw::git_tag_list(&mut arr, self.raw));
2038                }
2039            }
2040            Ok(Binding::from_raw(arr))
2041        }
2042    }
2043
2044    /// iterate over all tags calling `cb` on each.
2045    /// the callback is provided the tag id and name
2046    pub fn tag_foreach<T>(&self, cb: T) -> Result<(), Error>
2047    where
2048        T: FnMut(Oid, &[u8]) -> bool,
2049    {
2050        let mut data = TagForeachData {
2051            cb: Box::new(cb) as TagForeachCB<'_>,
2052        };
2053
2054        unsafe {
2055            raw::git_tag_foreach(
2056                self.raw,
2057                Some(tag_foreach_cb),
2058                (&mut data) as *mut _ as *mut _,
2059            );
2060        }
2061        Ok(())
2062    }
2063
2064    /// Updates files in the index and the working tree to match the content of
2065    /// the commit pointed at by HEAD.
2066    pub fn checkout_head(&self, opts: Option<&mut CheckoutBuilder<'_>>) -> Result<(), Error> {
2067        unsafe {
2068            let mut raw_opts = mem::zeroed();
2069            try_call!(raw::git_checkout_init_options(
2070                &mut raw_opts,
2071                raw::GIT_CHECKOUT_OPTIONS_VERSION
2072            ));
2073            if let Some(c) = opts {
2074                c.configure(&mut raw_opts);
2075            }
2076
2077            try_call!(raw::git_checkout_head(self.raw, &raw_opts));
2078        }
2079        Ok(())
2080    }
2081
2082    /// Updates files in the working tree to match the content of the index.
2083    ///
2084    /// If the index is `None`, the repository's index will be used.
2085    pub fn checkout_index(
2086        &self,
2087        index: Option<&mut Index>,
2088        opts: Option<&mut CheckoutBuilder<'_>>,
2089    ) -> Result<(), Error> {
2090        unsafe {
2091            let mut raw_opts = mem::zeroed();
2092            try_call!(raw::git_checkout_init_options(
2093                &mut raw_opts,
2094                raw::GIT_CHECKOUT_OPTIONS_VERSION
2095            ));
2096            if let Some(c) = opts {
2097                c.configure(&mut raw_opts);
2098            }
2099
2100            try_call!(raw::git_checkout_index(
2101                self.raw,
2102                index.map(|i| &mut *i.raw()),
2103                &raw_opts
2104            ));
2105        }
2106        Ok(())
2107    }
2108
2109    /// Updates files in the index and working tree to match the content of the
2110    /// tree pointed at by the treeish.
2111    pub fn checkout_tree(
2112        &self,
2113        treeish: &Object<'_>,
2114        opts: Option<&mut CheckoutBuilder<'_>>,
2115    ) -> Result<(), Error> {
2116        unsafe {
2117            let mut raw_opts = mem::zeroed();
2118            try_call!(raw::git_checkout_init_options(
2119                &mut raw_opts,
2120                raw::GIT_CHECKOUT_OPTIONS_VERSION
2121            ));
2122            if let Some(c) = opts {
2123                c.configure(&mut raw_opts);
2124            }
2125
2126            try_call!(raw::git_checkout_tree(self.raw, &*treeish.raw(), &raw_opts));
2127        }
2128        Ok(())
2129    }
2130
2131    /// Merges the given commit(s) into HEAD, writing the results into the
2132    /// working directory. Any changes are staged for commit and any conflicts
2133    /// are written to the index. Callers should inspect the repository's index
2134    /// after this completes, resolve any conflicts and prepare a commit.
2135    ///
2136    /// For compatibility with git, the repository is put into a merging state.
2137    /// Once the commit is done (or if the user wishes to abort), you should
2138    /// clear this state by calling cleanup_state().
2139    pub fn merge(
2140        &self,
2141        annotated_commits: &[&AnnotatedCommit<'_>],
2142        merge_opts: Option<&mut MergeOptions>,
2143        checkout_opts: Option<&mut CheckoutBuilder<'_>>,
2144    ) -> Result<(), Error> {
2145        unsafe {
2146            let mut raw_checkout_opts = mem::zeroed();
2147            try_call!(raw::git_checkout_init_options(
2148                &mut raw_checkout_opts,
2149                raw::GIT_CHECKOUT_OPTIONS_VERSION
2150            ));
2151            if let Some(c) = checkout_opts {
2152                c.configure(&mut raw_checkout_opts);
2153            }
2154
2155            let mut commit_ptrs = annotated_commits
2156                .iter()
2157                .map(|c| c.raw() as *const raw::git_annotated_commit)
2158                .collect::<Vec<_>>();
2159
2160            try_call!(raw::git_merge(
2161                self.raw,
2162                commit_ptrs.as_mut_ptr(),
2163                annotated_commits.len() as size_t,
2164                merge_opts.map(|o| o.raw()).unwrap_or(ptr::null()),
2165                &raw_checkout_opts
2166            ));
2167        }
2168        Ok(())
2169    }
2170
2171    /// Merge two commits, producing an index that reflects the result of
2172    /// the merge. The index may be written as-is to the working directory or
2173    /// checked out. If the index is to be converted to a tree, the caller
2174    /// should resolve any conflicts that arose as part of the merge.
2175    pub fn merge_commits(
2176        &self,
2177        our_commit: &Commit<'_>,
2178        their_commit: &Commit<'_>,
2179        opts: Option<&MergeOptions>,
2180    ) -> Result<Index, Error> {
2181        let mut raw = ptr::null_mut();
2182        unsafe {
2183            try_call!(raw::git_merge_commits(
2184                &mut raw,
2185                self.raw,
2186                our_commit.raw(),
2187                their_commit.raw(),
2188                opts.map(|o| o.raw())
2189            ));
2190            Ok(Binding::from_raw(raw))
2191        }
2192    }
2193
2194    /// Merge two trees, producing an index that reflects the result of
2195    /// the merge. The index may be written as-is to the working directory or
2196    /// checked out. If the index is to be converted to a tree, the caller
2197    /// should resolve any conflicts that arose as part of the merge.
2198    pub fn merge_trees(
2199        &self,
2200        ancestor_tree: &Tree<'_>,
2201        our_tree: &Tree<'_>,
2202        their_tree: &Tree<'_>,
2203        opts: Option<&MergeOptions>,
2204    ) -> Result<Index, Error> {
2205        let mut raw = ptr::null_mut();
2206        unsafe {
2207            try_call!(raw::git_merge_trees(
2208                &mut raw,
2209                self.raw,
2210                ancestor_tree.raw(),
2211                our_tree.raw(),
2212                their_tree.raw(),
2213                opts.map(|o| o.raw())
2214            ));
2215            Ok(Binding::from_raw(raw))
2216        }
2217    }
2218
2219    /// Remove all the metadata associated with an ongoing command like merge,
2220    /// revert, cherry-pick, etc. For example: MERGE_HEAD, MERGE_MSG, etc.
2221    pub fn cleanup_state(&self) -> Result<(), Error> {
2222        unsafe {
2223            try_call!(raw::git_repository_state_cleanup(self.raw));
2224        }
2225        Ok(())
2226    }
2227
2228    /// Analyzes the given branch(es) and determines the opportunities for
2229    /// merging them into the HEAD of the repository.
2230    pub fn merge_analysis(
2231        &self,
2232        their_heads: &[&AnnotatedCommit<'_>],
2233    ) -> Result<(MergeAnalysis, MergePreference), Error> {
2234        unsafe {
2235            let mut raw_merge_analysis = 0 as raw::git_merge_analysis_t;
2236            let mut raw_merge_preference = 0 as raw::git_merge_preference_t;
2237            let mut their_heads = their_heads
2238                .iter()
2239                .map(|v| v.raw() as *const _)
2240                .collect::<Vec<_>>();
2241            try_call!(raw::git_merge_analysis(
2242                &mut raw_merge_analysis,
2243                &mut raw_merge_preference,
2244                self.raw,
2245                their_heads.as_mut_ptr() as *mut _,
2246                their_heads.len()
2247            ));
2248            Ok((
2249                MergeAnalysis::from_bits_truncate(raw_merge_analysis as u32),
2250                MergePreference::from_bits_truncate(raw_merge_preference as u32),
2251            ))
2252        }
2253    }
2254
2255    /// Analyzes the given branch(es) and determines the opportunities for
2256    /// merging them into a reference.
2257    pub fn merge_analysis_for_ref(
2258        &self,
2259        our_ref: &Reference<'_>,
2260        their_heads: &[&AnnotatedCommit<'_>],
2261    ) -> Result<(MergeAnalysis, MergePreference), Error> {
2262        unsafe {
2263            let mut raw_merge_analysis = 0 as raw::git_merge_analysis_t;
2264            let mut raw_merge_preference = 0 as raw::git_merge_preference_t;
2265            let mut their_heads = their_heads
2266                .iter()
2267                .map(|v| v.raw() as *const _)
2268                .collect::<Vec<_>>();
2269            try_call!(raw::git_merge_analysis_for_ref(
2270                &mut raw_merge_analysis,
2271                &mut raw_merge_preference,
2272                self.raw,
2273                our_ref.raw(),
2274                their_heads.as_mut_ptr() as *mut _,
2275                their_heads.len()
2276            ));
2277            Ok((
2278                MergeAnalysis::from_bits_truncate(raw_merge_analysis as u32),
2279                MergePreference::from_bits_truncate(raw_merge_preference as u32),
2280            ))
2281        }
2282    }
2283
2284    /// Initializes a rebase operation to rebase the changes in `branch`
2285    /// relative to `upstream` onto another branch. To begin the rebase process,
2286    /// call `next()`.
2287    pub fn rebase(
2288        &self,
2289        branch: Option<&AnnotatedCommit<'_>>,
2290        upstream: Option<&AnnotatedCommit<'_>>,
2291        onto: Option<&AnnotatedCommit<'_>>,
2292        opts: Option<&mut RebaseOptions<'_>>,
2293    ) -> Result<Rebase<'_>, Error> {
2294        let mut rebase: *mut raw::git_rebase = ptr::null_mut();
2295        unsafe {
2296            try_call!(raw::git_rebase_init(
2297                &mut rebase,
2298                self.raw(),
2299                branch.map(|c| c.raw()),
2300                upstream.map(|c| c.raw()),
2301                onto.map(|c| c.raw()),
2302                opts.map(|o| o.raw()).unwrap_or(ptr::null())
2303            ));
2304
2305            Ok(Rebase::from_raw(rebase))
2306        }
2307    }
2308
2309    /// Opens an existing rebase that was previously started by either an
2310    /// invocation of `rebase()` or by another client.
2311    pub fn open_rebase(&self, opts: Option<&mut RebaseOptions<'_>>) -> Result<Rebase<'_>, Error> {
2312        let mut rebase: *mut raw::git_rebase = ptr::null_mut();
2313        unsafe {
2314            try_call!(raw::git_rebase_open(
2315                &mut rebase,
2316                self.raw(),
2317                opts.map(|o| o.raw()).unwrap_or(ptr::null())
2318            ));
2319            Ok(Rebase::from_raw(rebase))
2320        }
2321    }
2322
2323    /// Add a note for an object
2324    ///
2325    /// The `notes_ref` argument is the canonical name of the reference to use,
2326    /// defaulting to "refs/notes/commits". If `force` is specified then
2327    /// previous notes are overwritten.
2328    pub fn note(
2329        &self,
2330        author: &Signature<'_>,
2331        committer: &Signature<'_>,
2332        notes_ref: Option<&str>,
2333        oid: Oid,
2334        note: &str,
2335        force: bool,
2336    ) -> Result<Oid, Error> {
2337        let notes_ref = crate::opt_cstr(notes_ref)?;
2338        let note = CString::new(note)?;
2339        let mut ret = raw::git_oid {
2340            id: [0; raw::GIT_OID_RAWSZ],
2341        };
2342        unsafe {
2343            try_call!(raw::git_note_create(
2344                &mut ret,
2345                self.raw,
2346                notes_ref,
2347                author.raw(),
2348                committer.raw(),
2349                oid.raw(),
2350                note,
2351                force
2352            ));
2353            Ok(Binding::from_raw(&ret as *const _))
2354        }
2355    }
2356
2357    /// Get the default notes reference for this repository
2358    pub fn note_default_ref(&self) -> Result<String, Error> {
2359        let ret = Buf::new();
2360        unsafe {
2361            try_call!(raw::git_note_default_ref(ret.raw(), self.raw));
2362        }
2363        Ok(str::from_utf8(&ret).unwrap().to_string())
2364    }
2365
2366    /// Creates a new iterator for notes in this repository.
2367    ///
2368    /// The `notes_ref` argument is the canonical name of the reference to use,
2369    /// defaulting to "refs/notes/commits".
2370    ///
2371    /// The iterator returned yields pairs of (Oid, Oid) where the first element
2372    /// is the id of the note and the second id is the id the note is
2373    /// annotating.
2374    pub fn notes(&self, notes_ref: Option<&str>) -> Result<Notes<'_>, Error> {
2375        let notes_ref = crate::opt_cstr(notes_ref)?;
2376        let mut ret = ptr::null_mut();
2377        unsafe {
2378            try_call!(raw::git_note_iterator_new(&mut ret, self.raw, notes_ref));
2379            Ok(Binding::from_raw(ret))
2380        }
2381    }
2382
2383    /// Read the note for an object.
2384    ///
2385    /// The `notes_ref` argument is the canonical name of the reference to use,
2386    /// defaulting to "refs/notes/commits".
2387    ///
2388    /// The id specified is the Oid of the git object to read the note from.
2389    pub fn find_note(&self, notes_ref: Option<&str>, id: Oid) -> Result<Note<'_>, Error> {
2390        let notes_ref = crate::opt_cstr(notes_ref)?;
2391        let mut ret = ptr::null_mut();
2392        unsafe {
2393            try_call!(raw::git_note_read(&mut ret, self.raw, notes_ref, id.raw()));
2394            Ok(Binding::from_raw(ret))
2395        }
2396    }
2397
2398    /// Remove the note for an object.
2399    ///
2400    /// The `notes_ref` argument is the canonical name of the reference to use,
2401    /// defaulting to "refs/notes/commits".
2402    ///
2403    /// The id specified is the Oid of the git object to remove the note from.
2404    pub fn note_delete(
2405        &self,
2406        id: Oid,
2407        notes_ref: Option<&str>,
2408        author: &Signature<'_>,
2409        committer: &Signature<'_>,
2410    ) -> Result<(), Error> {
2411        let notes_ref = crate::opt_cstr(notes_ref)?;
2412        unsafe {
2413            try_call!(raw::git_note_remove(
2414                self.raw,
2415                notes_ref,
2416                author.raw(),
2417                committer.raw(),
2418                id.raw()
2419            ));
2420            Ok(())
2421        }
2422    }
2423
2424    /// Create a revwalk that can be used to traverse the commit graph.
2425    pub fn revwalk(&self) -> Result<Revwalk<'_>, Error> {
2426        let mut raw = ptr::null_mut();
2427        unsafe {
2428            try_call!(raw::git_revwalk_new(&mut raw, self.raw()));
2429            Ok(Binding::from_raw(raw))
2430        }
2431    }
2432
2433    /// Get the blame for a single file.
2434    pub fn blame_file(
2435        &self,
2436        path: &Path,
2437        opts: Option<&mut BlameOptions>,
2438    ) -> Result<Blame<'_>, Error> {
2439        let path = path_to_repo_path(path)?;
2440        let mut raw = ptr::null_mut();
2441
2442        unsafe {
2443            try_call!(raw::git_blame_file(
2444                &mut raw,
2445                self.raw(),
2446                path,
2447                opts.map(|s| s.raw())
2448            ));
2449            Ok(Binding::from_raw(raw))
2450        }
2451    }
2452
2453    /// Find a merge base between two commits
2454    pub fn merge_base(&self, one: Oid, two: Oid) -> Result<Oid, Error> {
2455        let mut raw = raw::git_oid {
2456            id: [0; raw::GIT_OID_RAWSZ],
2457        };
2458        unsafe {
2459            try_call!(raw::git_merge_base(
2460                &mut raw,
2461                self.raw,
2462                one.raw(),
2463                two.raw()
2464            ));
2465            Ok(Binding::from_raw(&raw as *const _))
2466        }
2467    }
2468
2469    /// Find a merge base given a list of commits
2470    ///
2471    /// This behaves similar to [`git merge-base`](https://git-scm.com/docs/git-merge-base#_discussion).
2472    /// Given three commits `a`, `b`, and `c`, `merge_base_many(&[a, b, c])`
2473    /// will compute a hypothetical commit `m`, which is a merge between `b`
2474    /// and `c`.
2475    ///
2476    /// For example, with the following topology:
2477    /// ```text
2478    ///        o---o---o---o---C
2479    ///       /
2480    ///      /   o---o---o---B
2481    ///     /   /
2482    /// ---2---1---o---o---o---A
2483    /// ```
2484    ///
2485    /// the result of `merge_base_many(&[a, b, c])` is 1. This is because the
2486    /// equivalent topology with a merge commit `m` between `b` and `c` would
2487    /// is:
2488    /// ```text
2489    ///        o---o---o---o---o
2490    ///       /                 \
2491    ///      /   o---o---o---o---M
2492    ///     /   /
2493    /// ---2---1---o---o---o---A
2494    /// ```
2495    ///
2496    /// and the result of `merge_base_many(&[a, m])` is 1.
2497    ///
2498    /// ---
2499    ///
2500    /// If you're looking to recieve the common merge base between all the
2501    /// given commits, use [`Self::merge_base_octopus`].
2502    pub fn merge_base_many(&self, oids: &[Oid]) -> Result<Oid, Error> {
2503        let mut raw = raw::git_oid {
2504            id: [0; raw::GIT_OID_RAWSZ],
2505        };
2506
2507        unsafe {
2508            try_call!(raw::git_merge_base_many(
2509                &mut raw,
2510                self.raw,
2511                oids.len() as size_t,
2512                oids.as_ptr() as *const raw::git_oid
2513            ));
2514            Ok(Binding::from_raw(&raw as *const _))
2515        }
2516    }
2517
2518    /// Find a common merge base between all given a list of commits
2519    pub fn merge_base_octopus(&self, oids: &[Oid]) -> Result<Oid, Error> {
2520        let mut raw = raw::git_oid {
2521            id: [0; raw::GIT_OID_RAWSZ],
2522        };
2523
2524        unsafe {
2525            try_call!(raw::git_merge_base_octopus(
2526                &mut raw,
2527                self.raw,
2528                oids.len() as size_t,
2529                oids.as_ptr() as *const raw::git_oid
2530            ));
2531            Ok(Binding::from_raw(&raw as *const _))
2532        }
2533    }
2534
2535    /// Find all merge bases between two commits
2536    pub fn merge_bases(&self, one: Oid, two: Oid) -> Result<OidArray, Error> {
2537        let mut arr = raw::git_oidarray {
2538            ids: ptr::null_mut(),
2539            count: 0,
2540        };
2541        unsafe {
2542            try_call!(raw::git_merge_bases(
2543                &mut arr,
2544                self.raw,
2545                one.raw(),
2546                two.raw()
2547            ));
2548            Ok(Binding::from_raw(arr))
2549        }
2550    }
2551
2552    /// Find all merge bases given a list of commits
2553    pub fn merge_bases_many(&self, oids: &[Oid]) -> Result<OidArray, Error> {
2554        let mut arr = raw::git_oidarray {
2555            ids: ptr::null_mut(),
2556            count: 0,
2557        };
2558        unsafe {
2559            try_call!(raw::git_merge_bases_many(
2560                &mut arr,
2561                self.raw,
2562                oids.len() as size_t,
2563                oids.as_ptr() as *const raw::git_oid
2564            ));
2565            Ok(Binding::from_raw(arr))
2566        }
2567    }
2568
2569    /// Count the number of unique commits between two commit objects
2570    ///
2571    /// There is no need for branches containing the commits to have any
2572    /// upstream relationship, but it helps to think of one as a branch and the
2573    /// other as its upstream, the ahead and behind values will be what git
2574    /// would report for the branches.
2575    pub fn graph_ahead_behind(&self, local: Oid, upstream: Oid) -> Result<(usize, usize), Error> {
2576        unsafe {
2577            let mut ahead: size_t = 0;
2578            let mut behind: size_t = 0;
2579            try_call!(raw::git_graph_ahead_behind(
2580                &mut ahead,
2581                &mut behind,
2582                self.raw(),
2583                local.raw(),
2584                upstream.raw()
2585            ));
2586            Ok((ahead as usize, behind as usize))
2587        }
2588    }
2589
2590    /// Determine if a commit is the descendant of another commit
2591    ///
2592    /// Note that a commit is not considered a descendant of itself, in contrast
2593    /// to `git merge-base --is-ancestor`.
2594    pub fn graph_descendant_of(&self, commit: Oid, ancestor: Oid) -> Result<bool, Error> {
2595        unsafe {
2596            let rv = try_call!(raw::git_graph_descendant_of(
2597                self.raw(),
2598                commit.raw(),
2599                ancestor.raw()
2600            ));
2601            Ok(rv != 0)
2602        }
2603    }
2604
2605    /// Read the reflog for the given reference
2606    ///
2607    /// If there is no reflog file for the given reference yet, an empty reflog
2608    /// object will be returned.
2609    pub fn reflog(&self, name: &str) -> Result<Reflog, Error> {
2610        let name = CString::new(name)?;
2611        let mut ret = ptr::null_mut();
2612        unsafe {
2613            try_call!(raw::git_reflog_read(&mut ret, self.raw, name));
2614            Ok(Binding::from_raw(ret))
2615        }
2616    }
2617
2618    /// Delete the reflog for the given reference
2619    pub fn reflog_delete(&self, name: &str) -> Result<(), Error> {
2620        let name = CString::new(name)?;
2621        unsafe {
2622            try_call!(raw::git_reflog_delete(self.raw, name));
2623        }
2624        Ok(())
2625    }
2626
2627    /// Rename a reflog
2628    ///
2629    /// The reflog to be renamed is expected to already exist.
2630    pub fn reflog_rename(&self, old_name: &str, new_name: &str) -> Result<(), Error> {
2631        let old_name = CString::new(old_name)?;
2632        let new_name = CString::new(new_name)?;
2633        unsafe {
2634            try_call!(raw::git_reflog_rename(self.raw, old_name, new_name));
2635        }
2636        Ok(())
2637    }
2638
2639    /// Check if the given reference has a reflog.
2640    pub fn reference_has_log(&self, name: &str) -> Result<bool, Error> {
2641        let name = CString::new(name)?;
2642        let ret = unsafe { try_call!(raw::git_reference_has_log(self.raw, name)) };
2643        Ok(ret != 0)
2644    }
2645
2646    /// Ensure that the given reference has a reflog.
2647    pub fn reference_ensure_log(&self, name: &str) -> Result<(), Error> {
2648        let name = CString::new(name)?;
2649        unsafe {
2650            try_call!(raw::git_reference_ensure_log(self.raw, name));
2651        }
2652        Ok(())
2653    }
2654
2655    /// Describes a commit
2656    ///
2657    /// Performs a describe operation on the current commit and the worktree.
2658    /// After performing a describe on HEAD, a status is run and description is
2659    /// considered to be dirty if there are.
2660    pub fn describe(&self, opts: &DescribeOptions) -> Result<Describe<'_>, Error> {
2661        let mut ret = ptr::null_mut();
2662        unsafe {
2663            try_call!(raw::git_describe_workdir(&mut ret, self.raw, opts.raw()));
2664            Ok(Binding::from_raw(ret))
2665        }
2666    }
2667
2668    /// Directly run a diff on two blobs.
2669    ///
2670    /// Compared to a file, a blob lacks some contextual information. As such, the
2671    /// `DiffFile` given to the callback will have some fake data; i.e. mode will be
2672    /// 0 and path will be `None`.
2673    ///
2674    /// `None` is allowed for either `old_blob` or `new_blob` and will be treated
2675    /// as an empty blob, with the oid set to zero in the `DiffFile`. Passing `None`
2676    /// for both blobs is a noop; no callbacks will be made at all.
2677    ///
2678    /// We do run a binary content check on the blob content and if either blob looks
2679    /// like binary data, the `DiffFile` binary attribute will be set to 1 and no call to
2680    /// the `hunk_cb` nor `line_cb` will be made (unless you set the `force_text`
2681    /// option).
2682    pub fn diff_blobs(
2683        &self,
2684        old_blob: Option<&Blob<'_>>,
2685        old_as_path: Option<&str>,
2686        new_blob: Option<&Blob<'_>>,
2687        new_as_path: Option<&str>,
2688        opts: Option<&mut DiffOptions>,
2689        file_cb: Option<&mut FileCb<'_>>,
2690        binary_cb: Option<&mut BinaryCb<'_>>,
2691        hunk_cb: Option<&mut HunkCb<'_>>,
2692        line_cb: Option<&mut LineCb<'_>>,
2693    ) -> Result<(), Error> {
2694        let old_as_path = crate::opt_cstr(old_as_path)?;
2695        let new_as_path = crate::opt_cstr(new_as_path)?;
2696        let mut cbs = DiffCallbacks {
2697            file: file_cb,
2698            binary: binary_cb,
2699            hunk: hunk_cb,
2700            line: line_cb,
2701        };
2702        let ptr = &mut cbs as *mut _;
2703        unsafe {
2704            let file_cb_c: raw::git_diff_file_cb = if cbs.file.is_some() {
2705                Some(file_cb_c)
2706            } else {
2707                None
2708            };
2709            let binary_cb_c: raw::git_diff_binary_cb = if cbs.binary.is_some() {
2710                Some(binary_cb_c)
2711            } else {
2712                None
2713            };
2714            let hunk_cb_c: raw::git_diff_hunk_cb = if cbs.hunk.is_some() {
2715                Some(hunk_cb_c)
2716            } else {
2717                None
2718            };
2719            let line_cb_c: raw::git_diff_line_cb = if cbs.line.is_some() {
2720                Some(line_cb_c)
2721            } else {
2722                None
2723            };
2724            try_call!(raw::git_diff_blobs(
2725                old_blob.map(|s| s.raw()),
2726                old_as_path,
2727                new_blob.map(|s| s.raw()),
2728                new_as_path,
2729                opts.map(|s| s.raw()),
2730                file_cb_c,
2731                binary_cb_c,
2732                hunk_cb_c,
2733                line_cb_c,
2734                ptr as *mut _
2735            ));
2736            Ok(())
2737        }
2738    }
2739
2740    /// Create a diff with the difference between two tree objects.
2741    ///
2742    /// This is equivalent to `git diff <old-tree> <new-tree>`
2743    ///
2744    /// The first tree will be used for the "old_file" side of the delta and the
2745    /// second tree will be used for the "new_file" side of the delta.  You can
2746    /// pass `None` to indicate an empty tree, although it is an error to pass
2747    /// `None` for both the `old_tree` and `new_tree`.
2748    pub fn diff_tree_to_tree(
2749        &self,
2750        old_tree: Option<&Tree<'_>>,
2751        new_tree: Option<&Tree<'_>>,
2752        opts: Option<&mut DiffOptions>,
2753    ) -> Result<Diff<'_>, Error> {
2754        let mut ret = ptr::null_mut();
2755        unsafe {
2756            try_call!(raw::git_diff_tree_to_tree(
2757                &mut ret,
2758                self.raw(),
2759                old_tree.map(|s| s.raw()),
2760                new_tree.map(|s| s.raw()),
2761                opts.map(|s| s.raw())
2762            ));
2763            Ok(Binding::from_raw(ret))
2764        }
2765    }
2766
2767    /// Create a diff between a tree and repository index.
2768    ///
2769    /// This is equivalent to `git diff --cached <treeish>` or if you pass
2770    /// the HEAD tree, then like `git diff --cached`.
2771    ///
2772    /// The tree you pass will be used for the "old_file" side of the delta, and
2773    /// the index will be used for the "new_file" side of the delta.
2774    ///
2775    /// If you pass `None` for the index, then the existing index of the `repo`
2776    /// will be used.  In this case, the index will be refreshed from disk
2777    /// (if it has changed) before the diff is generated.
2778    ///
2779    /// If the tree is `None`, then it is considered an empty tree.
2780    pub fn diff_tree_to_index(
2781        &self,
2782        old_tree: Option<&Tree<'_>>,
2783        index: Option<&Index>,
2784        opts: Option<&mut DiffOptions>,
2785    ) -> Result<Diff<'_>, Error> {
2786        let mut ret = ptr::null_mut();
2787        unsafe {
2788            try_call!(raw::git_diff_tree_to_index(
2789                &mut ret,
2790                self.raw(),
2791                old_tree.map(|s| s.raw()),
2792                index.map(|s| s.raw()),
2793                opts.map(|s| s.raw())
2794            ));
2795            Ok(Binding::from_raw(ret))
2796        }
2797    }
2798
2799    /// Create a diff between two index objects.
2800    ///
2801    /// The first index will be used for the "old_file" side of the delta, and
2802    /// the second index will be used for the "new_file" side of the delta.
2803    pub fn diff_index_to_index(
2804        &self,
2805        old_index: &Index,
2806        new_index: &Index,
2807        opts: Option<&mut DiffOptions>,
2808    ) -> Result<Diff<'_>, Error> {
2809        let mut ret = ptr::null_mut();
2810        unsafe {
2811            try_call!(raw::git_diff_index_to_index(
2812                &mut ret,
2813                self.raw(),
2814                old_index.raw(),
2815                new_index.raw(),
2816                opts.map(|s| s.raw())
2817            ));
2818            Ok(Binding::from_raw(ret))
2819        }
2820    }
2821
2822    /// Create a diff between the repository index and the workdir directory.
2823    ///
2824    /// This matches the `git diff` command.  See the note below on
2825    /// `tree_to_workdir` for a discussion of the difference between
2826    /// `git diff` and `git diff HEAD` and how to emulate a `git diff <treeish>`
2827    /// using libgit2.
2828    ///
2829    /// The index will be used for the "old_file" side of the delta, and the
2830    /// working directory will be used for the "new_file" side of the delta.
2831    ///
2832    /// If you pass `None` for the index, then the existing index of the `repo`
2833    /// will be used.  In this case, the index will be refreshed from disk
2834    /// (if it has changed) before the diff is generated.
2835    pub fn diff_index_to_workdir(
2836        &self,
2837        index: Option<&Index>,
2838        opts: Option<&mut DiffOptions>,
2839    ) -> Result<Diff<'_>, Error> {
2840        let mut ret = ptr::null_mut();
2841        unsafe {
2842            try_call!(raw::git_diff_index_to_workdir(
2843                &mut ret,
2844                self.raw(),
2845                index.map(|s| s.raw()),
2846                opts.map(|s| s.raw())
2847            ));
2848            Ok(Binding::from_raw(ret))
2849        }
2850    }
2851
2852    /// Create a diff between a tree and the working directory.
2853    ///
2854    /// The tree you provide will be used for the "old_file" side of the delta,
2855    /// and the working directory will be used for the "new_file" side.
2856    ///
2857    /// This is not the same as `git diff <treeish>` or `git diff-index
2858    /// <treeish>`.  Those commands use information from the index, whereas this
2859    /// function strictly returns the differences between the tree and the files
2860    /// in the working directory, regardless of the state of the index.  Use
2861    /// `tree_to_workdir_with_index` to emulate those commands.
2862    ///
2863    /// To see difference between this and `tree_to_workdir_with_index`,
2864    /// consider the example of a staged file deletion where the file has then
2865    /// been put back into the working dir and further modified.  The
2866    /// tree-to-workdir diff for that file is 'modified', but `git diff` would
2867    /// show status 'deleted' since there is a staged delete.
2868    ///
2869    /// If `None` is passed for `tree`, then an empty tree is used.
2870    pub fn diff_tree_to_workdir(
2871        &self,
2872        old_tree: Option<&Tree<'_>>,
2873        opts: Option<&mut DiffOptions>,
2874    ) -> Result<Diff<'_>, Error> {
2875        let mut ret = ptr::null_mut();
2876        unsafe {
2877            try_call!(raw::git_diff_tree_to_workdir(
2878                &mut ret,
2879                self.raw(),
2880                old_tree.map(|s| s.raw()),
2881                opts.map(|s| s.raw())
2882            ));
2883            Ok(Binding::from_raw(ret))
2884        }
2885    }
2886
2887    /// Create a diff between a tree and the working directory using index data
2888    /// to account for staged deletes, tracked files, etc.
2889    ///
2890    /// This emulates `git diff <tree>` by diffing the tree to the index and
2891    /// the index to the working directory and blending the results into a
2892    /// single diff that includes staged deleted, etc.
2893    pub fn diff_tree_to_workdir_with_index(
2894        &self,
2895        old_tree: Option<&Tree<'_>>,
2896        opts: Option<&mut DiffOptions>,
2897    ) -> Result<Diff<'_>, Error> {
2898        let mut ret = ptr::null_mut();
2899        unsafe {
2900            try_call!(raw::git_diff_tree_to_workdir_with_index(
2901                &mut ret,
2902                self.raw(),
2903                old_tree.map(|s| s.raw()),
2904                opts.map(|s| s.raw())
2905            ));
2906            Ok(Binding::from_raw(ret))
2907        }
2908    }
2909
2910    /// Create a PackBuilder
2911    pub fn packbuilder(&self) -> Result<PackBuilder<'_>, Error> {
2912        let mut ret = ptr::null_mut();
2913        unsafe {
2914            try_call!(raw::git_packbuilder_new(&mut ret, self.raw()));
2915            Ok(Binding::from_raw(ret))
2916        }
2917    }
2918
2919    /// Save the local modifications to a new stash.
2920    pub fn stash_save(
2921        &mut self,
2922        stasher: &Signature<'_>,
2923        message: &str,
2924        flags: Option<StashFlags>,
2925    ) -> Result<Oid, Error> {
2926        self.stash_save2(stasher, Some(message), flags)
2927    }
2928
2929    /// Save the local modifications to a new stash.
2930    /// unlike `stash_save` it allows to pass a null `message`
2931    pub fn stash_save2(
2932        &mut self,
2933        stasher: &Signature<'_>,
2934        message: Option<&str>,
2935        flags: Option<StashFlags>,
2936    ) -> Result<Oid, Error> {
2937        unsafe {
2938            let mut raw_oid = raw::git_oid {
2939                id: [0; raw::GIT_OID_RAWSZ],
2940            };
2941            let message = crate::opt_cstr(message)?;
2942            let flags = flags.unwrap_or_else(StashFlags::empty);
2943            try_call!(raw::git_stash_save(
2944                &mut raw_oid,
2945                self.raw(),
2946                stasher.raw(),
2947                message,
2948                flags.bits() as c_uint
2949            ));
2950            Ok(Binding::from_raw(&raw_oid as *const _))
2951        }
2952    }
2953
2954    /// Like `stash_save` but with more options like selective statshing via path patterns.
2955    pub fn stash_save_ext(
2956        &mut self,
2957        opts: Option<&mut StashSaveOptions<'_>>,
2958    ) -> Result<Oid, Error> {
2959        unsafe {
2960            let mut raw_oid = raw::git_oid {
2961                id: [0; raw::GIT_OID_RAWSZ],
2962            };
2963            let opts = opts.map(|opts| opts.raw());
2964            try_call!(raw::git_stash_save_with_opts(
2965                &mut raw_oid,
2966                self.raw(),
2967                opts
2968            ));
2969            Ok(Binding::from_raw(&raw_oid as *const _))
2970        }
2971    }
2972
2973    /// Apply a single stashed state from the stash list.
2974    pub fn stash_apply(
2975        &mut self,
2976        index: usize,
2977        opts: Option<&mut StashApplyOptions<'_>>,
2978    ) -> Result<(), Error> {
2979        unsafe {
2980            let opts = opts.map(|opts| opts.raw());
2981            try_call!(raw::git_stash_apply(self.raw(), index, opts));
2982            Ok(())
2983        }
2984    }
2985
2986    /// Loop over all the stashed states and issue a callback for each one.
2987    ///
2988    /// Return `true` to continue iterating or `false` to stop.
2989    pub fn stash_foreach<C>(&mut self, mut callback: C) -> Result<(), Error>
2990    where
2991        C: FnMut(usize, &str, &Oid) -> bool,
2992    {
2993        unsafe {
2994            let mut data = StashCbData {
2995                callback: &mut callback,
2996            };
2997            let cb: raw::git_stash_cb = Some(stash_cb);
2998            try_call!(raw::git_stash_foreach(
2999                self.raw(),
3000                cb,
3001                &mut data as *mut _ as *mut _
3002            ));
3003            Ok(())
3004        }
3005    }
3006
3007    /// Remove a single stashed state from the stash list.
3008    pub fn stash_drop(&mut self, index: usize) -> Result<(), Error> {
3009        unsafe {
3010            try_call!(raw::git_stash_drop(self.raw(), index));
3011            Ok(())
3012        }
3013    }
3014
3015    /// Apply a single stashed state from the stash list and remove it from the list if successful.
3016    pub fn stash_pop(
3017        &mut self,
3018        index: usize,
3019        opts: Option<&mut StashApplyOptions<'_>>,
3020    ) -> Result<(), Error> {
3021        unsafe {
3022            let opts = opts.map(|opts| opts.raw());
3023            try_call!(raw::git_stash_pop(self.raw(), index, opts));
3024            Ok(())
3025        }
3026    }
3027
3028    /// Add ignore rules for a repository.
3029    ///
3030    /// The format of the rules is the same one of the .gitignore file.
3031    pub fn add_ignore_rule(&self, rules: &str) -> Result<(), Error> {
3032        let rules = CString::new(rules)?;
3033        unsafe {
3034            try_call!(raw::git_ignore_add_rule(self.raw, rules));
3035        }
3036        Ok(())
3037    }
3038
3039    /// Clear ignore rules that were explicitly added.
3040    pub fn clear_ignore_rules(&self) -> Result<(), Error> {
3041        unsafe {
3042            try_call!(raw::git_ignore_clear_internal_rules(self.raw));
3043        }
3044        Ok(())
3045    }
3046
3047    /// Test if the ignore rules apply to a given path.
3048    pub fn is_path_ignored<P: AsRef<Path>>(&self, path: P) -> Result<bool, Error> {
3049        let path = util::cstring_to_repo_path(path.as_ref())?;
3050        let mut ignored: c_int = 0;
3051        unsafe {
3052            try_call!(raw::git_ignore_path_is_ignored(
3053                &mut ignored,
3054                self.raw,
3055                path
3056            ));
3057        }
3058        Ok(ignored == 1)
3059    }
3060
3061    /// Perform a cherrypick
3062    pub fn cherrypick(
3063        &self,
3064        commit: &Commit<'_>,
3065        options: Option<&mut CherrypickOptions<'_>>,
3066    ) -> Result<(), Error> {
3067        let raw_opts = options.map(|o| o.raw());
3068        let ptr_raw_opts = match raw_opts.as_ref() {
3069            Some(v) => v,
3070            None => std::ptr::null(),
3071        };
3072        unsafe {
3073            try_call!(raw::git_cherrypick(self.raw(), commit.raw(), ptr_raw_opts));
3074
3075            Ok(())
3076        }
3077    }
3078
3079    /// Create an index of uncommitted changes, representing the result of
3080    /// cherry-picking.
3081    pub fn cherrypick_commit(
3082        &self,
3083        cherrypick_commit: &Commit<'_>,
3084        our_commit: &Commit<'_>,
3085        mainline: u32,
3086        options: Option<&MergeOptions>,
3087    ) -> Result<Index, Error> {
3088        let mut ret = ptr::null_mut();
3089        unsafe {
3090            try_call!(raw::git_cherrypick_commit(
3091                &mut ret,
3092                self.raw(),
3093                cherrypick_commit.raw(),
3094                our_commit.raw(),
3095                mainline,
3096                options.map(|o| o.raw())
3097            ));
3098            Ok(Binding::from_raw(ret))
3099        }
3100    }
3101
3102    /// Find the remote name of a remote-tracking branch
3103    pub fn branch_remote_name(&self, refname: &str) -> Result<Buf, Error> {
3104        let refname = CString::new(refname)?;
3105        unsafe {
3106            let buf = Buf::new();
3107            try_call!(raw::git_branch_remote_name(buf.raw(), self.raw, refname));
3108            Ok(buf)
3109        }
3110    }
3111
3112    /// Retrieves the name of the reference supporting the remote tracking branch,
3113    /// given the name of a local branch reference.
3114    pub fn branch_upstream_name(&self, refname: &str) -> Result<Buf, Error> {
3115        let refname = CString::new(refname)?;
3116        unsafe {
3117            let buf = Buf::new();
3118            try_call!(raw::git_branch_upstream_name(buf.raw(), self.raw, refname));
3119            Ok(buf)
3120        }
3121    }
3122
3123    /// Retrieve the name of the upstream remote of a local branch.
3124    ///
3125    /// `refname` must be in the form `refs/heads/{branch_name}`
3126    pub fn branch_upstream_remote(&self, refname: &str) -> Result<Buf, Error> {
3127        let refname = CString::new(refname)?;
3128        unsafe {
3129            let buf = Buf::new();
3130            try_call!(raw::git_branch_upstream_remote(
3131                buf.raw(),
3132                self.raw,
3133                refname
3134            ));
3135            Ok(buf)
3136        }
3137    }
3138
3139    /// Apply a Diff to the given repo, making changes directly in the working directory, the index, or both.
3140    pub fn apply(
3141        &self,
3142        diff: &Diff<'_>,
3143        location: ApplyLocation,
3144        options: Option<&mut ApplyOptions<'_>>,
3145    ) -> Result<(), Error> {
3146        unsafe {
3147            try_call!(raw::git_apply(
3148                self.raw,
3149                diff.raw(),
3150                location.raw(),
3151                options.map(|s| s.raw()).unwrap_or(ptr::null())
3152            ));
3153
3154            Ok(())
3155        }
3156    }
3157
3158    /// Apply a Diff to the provided tree, and return the resulting Index.
3159    pub fn apply_to_tree(
3160        &self,
3161        tree: &Tree<'_>,
3162        diff: &Diff<'_>,
3163        options: Option<&mut ApplyOptions<'_>>,
3164    ) -> Result<Index, Error> {
3165        let mut ret = ptr::null_mut();
3166        unsafe {
3167            try_call!(raw::git_apply_to_tree(
3168                &mut ret,
3169                self.raw,
3170                tree.raw(),
3171                diff.raw(),
3172                options.map(|s| s.raw()).unwrap_or(ptr::null())
3173            ));
3174            Ok(Binding::from_raw(ret))
3175        }
3176    }
3177
3178    /// Reverts the given commit, producing changes in the index and working directory.
3179    pub fn revert(
3180        &self,
3181        commit: &Commit<'_>,
3182        options: Option<&mut RevertOptions<'_>>,
3183    ) -> Result<(), Error> {
3184        let raw_opts = options.map(|o| o.raw());
3185        let ptr_raw_opts = match raw_opts.as_ref() {
3186            Some(v) => v,
3187            None => 0 as *const _,
3188        };
3189        unsafe {
3190            try_call!(raw::git_revert(self.raw(), commit.raw(), ptr_raw_opts));
3191            Ok(())
3192        }
3193    }
3194
3195    /// Reverts the given commit against the given "our" commit,
3196    /// producing an index that reflects the result of the revert.
3197    pub fn revert_commit(
3198        &self,
3199        revert_commit: &Commit<'_>,
3200        our_commit: &Commit<'_>,
3201        mainline: u32,
3202        options: Option<&MergeOptions>,
3203    ) -> Result<Index, Error> {
3204        let mut ret = ptr::null_mut();
3205        unsafe {
3206            try_call!(raw::git_revert_commit(
3207                &mut ret,
3208                self.raw(),
3209                revert_commit.raw(),
3210                our_commit.raw(),
3211                mainline,
3212                options.map(|o| o.raw())
3213            ));
3214            Ok(Binding::from_raw(ret))
3215        }
3216    }
3217
3218    /// Lists all the worktrees for the repository
3219    pub fn worktrees(&self) -> Result<StringArray, Error> {
3220        let mut arr = raw::git_strarray {
3221            strings: ptr::null_mut(),
3222            count: 0,
3223        };
3224        unsafe {
3225            try_call!(raw::git_worktree_list(&mut arr, self.raw));
3226            Ok(Binding::from_raw(arr))
3227        }
3228    }
3229
3230    /// Opens a worktree by name for the given repository
3231    ///
3232    /// This can open any worktree that the worktrees method returns.
3233    pub fn find_worktree(&self, name: &str) -> Result<Worktree, Error> {
3234        let mut raw = ptr::null_mut();
3235        let raw_name = CString::new(name)?;
3236        unsafe {
3237            try_call!(raw::git_worktree_lookup(&mut raw, self.raw, raw_name));
3238            Ok(Binding::from_raw(raw))
3239        }
3240    }
3241
3242    /// Creates a new worktree for the repository
3243    pub fn worktree<'a>(
3244        &'a self,
3245        name: &str,
3246        path: &Path,
3247        opts: Option<&WorktreeAddOptions<'a>>,
3248    ) -> Result<Worktree, Error> {
3249        let mut raw = ptr::null_mut();
3250        let raw_name = CString::new(name)?;
3251        let raw_path = path.into_c_string()?;
3252
3253        unsafe {
3254            try_call!(raw::git_worktree_add(
3255                &mut raw,
3256                self.raw,
3257                raw_name,
3258                raw_path,
3259                opts.map(|o| o.raw())
3260            ));
3261            Ok(Binding::from_raw(raw))
3262        }
3263    }
3264
3265    /// Create a new transaction
3266    pub fn transaction<'a>(&'a self) -> Result<Transaction<'a>, Error> {
3267        let mut raw = ptr::null_mut();
3268        unsafe {
3269            try_call!(raw::git_transaction_new(&mut raw, self.raw));
3270            Ok(Binding::from_raw(raw))
3271        }
3272    }
3273
3274    /// Gets this repository's mailmap.
3275    pub fn mailmap(&self) -> Result<Mailmap, Error> {
3276        let mut ret = ptr::null_mut();
3277        unsafe {
3278            try_call!(raw::git_mailmap_from_repository(&mut ret, self.raw));
3279            Ok(Binding::from_raw(ret))
3280        }
3281    }
3282
3283    ///  If a merge is in progress, invoke 'callback' for each commit ID in the
3284    ///  MERGE_HEAD file.
3285    pub fn mergehead_foreach<C>(&mut self, mut callback: C) -> Result<(), Error>
3286    where
3287        C: FnMut(&Oid) -> bool,
3288    {
3289        unsafe {
3290            let mut data = MergeheadForeachCbData {
3291                callback: &mut callback,
3292            };
3293            let cb: raw::git_repository_mergehead_foreach_cb = Some(mergehead_foreach_cb);
3294            try_call!(raw::git_repository_mergehead_foreach(
3295                self.raw(),
3296                cb,
3297                &mut data as *mut _ as *mut _
3298            ));
3299            Ok(())
3300        }
3301    }
3302
3303    /// Invoke 'callback' for each entry in the given FETCH_HEAD file.
3304    ///
3305    /// `callback` will be called with with following arguments:
3306    ///
3307    /// - `&str`: the reference name
3308    /// - `&[u8]`: the remote URL
3309    /// - `&Oid`: the reference target OID
3310    /// - `bool`: was the reference the result of a merge
3311    pub fn fetchhead_foreach<C>(&self, mut callback: C) -> Result<(), Error>
3312    where
3313        C: FnMut(&str, &[u8], &Oid, bool) -> bool,
3314    {
3315        unsafe {
3316            let mut data = FetchheadForeachCbData {
3317                callback: &mut callback,
3318            };
3319            let cb: raw::git_repository_fetchhead_foreach_cb = Some(fetchhead_foreach_cb);
3320            try_call!(raw::git_repository_fetchhead_foreach(
3321                self.raw(),
3322                cb,
3323                &mut data as *mut _ as *mut _
3324            ));
3325            Ok(())
3326        }
3327    }
3328}
3329
3330impl Binding for Repository {
3331    type Raw = *mut raw::git_repository;
3332    unsafe fn from_raw(ptr: *mut raw::git_repository) -> Repository {
3333        Repository { raw: ptr }
3334    }
3335    fn raw(&self) -> *mut raw::git_repository {
3336        self.raw
3337    }
3338}
3339
3340impl Drop for Repository {
3341    fn drop(&mut self) {
3342        unsafe { raw::git_repository_free(self.raw) }
3343    }
3344}
3345
3346impl RepositoryInitOptions {
3347    /// Creates a default set of initialization options.
3348    ///
3349    /// By default this will set flags for creating all necessary directories
3350    /// and initializing a directory from the user-configured templates path.
3351    pub fn new() -> RepositoryInitOptions {
3352        RepositoryInitOptions {
3353            flags: raw::GIT_REPOSITORY_INIT_MKDIR as u32
3354                | raw::GIT_REPOSITORY_INIT_MKPATH as u32
3355                | raw::GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE as u32,
3356            mode: 0,
3357            workdir_path: None,
3358            description: None,
3359            template_path: None,
3360            initial_head: None,
3361            origin_url: None,
3362        }
3363    }
3364
3365    /// Create a bare repository with no working directory.
3366    ///
3367    /// Defaults to false.
3368    pub fn bare(&mut self, bare: bool) -> &mut RepositoryInitOptions {
3369        self.flag(raw::GIT_REPOSITORY_INIT_BARE, bare)
3370    }
3371
3372    /// Return an error if the repository path appears to already be a git
3373    /// repository.
3374    ///
3375    /// Defaults to false.
3376    pub fn no_reinit(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3377        self.flag(raw::GIT_REPOSITORY_INIT_NO_REINIT, enabled)
3378    }
3379
3380    /// Normally a '/.git/' will be appended to the repo path for non-bare repos
3381    /// (if it is not already there), but passing this flag prevents that
3382    /// behavior.
3383    ///
3384    /// Defaults to false.
3385    pub fn no_dotgit_dir(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3386        self.flag(raw::GIT_REPOSITORY_INIT_NO_DOTGIT_DIR, enabled)
3387    }
3388
3389    /// Make the repo path (and workdir path) as needed. The ".git" directory
3390    /// will always be created regardless of this flag.
3391    ///
3392    /// Defaults to true.
3393    pub fn mkdir(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3394        self.flag(raw::GIT_REPOSITORY_INIT_MKDIR, enabled)
3395    }
3396
3397    /// Recursively make all components of the repo and workdir path as
3398    /// necessary.
3399    ///
3400    /// Defaults to true.
3401    pub fn mkpath(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3402        self.flag(raw::GIT_REPOSITORY_INIT_MKPATH, enabled)
3403    }
3404
3405    /// Set to one of the `RepositoryInit` constants, or a custom value.
3406    pub fn mode(&mut self, mode: RepositoryInitMode) -> &mut RepositoryInitOptions {
3407        self.mode = mode.bits();
3408        self
3409    }
3410
3411    /// Enable or disable using external templates.
3412    ///
3413    /// If enabled, then the `template_path` option will be queried first, then
3414    /// `init.templatedir` from the global config, and finally
3415    /// `/usr/share/git-core-templates` will be used (if it exists).
3416    ///
3417    /// Defaults to true.
3418    pub fn external_template(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
3419        self.flag(raw::GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE, enabled)
3420    }
3421
3422    fn flag(
3423        &mut self,
3424        flag: raw::git_repository_init_flag_t,
3425        on: bool,
3426    ) -> &mut RepositoryInitOptions {
3427        if on {
3428            self.flags |= flag as u32;
3429        } else {
3430            self.flags &= !(flag as u32);
3431        }
3432        self
3433    }
3434
3435    /// The path to the working directory.
3436    ///
3437    /// If this is a relative path it will be evaluated relative to the repo
3438    /// path. If this is not the "natural" working directory, a .git gitlink
3439    /// file will be created here linking to the repo path.
3440    pub fn workdir_path(&mut self, path: &Path) -> &mut RepositoryInitOptions {
3441        // Normal file path OK (does not need Windows conversion).
3442        self.workdir_path = Some(path.into_c_string().unwrap());
3443        self
3444    }
3445
3446    /// If set, this will be used to initialize the "description" file in the
3447    /// repository instead of using the template content.
3448    pub fn description(&mut self, desc: &str) -> &mut RepositoryInitOptions {
3449        self.description = Some(CString::new(desc).unwrap());
3450        self
3451    }
3452
3453    /// When the `external_template` option is set, this is the first location
3454    /// to check for the template directory.
3455    ///
3456    /// If this is not configured, then the default locations will be searched
3457    /// instead.
3458    pub fn template_path(&mut self, path: &Path) -> &mut RepositoryInitOptions {
3459        // Normal file path OK (does not need Windows conversion).
3460        self.template_path = Some(path.into_c_string().unwrap());
3461        self
3462    }
3463
3464    /// The name of the head to point HEAD at.
3465    ///
3466    /// If not configured, this will be taken from your git configuration.
3467    /// If this begins with `refs/` it will be used verbatim;
3468    /// otherwise `refs/heads/` will be prefixed
3469    pub fn initial_head(&mut self, head: &str) -> &mut RepositoryInitOptions {
3470        self.initial_head = Some(CString::new(head).unwrap());
3471        self
3472    }
3473
3474    /// If set, then after the rest of the repository initialization is
3475    /// completed an `origin` remote will be added pointing to this URL.
3476    pub fn origin_url(&mut self, url: &str) -> &mut RepositoryInitOptions {
3477        self.origin_url = Some(CString::new(url).unwrap());
3478        self
3479    }
3480
3481    /// Creates a set of raw init options to be used with
3482    /// `git_repository_init_ext`.
3483    ///
3484    /// This method is unsafe as the returned value may have pointers to the
3485    /// interior of this structure.
3486    pub unsafe fn raw(&self) -> raw::git_repository_init_options {
3487        let mut opts = mem::zeroed();
3488        assert_eq!(
3489            raw::git_repository_init_init_options(
3490                &mut opts,
3491                raw::GIT_REPOSITORY_INIT_OPTIONS_VERSION
3492            ),
3493            0
3494        );
3495        opts.flags = self.flags;
3496        opts.mode = self.mode;
3497        opts.workdir_path = crate::call::convert(&self.workdir_path);
3498        opts.description = crate::call::convert(&self.description);
3499        opts.template_path = crate::call::convert(&self.template_path);
3500        opts.initial_head = crate::call::convert(&self.initial_head);
3501        opts.origin_url = crate::call::convert(&self.origin_url);
3502        opts
3503    }
3504}
3505
3506#[cfg(test)]
3507mod tests {
3508    use crate::build::CheckoutBuilder;
3509    use crate::CherrypickOptions;
3510    use crate::{
3511        ObjectType, Oid, Repository, ResetType, Signature, SubmoduleIgnore, SubmoduleUpdate,
3512    };
3513    use std::ffi::OsStr;
3514    use std::fs;
3515    use std::path::Path;
3516    use tempfile::TempDir;
3517
3518    #[test]
3519    fn smoke_init() {
3520        let td = TempDir::new().unwrap();
3521        let path = td.path();
3522
3523        let repo = Repository::init(path).unwrap();
3524        assert!(!repo.is_bare());
3525    }
3526
3527    #[test]
3528    fn smoke_init_bare() {
3529        let td = TempDir::new().unwrap();
3530        let path = td.path();
3531
3532        let repo = Repository::init_bare(path).unwrap();
3533        assert!(repo.is_bare());
3534        assert!(repo.namespace().is_none());
3535    }
3536
3537    #[test]
3538    fn smoke_open() {
3539        let td = TempDir::new().unwrap();
3540        let path = td.path();
3541        Repository::init(td.path()).unwrap();
3542        let repo = Repository::open(path).unwrap();
3543        assert!(!repo.is_bare());
3544        assert!(!repo.is_shallow());
3545        assert!(repo.is_empty().unwrap());
3546        assert_eq!(
3547            crate::test::realpath(&repo.path()).unwrap(),
3548            crate::test::realpath(&td.path().join(".git/")).unwrap()
3549        );
3550        assert_eq!(repo.state(), crate::RepositoryState::Clean);
3551    }
3552
3553    #[test]
3554    fn smoke_open_bare() {
3555        let td = TempDir::new().unwrap();
3556        let path = td.path();
3557        Repository::init_bare(td.path()).unwrap();
3558
3559        let repo = Repository::open(path).unwrap();
3560        assert!(repo.is_bare());
3561        assert_eq!(
3562            crate::test::realpath(&repo.path()).unwrap(),
3563            crate::test::realpath(&td.path().join("")).unwrap()
3564        );
3565    }
3566
3567    #[test]
3568    fn smoke_checkout() {
3569        let (_td, repo) = crate::test::repo_init();
3570        repo.checkout_head(None).unwrap();
3571    }
3572
3573    #[test]
3574    fn smoke_revparse() {
3575        let (_td, repo) = crate::test::repo_init();
3576        let rev = repo.revparse("HEAD").unwrap();
3577        assert!(rev.to().is_none());
3578        let from = rev.from().unwrap();
3579        assert!(rev.from().is_some());
3580
3581        assert_eq!(repo.revparse_single("HEAD").unwrap().id(), from.id());
3582        let obj = repo.find_object(from.id(), None).unwrap().clone();
3583        obj.peel(ObjectType::Any).unwrap();
3584        obj.short_id().unwrap();
3585        repo.reset(&obj, ResetType::Hard, None).unwrap();
3586        let mut opts = CheckoutBuilder::new();
3587        t!(repo.reset(&obj, ResetType::Soft, Some(&mut opts)));
3588    }
3589
3590    #[test]
3591    fn makes_dirs() {
3592        let td = TempDir::new().unwrap();
3593        Repository::init(&td.path().join("a/b/c/d")).unwrap();
3594    }
3595
3596    #[test]
3597    fn smoke_discover() {
3598        let td = TempDir::new().unwrap();
3599        let subdir = td.path().join("subdi");
3600        fs::create_dir(&subdir).unwrap();
3601        Repository::init_bare(td.path()).unwrap();
3602        let repo = Repository::discover(&subdir).unwrap();
3603        assert_eq!(
3604            crate::test::realpath(&repo.path()).unwrap(),
3605            crate::test::realpath(&td.path().join("")).unwrap()
3606        );
3607    }
3608
3609    #[test]
3610    fn smoke_discover_path() {
3611        let td = TempDir::new().unwrap();
3612        let subdir = td.path().join("subdi");
3613        fs::create_dir(&subdir).unwrap();
3614        Repository::init_bare(td.path()).unwrap();
3615        let path = Repository::discover_path(&subdir, &[] as &[&OsStr]).unwrap();
3616        assert_eq!(
3617            crate::test::realpath(&path).unwrap(),
3618            crate::test::realpath(&td.path().join("")).unwrap()
3619        );
3620    }
3621
3622    #[test]
3623    fn smoke_discover_path_ceiling_dir() {
3624        let td = TempDir::new().unwrap();
3625        let subdir = td.path().join("subdi");
3626        fs::create_dir(&subdir).unwrap();
3627        let ceilingdir = subdir.join("ceiling");
3628        fs::create_dir(&ceilingdir).unwrap();
3629        let testdir = ceilingdir.join("testdi");
3630        fs::create_dir(&testdir).unwrap();
3631        Repository::init_bare(td.path()).unwrap();
3632        let path = Repository::discover_path(&testdir, &[ceilingdir.as_os_str()]);
3633
3634        assert!(path.is_err());
3635    }
3636
3637    #[test]
3638    fn smoke_open_ext() {
3639        let td = TempDir::new().unwrap();
3640        let subdir = td.path().join("subdir");
3641        fs::create_dir(&subdir).unwrap();
3642        Repository::init(td.path()).unwrap();
3643
3644        let repo = Repository::open_ext(
3645            &subdir,
3646            crate::RepositoryOpenFlags::empty(),
3647            &[] as &[&OsStr],
3648        )
3649        .unwrap();
3650        assert!(!repo.is_bare());
3651        assert_eq!(
3652            crate::test::realpath(&repo.path()).unwrap(),
3653            crate::test::realpath(&td.path().join(".git")).unwrap()
3654        );
3655
3656        let repo =
3657            Repository::open_ext(&subdir, crate::RepositoryOpenFlags::BARE, &[] as &[&OsStr])
3658                .unwrap();
3659        assert!(repo.is_bare());
3660        assert_eq!(
3661            crate::test::realpath(&repo.path()).unwrap(),
3662            crate::test::realpath(&td.path().join(".git")).unwrap()
3663        );
3664
3665        let err = Repository::open_ext(
3666            &subdir,
3667            crate::RepositoryOpenFlags::NO_SEARCH,
3668            &[] as &[&OsStr],
3669        )
3670        .err()
3671        .unwrap();
3672        assert_eq!(err.code(), crate::ErrorCode::NotFound);
3673
3674        assert!(
3675            Repository::open_ext(&subdir, crate::RepositoryOpenFlags::empty(), &[&subdir]).is_ok()
3676        );
3677    }
3678
3679    fn graph_repo_init() -> (TempDir, Repository) {
3680        let (_td, repo) = crate::test::repo_init();
3681        {
3682            let head = repo.head().unwrap().target().unwrap();
3683            let head = repo.find_commit(head).unwrap();
3684
3685            let mut index = repo.index().unwrap();
3686            let id = index.write_tree().unwrap();
3687
3688            let tree = repo.find_tree(id).unwrap();
3689            let sig = repo.signature().unwrap();
3690            repo.commit(Some("HEAD"), &sig, &sig, "second", &tree, &[&head])
3691                .unwrap();
3692        }
3693        (_td, repo)
3694    }
3695
3696    #[test]
3697    fn smoke_graph_ahead_behind() {
3698        let (_td, repo) = graph_repo_init();
3699        let head = repo.head().unwrap().target().unwrap();
3700        let head = repo.find_commit(head).unwrap();
3701        let head_id = head.id();
3702        let head_parent_id = head.parent(0).unwrap().id();
3703        let (ahead, behind) = repo.graph_ahead_behind(head_id, head_parent_id).unwrap();
3704        assert_eq!(ahead, 1);
3705        assert_eq!(behind, 0);
3706        let (ahead, behind) = repo.graph_ahead_behind(head_parent_id, head_id).unwrap();
3707        assert_eq!(ahead, 0);
3708        assert_eq!(behind, 1);
3709    }
3710
3711    #[test]
3712    fn smoke_graph_descendant_of() {
3713        let (_td, repo) = graph_repo_init();
3714        let head = repo.head().unwrap().target().unwrap();
3715        let head = repo.find_commit(head).unwrap();
3716        let head_id = head.id();
3717        let head_parent_id = head.parent(0).unwrap().id();
3718        assert!(repo.graph_descendant_of(head_id, head_parent_id).unwrap());
3719        assert!(!repo.graph_descendant_of(head_parent_id, head_id).unwrap());
3720    }
3721
3722    #[test]
3723    fn smoke_reference_has_log_ensure_log() {
3724        let (_td, repo) = crate::test::repo_init();
3725
3726        assert_eq!(repo.reference_has_log("HEAD").unwrap(), true);
3727        assert_eq!(repo.reference_has_log("refs/heads/main").unwrap(), true);
3728        assert_eq!(repo.reference_has_log("NOT_HEAD").unwrap(), false);
3729        let main_oid = repo.revparse_single("main").unwrap().id();
3730        assert!(repo
3731            .reference("NOT_HEAD", main_oid, false, "creating a new branch")
3732            .is_ok());
3733        assert_eq!(repo.reference_has_log("NOT_HEAD").unwrap(), false);
3734        assert!(repo.reference_ensure_log("NOT_HEAD").is_ok());
3735        assert_eq!(repo.reference_has_log("NOT_HEAD").unwrap(), true);
3736    }
3737
3738    #[test]
3739    fn smoke_set_head() {
3740        let (_td, repo) = crate::test::repo_init();
3741
3742        assert!(repo.set_head("refs/heads/does-not-exist").is_ok());
3743        assert!(repo.head().is_err());
3744
3745        assert!(repo.set_head("refs/heads/main").is_ok());
3746        assert!(repo.head().is_ok());
3747
3748        assert!(repo.set_head("*").is_err());
3749    }
3750
3751    #[test]
3752    fn smoke_set_head_bytes() {
3753        let (_td, repo) = crate::test::repo_init();
3754
3755        assert!(repo.set_head_bytes(b"refs/heads/does-not-exist").is_ok());
3756        assert!(repo.head().is_err());
3757
3758        assert!(repo.set_head_bytes(b"refs/heads/main").is_ok());
3759        assert!(repo.head().is_ok());
3760
3761        assert!(repo.set_head_bytes(b"*").is_err());
3762    }
3763
3764    #[test]
3765    fn smoke_set_head_detached() {
3766        let (_td, repo) = crate::test::repo_init();
3767
3768        let void_oid = Oid::from_bytes(b"00000000000000000000").unwrap();
3769        assert!(repo.set_head_detached(void_oid).is_err());
3770
3771        let main_oid = repo.revparse_single("main").unwrap().id();
3772        assert!(repo.set_head_detached(main_oid).is_ok());
3773        assert_eq!(repo.head().unwrap().target().unwrap(), main_oid);
3774    }
3775
3776    #[test]
3777    fn smoke_find_object_by_prefix() {
3778        let (_td, repo) = crate::test::repo_init();
3779        let head = repo.head().unwrap().target().unwrap();
3780        let head = repo.find_commit(head).unwrap();
3781        let head_id = head.id();
3782        let head_prefix = &head_id.to_string()[..7];
3783        let obj = repo.find_object_by_prefix(head_prefix, None).unwrap();
3784        assert_eq!(obj.id(), head_id);
3785    }
3786
3787    /// create the following:
3788    ///    /---o4
3789    ///   /---o3
3790    /// o1---o2
3791    #[test]
3792    fn smoke_merge_base() {
3793        let (_td, repo) = graph_repo_init();
3794        let sig = repo.signature().unwrap();
3795
3796        // let oid1 = head
3797        let oid1 = repo.head().unwrap().target().unwrap();
3798        let commit1 = repo.find_commit(oid1).unwrap();
3799        println!("created oid1 {:?}", oid1);
3800
3801        repo.branch("branch_a", &commit1, true).unwrap();
3802        repo.branch("branch_b", &commit1, true).unwrap();
3803        repo.branch("branch_c", &commit1, true).unwrap();
3804
3805        // create commit oid2 on branch_a
3806        let mut index = repo.index().unwrap();
3807        let p = Path::new(repo.workdir().unwrap()).join("file_a");
3808        println!("using path {:?}", p);
3809        fs::File::create(&p).unwrap();
3810        index.add_path(Path::new("file_a")).unwrap();
3811        let id_a = index.write_tree().unwrap();
3812        let tree_a = repo.find_tree(id_a).unwrap();
3813        let oid2 = repo
3814            .commit(
3815                Some("refs/heads/branch_a"),
3816                &sig,
3817                &sig,
3818                "commit 2",
3819                &tree_a,
3820                &[&commit1],
3821            )
3822            .unwrap();
3823        repo.find_commit(oid2).unwrap();
3824        println!("created oid2 {:?}", oid2);
3825
3826        t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
3827
3828        // create commit oid3 on branch_b
3829        let mut index = repo.index().unwrap();
3830        let p = Path::new(repo.workdir().unwrap()).join("file_b");
3831        fs::File::create(&p).unwrap();
3832        index.add_path(Path::new("file_b")).unwrap();
3833        let id_b = index.write_tree().unwrap();
3834        let tree_b = repo.find_tree(id_b).unwrap();
3835        let oid3 = repo
3836            .commit(
3837                Some("refs/heads/branch_b"),
3838                &sig,
3839                &sig,
3840                "commit 3",
3841                &tree_b,
3842                &[&commit1],
3843            )
3844            .unwrap();
3845        repo.find_commit(oid3).unwrap();
3846        println!("created oid3 {:?}", oid3);
3847
3848        t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
3849
3850        // create commit oid4 on branch_c
3851        let mut index = repo.index().unwrap();
3852        let p = Path::new(repo.workdir().unwrap()).join("file_c");
3853        fs::File::create(&p).unwrap();
3854        index.add_path(Path::new("file_c")).unwrap();
3855        let id_c = index.write_tree().unwrap();
3856        let tree_c = repo.find_tree(id_c).unwrap();
3857        let oid4 = repo
3858            .commit(
3859                Some("refs/heads/branch_c"),
3860                &sig,
3861                &sig,
3862                "commit 3",
3863                &tree_c,
3864                &[&commit1],
3865            )
3866            .unwrap();
3867        repo.find_commit(oid4).unwrap();
3868        println!("created oid4 {:?}", oid4);
3869
3870        // the merge base of (oid2,oid3) should be oid1
3871        let merge_base = repo.merge_base(oid2, oid3).unwrap();
3872        assert_eq!(merge_base, oid1);
3873
3874        // the merge base of (oid2,oid3,oid4) should be oid1
3875        let merge_base = repo.merge_base_many(&[oid2, oid3, oid4]).unwrap();
3876        assert_eq!(merge_base, oid1);
3877
3878        // the octopus merge base of (oid2,oid3,oid4) should be oid1
3879        let merge_base = repo.merge_base_octopus(&[oid2, oid3, oid4]).unwrap();
3880        assert_eq!(merge_base, oid1);
3881    }
3882
3883    /// create an octopus:
3884    ///   /---o2-o4
3885    /// o1      X
3886    ///   \---o3-o5
3887    /// and checks that the merge bases of (o4,o5) are (o2,o3)
3888    #[test]
3889    fn smoke_merge_bases() {
3890        let (_td, repo) = graph_repo_init();
3891        let sig = repo.signature().unwrap();
3892
3893        // let oid1 = head
3894        let oid1 = repo.head().unwrap().target().unwrap();
3895        let commit1 = repo.find_commit(oid1).unwrap();
3896        println!("created oid1 {:?}", oid1);
3897
3898        repo.branch("branch_a", &commit1, true).unwrap();
3899        repo.branch("branch_b", &commit1, true).unwrap();
3900
3901        // create commit oid2 on branchA
3902        let mut index = repo.index().unwrap();
3903        let p = Path::new(repo.workdir().unwrap()).join("file_a");
3904        println!("using path {:?}", p);
3905        fs::File::create(&p).unwrap();
3906        index.add_path(Path::new("file_a")).unwrap();
3907        let id_a = index.write_tree().unwrap();
3908        let tree_a = repo.find_tree(id_a).unwrap();
3909        let oid2 = repo
3910            .commit(
3911                Some("refs/heads/branch_a"),
3912                &sig,
3913                &sig,
3914                "commit 2",
3915                &tree_a,
3916                &[&commit1],
3917            )
3918            .unwrap();
3919        let commit2 = repo.find_commit(oid2).unwrap();
3920        println!("created oid2 {:?}", oid2);
3921
3922        t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
3923
3924        // create commit oid3 on branchB
3925        let mut index = repo.index().unwrap();
3926        let p = Path::new(repo.workdir().unwrap()).join("file_b");
3927        fs::File::create(&p).unwrap();
3928        index.add_path(Path::new("file_b")).unwrap();
3929        let id_b = index.write_tree().unwrap();
3930        let tree_b = repo.find_tree(id_b).unwrap();
3931        let oid3 = repo
3932            .commit(
3933                Some("refs/heads/branch_b"),
3934                &sig,
3935                &sig,
3936                "commit 3",
3937                &tree_b,
3938                &[&commit1],
3939            )
3940            .unwrap();
3941        let commit3 = repo.find_commit(oid3).unwrap();
3942        println!("created oid3 {:?}", oid3);
3943
3944        // create merge commit oid4 on branchA with parents oid2 and oid3
3945        //let mut index4 = repo.merge_commits(&commit2, &commit3, None).unwrap();
3946        repo.set_head("refs/heads/branch_a").unwrap();
3947        repo.checkout_head(None).unwrap();
3948        let oid4 = repo
3949            .commit(
3950                Some("refs/heads/branch_a"),
3951                &sig,
3952                &sig,
3953                "commit 4",
3954                &tree_a,
3955                &[&commit2, &commit3],
3956            )
3957            .unwrap();
3958        //index4.write_tree_to(&repo).unwrap();
3959        println!("created oid4 {:?}", oid4);
3960
3961        // create merge commit oid5 on branchB with parents oid2 and oid3
3962        //let mut index5 = repo.merge_commits(&commit3, &commit2, None).unwrap();
3963        repo.set_head("refs/heads/branch_b").unwrap();
3964        repo.checkout_head(None).unwrap();
3965        let oid5 = repo
3966            .commit(
3967                Some("refs/heads/branch_b"),
3968                &sig,
3969                &sig,
3970                "commit 5",
3971                &tree_a,
3972                &[&commit3, &commit2],
3973            )
3974            .unwrap();
3975        //index5.write_tree_to(&repo).unwrap();
3976        println!("created oid5 {:?}", oid5);
3977
3978        // merge bases of (oid4,oid5) should be (oid2,oid3)
3979        let merge_bases = repo.merge_bases(oid4, oid5).unwrap();
3980        let mut found_oid2 = false;
3981        let mut found_oid3 = false;
3982        for mg in merge_bases.iter() {
3983            println!("found merge base {:?}", mg);
3984            if mg == &oid2 {
3985                found_oid2 = true;
3986            } else if mg == &oid3 {
3987                found_oid3 = true;
3988            } else {
3989                assert!(false);
3990            }
3991        }
3992        assert!(found_oid2);
3993        assert!(found_oid3);
3994        assert_eq!(merge_bases.len(), 2);
3995
3996        // merge bases of (oid4,oid5) should be (oid2,oid3)
3997        let merge_bases = repo.merge_bases_many(&[oid4, oid5]).unwrap();
3998        let mut found_oid2 = false;
3999        let mut found_oid3 = false;
4000        for mg in merge_bases.iter() {
4001            println!("found merge base {:?}", mg);
4002            if mg == &oid2 {
4003                found_oid2 = true;
4004            } else if mg == &oid3 {
4005                found_oid3 = true;
4006            } else {
4007                assert!(false);
4008            }
4009        }
4010        assert!(found_oid2);
4011        assert!(found_oid3);
4012        assert_eq!(merge_bases.len(), 2);
4013    }
4014
4015    #[test]
4016    fn smoke_revparse_ext() {
4017        let (_td, repo) = graph_repo_init();
4018
4019        {
4020            let short_refname = "main";
4021            let expected_refname = "refs/heads/main";
4022            let (obj, reference) = repo.revparse_ext(short_refname).unwrap();
4023            let expected_obj = repo.revparse_single(expected_refname).unwrap();
4024            assert_eq!(obj.id(), expected_obj.id());
4025            assert_eq!(reference.unwrap().name().unwrap(), expected_refname);
4026        }
4027        {
4028            let missing_refname = "refs/heads/does-not-exist";
4029            assert!(repo.revparse_ext(missing_refname).is_err());
4030        }
4031        {
4032            let (_obj, reference) = repo.revparse_ext("HEAD^").unwrap();
4033            assert!(reference.is_none());
4034        }
4035    }
4036
4037    #[test]
4038    fn smoke_is_path_ignored() {
4039        let (_td, repo) = graph_repo_init();
4040
4041        assert!(!repo.is_path_ignored(Path::new("foo")).unwrap());
4042
4043        let _ = repo.add_ignore_rule("/foo");
4044        assert!(repo.is_path_ignored(Path::new("foo")).unwrap());
4045        if cfg!(windows) {
4046            assert!(repo.is_path_ignored(Path::new("foo\\thing")).unwrap());
4047        }
4048
4049        let _ = repo.clear_ignore_rules();
4050        assert!(!repo.is_path_ignored(Path::new("foo")).unwrap());
4051        if cfg!(windows) {
4052            assert!(!repo.is_path_ignored(Path::new("foo\\thing")).unwrap());
4053        }
4054    }
4055
4056    #[test]
4057    fn smoke_cherrypick() {
4058        let (_td, repo) = crate::test::repo_init();
4059        let sig = repo.signature().unwrap();
4060
4061        let oid1 = repo.head().unwrap().target().unwrap();
4062        let commit1 = repo.find_commit(oid1).unwrap();
4063
4064        repo.branch("branch_a", &commit1, true).unwrap();
4065
4066        // Add 2 commits on top of the initial one in branch_a
4067        let mut index = repo.index().unwrap();
4068        let p1 = Path::new(repo.workdir().unwrap()).join("file_c");
4069        fs::File::create(&p1).unwrap();
4070        index.add_path(Path::new("file_c")).unwrap();
4071        let id = index.write_tree().unwrap();
4072        let tree_c = repo.find_tree(id).unwrap();
4073        let oid2 = repo
4074            .commit(
4075                Some("refs/heads/branch_a"),
4076                &sig,
4077                &sig,
4078                "commit 2",
4079                &tree_c,
4080                &[&commit1],
4081            )
4082            .unwrap();
4083        let commit2 = repo.find_commit(oid2).unwrap();
4084        println!("created oid2 {:?}", oid2);
4085        assert!(p1.exists());
4086
4087        let mut index = repo.index().unwrap();
4088        let p2 = Path::new(repo.workdir().unwrap()).join("file_d");
4089        fs::File::create(&p2).unwrap();
4090        index.add_path(Path::new("file_d")).unwrap();
4091        let id = index.write_tree().unwrap();
4092        let tree_d = repo.find_tree(id).unwrap();
4093        let oid3 = repo
4094            .commit(
4095                Some("refs/heads/branch_a"),
4096                &sig,
4097                &sig,
4098                "commit 3",
4099                &tree_d,
4100                &[&commit2],
4101            )
4102            .unwrap();
4103        let commit3 = repo.find_commit(oid3).unwrap();
4104        println!("created oid3 {:?}", oid3);
4105        assert!(p1.exists());
4106        assert!(p2.exists());
4107
4108        // cherry-pick commit3 on top of commit1 in branch b
4109        repo.reset(commit1.as_object(), ResetType::Hard, None)
4110            .unwrap();
4111        let mut cherrypick_opts = CherrypickOptions::new();
4112        repo.cherrypick(&commit3, Some(&mut cherrypick_opts))
4113            .unwrap();
4114        let id = repo.index().unwrap().write_tree().unwrap();
4115        let tree_d = repo.find_tree(id).unwrap();
4116        let oid4 = repo
4117            .commit(Some("HEAD"), &sig, &sig, "commit 4", &tree_d, &[&commit1])
4118            .unwrap();
4119        let commit4 = repo.find_commit(oid4).unwrap();
4120        // should have file from commit3, but not the file from commit2
4121        assert_eq!(commit4.parent(0).unwrap().id(), commit1.id());
4122        assert!(!p1.exists());
4123        assert!(p2.exists());
4124    }
4125
4126    #[test]
4127    fn smoke_revert() {
4128        let (_td, repo) = crate::test::repo_init();
4129        let foo_file = Path::new(repo.workdir().unwrap()).join("foo");
4130        assert!(!foo_file.exists());
4131
4132        let (oid1, _id) = crate::test::commit(&repo);
4133        let commit1 = repo.find_commit(oid1).unwrap();
4134        t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
4135        assert!(foo_file.exists());
4136
4137        repo.revert(&commit1, None).unwrap();
4138        let id = repo.index().unwrap().write_tree().unwrap();
4139        let tree2 = repo.find_tree(id).unwrap();
4140        let sig = repo.signature().unwrap();
4141        repo.commit(Some("HEAD"), &sig, &sig, "commit 1", &tree2, &[&commit1])
4142            .unwrap();
4143        // reverting once removes `foo` file
4144        assert!(!foo_file.exists());
4145
4146        let oid2 = repo.head().unwrap().target().unwrap();
4147        let commit2 = repo.find_commit(oid2).unwrap();
4148        repo.revert(&commit2, None).unwrap();
4149        let id = repo.index().unwrap().write_tree().unwrap();
4150        let tree3 = repo.find_tree(id).unwrap();
4151        repo.commit(Some("HEAD"), &sig, &sig, "commit 2", &tree3, &[&commit2])
4152            .unwrap();
4153        // reverting twice restores `foo` file
4154        assert!(foo_file.exists());
4155    }
4156
4157    #[test]
4158    fn smoke_config_write_and_read() {
4159        let (td, repo) = crate::test::repo_init();
4160
4161        let mut config = repo.config().unwrap();
4162
4163        config.set_bool("commit.gpgsign", false).unwrap();
4164
4165        let c = fs::read_to_string(td.path().join(".git").join("config")).unwrap();
4166
4167        assert!(c.contains("[commit]"));
4168        assert!(c.contains("gpgsign = false"));
4169
4170        let config = repo.config().unwrap();
4171
4172        assert!(!config.get_bool("commit.gpgsign").unwrap());
4173    }
4174
4175    #[test]
4176    fn smoke_merge_analysis_for_ref() -> Result<(), crate::Error> {
4177        let (_td, repo) = graph_repo_init();
4178
4179        // Set up this repo state:
4180        // * second (their-branch)
4181        // * initial (HEAD -> main)
4182        //
4183        // We expect that their-branch can be fast-forward merged into main.
4184
4185        // git checkout --detach HEAD
4186        let head_commit = repo.head()?.peel_to_commit()?;
4187        repo.set_head_detached(head_commit.id())?;
4188
4189        // git branch their-branch HEAD
4190        let their_branch = repo.branch("their-branch", &head_commit, false)?;
4191
4192        // git branch -f main HEAD~
4193        let mut parents_iter = head_commit.parents();
4194        let parent = parents_iter.next().unwrap();
4195        assert!(parents_iter.next().is_none());
4196
4197        let main = repo.branch("main", &parent, true)?;
4198
4199        // git checkout main
4200        repo.set_head(main.get().name().expect("should be utf-8"))?;
4201
4202        let (merge_analysis, _merge_preference) = repo.merge_analysis_for_ref(
4203            main.get(),
4204            &[&repo.reference_to_annotated_commit(their_branch.get())?],
4205        )?;
4206
4207        assert!(merge_analysis.contains(crate::MergeAnalysis::ANALYSIS_FASTFORWARD));
4208
4209        Ok(())
4210    }
4211
4212    #[test]
4213    fn smoke_submodule_set() -> Result<(), crate::Error> {
4214        let (td1, _repo) = crate::test::repo_init();
4215        let (td2, mut repo2) = crate::test::repo_init();
4216        let url = crate::test::path2url(td1.path());
4217        let name = "bar";
4218        {
4219            let mut s = repo2.submodule(&url, Path::new(name), true)?;
4220            fs::remove_dir_all(td2.path().join("bar")).unwrap();
4221            Repository::clone(&url, td2.path().join("bar"))?;
4222            s.add_to_index(false)?;
4223            s.add_finalize()?;
4224        }
4225
4226        // update strategy
4227        repo2.submodule_set_update(name, SubmoduleUpdate::None)?;
4228        assert!(matches!(
4229            repo2.find_submodule(name)?.update_strategy(),
4230            SubmoduleUpdate::None
4231        ));
4232        repo2.submodule_set_update(name, SubmoduleUpdate::Rebase)?;
4233        assert!(matches!(
4234            repo2.find_submodule(name)?.update_strategy(),
4235            SubmoduleUpdate::Rebase
4236        ));
4237
4238        // ignore rule
4239        repo2.submodule_set_ignore(name, SubmoduleIgnore::Untracked)?;
4240        assert!(matches!(
4241            repo2.find_submodule(name)?.ignore_rule(),
4242            SubmoduleIgnore::Untracked
4243        ));
4244        repo2.submodule_set_ignore(name, SubmoduleIgnore::Dirty)?;
4245        assert!(matches!(
4246            repo2.find_submodule(name)?.ignore_rule(),
4247            SubmoduleIgnore::Dirty
4248        ));
4249
4250        // url
4251        repo2.submodule_set_url(name, "fake-url")?;
4252        assert_eq!(repo2.find_submodule(name)?.url(), Some("fake-url"));
4253
4254        // branch
4255        repo2.submodule_set_branch(name, "fake-branch")?;
4256        assert_eq!(repo2.find_submodule(name)?.branch(), Some("fake-branch"));
4257
4258        Ok(())
4259    }
4260
4261    #[test]
4262    fn smoke_mailmap_from_repository() {
4263        let (_td, repo) = crate::test::repo_init();
4264
4265        let commit = {
4266            let head = t!(repo.head()).target().unwrap();
4267            t!(repo.find_commit(head))
4268        };
4269
4270        // This is our baseline for HEAD.
4271        let author = commit.author();
4272        let committer = commit.committer();
4273        assert_eq!(author.name(), Some("name"));
4274        assert_eq!(author.email(), Some("email"));
4275        assert_eq!(committer.name(), Some("name"));
4276        assert_eq!(committer.email(), Some("email"));
4277
4278        // There is no .mailmap file in the test repo so all signature identities are equal.
4279        let mailmap = t!(repo.mailmap());
4280        let mailmapped_author = t!(commit.author_with_mailmap(&mailmap));
4281        let mailmapped_committer = t!(commit.committer_with_mailmap(&mailmap));
4282        assert_eq!(mailmapped_author.name(), author.name());
4283        assert_eq!(mailmapped_author.email(), author.email());
4284        assert_eq!(mailmapped_committer.name(), committer.name());
4285        assert_eq!(mailmapped_committer.email(), committer.email());
4286
4287        let commit = {
4288            // - Add a .mailmap file to the repository.
4289            // - Commit with a signature identity different from the author's.
4290            // - Include entries for both author and committer to prove we call
4291            //   the right raw functions.
4292            let mailmap_file = Path::new(".mailmap");
4293            let p = Path::new(repo.workdir().unwrap()).join(&mailmap_file);
4294            t!(fs::write(
4295                p,
4296                r#"
4297Author Name <author.proper@email> name <email>
4298Committer Name <committer.proper@email> <committer@email>"#,
4299            ));
4300            let mut index = t!(repo.index());
4301            t!(index.add_path(&mailmap_file));
4302            let id_mailmap = t!(index.write_tree());
4303            let tree_mailmap = t!(repo.find_tree(id_mailmap));
4304
4305            let head = t!(repo.commit(
4306                Some("HEAD"),
4307                &author,
4308                t!(&Signature::now("committer", "committer@email")),
4309                "Add mailmap",
4310                &tree_mailmap,
4311                &[&commit],
4312            ));
4313            t!(repo.find_commit(head))
4314        };
4315
4316        // Sanity check that we're working with the right commit and that its
4317        // author and committer identities differ.
4318        let author = commit.author();
4319        let committer = commit.committer();
4320        assert_ne!(author.name(), committer.name());
4321        assert_ne!(author.email(), committer.email());
4322        assert_eq!(author.name(), Some("name"));
4323        assert_eq!(author.email(), Some("email"));
4324        assert_eq!(committer.name(), Some("committer"));
4325        assert_eq!(committer.email(), Some("committer@email"));
4326
4327        // Fetch the newly added .mailmap from the repository.
4328        let mailmap = t!(repo.mailmap());
4329        let mailmapped_author = t!(commit.author_with_mailmap(&mailmap));
4330        let mailmapped_committer = t!(commit.committer_with_mailmap(&mailmap));
4331
4332        let mm_resolve_author = t!(mailmap.resolve_signature(&author));
4333        let mm_resolve_committer = t!(mailmap.resolve_signature(&committer));
4334
4335        // Mailmap Signature lifetime is independent of Commit lifetime.
4336        drop(author);
4337        drop(committer);
4338        drop(commit);
4339
4340        // author_with_mailmap() + committer_with_mailmap() work
4341        assert_eq!(mailmapped_author.name(), Some("Author Name"));
4342        assert_eq!(mailmapped_author.email(), Some("author.proper@email"));
4343        assert_eq!(mailmapped_committer.name(), Some("Committer Name"));
4344        assert_eq!(mailmapped_committer.email(), Some("committer.proper@email"));
4345
4346        // resolve_signature() works
4347        assert_eq!(mm_resolve_author.email(), mailmapped_author.email());
4348        assert_eq!(mm_resolve_committer.email(), mailmapped_committer.email());
4349    }
4350
4351    #[test]
4352    fn smoke_find_tag_by_prefix() {
4353        let (_td, repo) = crate::test::repo_init();
4354        let head = repo.head().unwrap();
4355        let tag_oid = repo
4356            .tag(
4357                "tag",
4358                &repo
4359                    .find_object(head.peel_to_commit().unwrap().id(), None)
4360                    .unwrap(),
4361                &repo.signature().unwrap(),
4362                "message",
4363                false,
4364            )
4365            .unwrap();
4366        let tag = repo.find_tag(tag_oid).unwrap();
4367        let found_tag = repo
4368            .find_tag_by_prefix(&tag.id().to_string()[0..7])
4369            .unwrap();
4370        assert_eq!(tag.id(), found_tag.id());
4371    }
4372
4373    #[test]
4374    fn smoke_commondir() {
4375        let (td, repo) = crate::test::repo_init();
4376        assert_eq!(
4377            crate::test::realpath(repo.path()).unwrap(),
4378            crate::test::realpath(repo.commondir()).unwrap()
4379        );
4380
4381        let worktree = repo
4382            .worktree("test", &td.path().join("worktree"), None)
4383            .unwrap();
4384        let worktree_repo = Repository::open_from_worktree(&worktree).unwrap();
4385        assert_eq!(
4386            crate::test::realpath(repo.path()).unwrap(),
4387            crate::test::realpath(worktree_repo.commondir()).unwrap()
4388        );
4389    }
4390}