diff options
Diffstat (limited to 'src/git.rs')
| -rw-r--r-- | src/git.rs | 137 |
1 files changed, 137 insertions, 0 deletions
| @@ -33,6 +33,7 @@ pub trait RepoActions { | |||
| 33 | fn get_checked_out_branch_name(&self) -> Result<String>; | 33 | fn get_checked_out_branch_name(&self) -> Result<String>; |
| 34 | fn get_tip_of_local_branch(&self, branch_name: &str) -> Result<Sha1Hash>; | 34 | fn get_tip_of_local_branch(&self, branch_name: &str) -> Result<Sha1Hash>; |
| 35 | fn get_root_commit(&self, branch_name: &str) -> Result<Sha1Hash>; | 35 | fn get_root_commit(&self, branch_name: &str) -> Result<Sha1Hash>; |
| 36 | fn does_commit_exist(&self, commit: &str) -> Result<bool>; | ||
| 36 | fn get_head_commit(&self) -> Result<Sha1Hash>; | 37 | fn get_head_commit(&self) -> Result<Sha1Hash>; |
| 37 | fn get_commit_parent(&self, commit: &Sha1Hash) -> Result<Sha1Hash>; | 38 | fn get_commit_parent(&self, commit: &Sha1Hash) -> Result<Sha1Hash>; |
| 38 | fn get_commits_ahead_behind( | 39 | fn get_commits_ahead_behind( |
| @@ -41,6 +42,8 @@ pub trait RepoActions { | |||
| 41 | latest_commit: &Sha1Hash, | 42 | latest_commit: &Sha1Hash, |
| 42 | ) -> Result<(Vec<Sha1Hash>, Vec<Sha1Hash>)>; | 43 | ) -> Result<(Vec<Sha1Hash>, Vec<Sha1Hash>)>; |
| 43 | fn make_patch_from_commit(&self, commit: &Sha1Hash) -> Result<String>; | 44 | fn make_patch_from_commit(&self, commit: &Sha1Hash) -> Result<String>; |
| 45 | fn checkout(&self, ref_name: &str) -> Result<()>; | ||
| 46 | fn create_branch_at_commit(&self, branch_name: &str, commit: &str) -> Result<()>; | ||
| 44 | } | 47 | } |
| 45 | 48 | ||
| 46 | impl RepoActions for Repo { | 49 | impl RepoActions for Repo { |
| @@ -118,6 +121,14 @@ impl RepoActions for Repo { | |||
| 118 | )) | 121 | )) |
| 119 | } | 122 | } |
| 120 | 123 | ||
| 124 | fn does_commit_exist(&self, commit: &str) -> Result<bool> { | ||
| 125 | if let Ok(c) = self.git_repo.find_commit(Oid::from_str(commit)?) { | ||
| 126 | Ok(true) | ||
| 127 | } else { | ||
| 128 | Ok(false) | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 121 | fn get_head_commit(&self) -> Result<Sha1Hash> { | 132 | fn get_head_commit(&self) -> Result<Sha1Hash> { |
| 122 | let head = self | 133 | let head = self |
| 123 | .git_repo | 134 | .git_repo |
| @@ -213,6 +224,31 @@ impl RepoActions for Repo { | |||
| 213 | }); | 224 | }); |
| 214 | Ok((ahead, behind)) | 225 | Ok((ahead, behind)) |
| 215 | } | 226 | } |
| 227 | |||
| 228 | fn checkout(&self, ref_name: &str) -> Result<()> { | ||
| 229 | let (object, reference) = self.git_repo.revparse_ext(ref_name)?; | ||
| 230 | |||
| 231 | self.git_repo.checkout_tree(&object, None)?; | ||
| 232 | |||
| 233 | match reference { | ||
| 234 | // gref is an actual reference like branches or tags | ||
| 235 | Some(gref) => self.git_repo.set_head(gref.name().unwrap()), | ||
| 236 | // this is a commit, not a reference | ||
| 237 | None => self.git_repo.set_head_detached(object.id()), | ||
| 238 | }?; | ||
| 239 | Ok(()) | ||
| 240 | } | ||
| 241 | |||
| 242 | fn create_branch_at_commit(&self, branch_name: &str, commit: &str) -> Result<()> { | ||
| 243 | self.git_repo | ||
| 244 | .branch( | ||
| 245 | branch_name, | ||
| 246 | &self.git_repo.find_commit(Oid::from_str(commit)?)?, | ||
| 247 | false, | ||
| 248 | ) | ||
| 249 | .context("branch could not be created")?; | ||
| 250 | Ok(()) | ||
| 251 | } | ||
| 216 | } | 252 | } |
| 217 | 253 | ||
| 218 | fn oid_to_u8_20_bytes(oid: &Oid) -> [u8; 20] { | 254 | fn oid_to_u8_20_bytes(oid: &Oid) -> [u8; 20] { |
| @@ -272,6 +308,42 @@ mod tests { | |||
| 272 | Ok(()) | 308 | Ok(()) |
| 273 | } | 309 | } |
| 274 | 310 | ||
| 311 | mod does_commit_exist { | ||
| 312 | use super::*; | ||
| 313 | |||
| 314 | #[test] | ||
| 315 | fn existing_commits_results_in_true() -> Result<()> { | ||
| 316 | let test_repo = GitTestRepo::default(); | ||
| 317 | let oid = test_repo.populate()?; | ||
| 318 | let git_repo = Repo::from_path(&test_repo.dir)?; | ||
| 319 | |||
| 320 | assert!(git_repo.does_commit_exist(&"431b84edc0d2fa118d63faa3c2db9c73d630a5ae")?); | ||
| 321 | Ok(()) | ||
| 322 | } | ||
| 323 | |||
| 324 | #[test] | ||
| 325 | fn correctly_formatted_hash_that_doesnt_correspond_to_an_existing_commit_results_in_false() | ||
| 326 | -> Result<()> { | ||
| 327 | let test_repo = GitTestRepo::default(); | ||
| 328 | let oid = test_repo.populate()?; | ||
| 329 | let git_repo = Repo::from_path(&test_repo.dir)?; | ||
| 330 | |||
| 331 | assert!(!git_repo.does_commit_exist(&"000004edc0d2fa118d63faa3c2db9c73d630a5ae")?); | ||
| 332 | Ok(()) | ||
| 333 | } | ||
| 334 | |||
| 335 | #[test] | ||
| 336 | fn incorrectly_formatted_hash_that_doesnt_correspond_to_an_existing_commit_results_in_error() | ||
| 337 | -> Result<()> { | ||
| 338 | let test_repo = GitTestRepo::default(); | ||
| 339 | let oid = test_repo.populate()?; | ||
| 340 | let git_repo = Repo::from_path(&test_repo.dir)?; | ||
| 341 | |||
| 342 | assert!(!git_repo.does_commit_exist(&"00").is_err()); | ||
| 343 | Ok(()) | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 275 | mod make_patch_from_commit { | 347 | mod make_patch_from_commit { |
| 276 | use super::*; | 348 | use super::*; |
| 277 | #[test] | 349 | #[test] |
| @@ -521,4 +593,69 @@ mod tests { | |||
| 521 | } | 593 | } |
| 522 | } | 594 | } |
| 523 | } | 595 | } |
| 596 | |||
| 597 | mod create_branch_at_commit { | ||
| 598 | use super::*; | ||
| 599 | #[test] | ||
| 600 | fn doesnt_error() -> Result<()> { | ||
| 601 | let test_repo = GitTestRepo::default(); | ||
| 602 | test_repo.populate()?; | ||
| 603 | // create feature branch and add 2 commits | ||
| 604 | test_repo.create_branch("feature")?; | ||
| 605 | test_repo.checkout("feature")?; | ||
| 606 | std::fs::write(test_repo.dir.join("t3.md"), "some content")?; | ||
| 607 | let ahead_1_oid = test_repo.stage_and_commit("add t3.md")?; | ||
| 608 | std::fs::write(test_repo.dir.join("t4.md"), "some content")?; | ||
| 609 | test_repo.stage_and_commit("add t4.md")?; | ||
| 610 | |||
| 611 | let git_repo = Repo::from_path(&test_repo.dir)?; | ||
| 612 | |||
| 613 | let branch_name = "test-name-1"; | ||
| 614 | git_repo.create_branch_at_commit(branch_name, &ahead_1_oid.to_string())?; | ||
| 615 | |||
| 616 | Ok(()) | ||
| 617 | } | ||
| 618 | |||
| 619 | #[test] | ||
| 620 | fn branch_gets_created() -> Result<()> { | ||
| 621 | let test_repo = GitTestRepo::default(); | ||
| 622 | test_repo.populate()?; | ||
| 623 | // create feature branch and add 2 commits | ||
| 624 | test_repo.create_branch("feature")?; | ||
| 625 | test_repo.checkout("feature")?; | ||
| 626 | std::fs::write(test_repo.dir.join("t3.md"), "some content")?; | ||
| 627 | let ahead_1_oid = test_repo.stage_and_commit("add t3.md")?; | ||
| 628 | std::fs::write(test_repo.dir.join("t4.md"), "some content")?; | ||
| 629 | test_repo.stage_and_commit("add t4.md")?; | ||
| 630 | |||
| 631 | let git_repo = Repo::from_path(&test_repo.dir)?; | ||
| 632 | |||
| 633 | let branch_name = "test-name-1"; | ||
| 634 | git_repo.create_branch_at_commit(branch_name, &ahead_1_oid.to_string())?; | ||
| 635 | |||
| 636 | assert!(test_repo.checkout(&branch_name).is_ok()); | ||
| 637 | Ok(()) | ||
| 638 | } | ||
| 639 | |||
| 640 | #[test] | ||
| 641 | fn branch_created_with_correct_commit() -> Result<()> { | ||
| 642 | let test_repo = GitTestRepo::default(); | ||
| 643 | test_repo.populate()?; | ||
| 644 | // create feature branch and add 2 commits | ||
| 645 | test_repo.create_branch("feature")?; | ||
| 646 | test_repo.checkout("feature")?; | ||
| 647 | std::fs::write(test_repo.dir.join("t3.md"), "some content")?; | ||
| 648 | let ahead_1_oid = test_repo.stage_and_commit("add t3.md")?; | ||
| 649 | std::fs::write(test_repo.dir.join("t4.md"), "some content")?; | ||
| 650 | test_repo.stage_and_commit("add t4.md")?; | ||
| 651 | |||
| 652 | let git_repo = Repo::from_path(&test_repo.dir)?; | ||
| 653 | |||
| 654 | let branch_name = "test-name-1"; | ||
| 655 | git_repo.create_branch_at_commit(branch_name, &ahead_1_oid.to_string())?; | ||
| 656 | |||
| 657 | assert_eq!(test_repo.checkout(&branch_name)?, ahead_1_oid); | ||
| 658 | Ok(()) | ||
| 659 | } | ||
| 660 | } | ||
| 524 | } | 661 | } |