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