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