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