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