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