upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/git.rs251
1 files changed, 91 insertions, 160 deletions
diff --git a/src/git.rs b/src/git.rs
index 0794788..27bd082 100644
--- a/src/git.rs
+++ b/src/git.rs
@@ -75,6 +75,7 @@ pub trait RepoActions {
75 branch_name: &str, 75 branch_name: &str,
76 patch_and_ancestors: Vec<nostr::Event>, 76 patch_and_ancestors: Vec<nostr::Event>,
77 ) -> Result<Vec<nostr::Event>>; 77 ) -> Result<Vec<nostr::Event>>;
78 fn create_commit_from_patch(&self, patch: &nostr::Event) -> Result<Oid>;
78 fn parse_starting_commits(&self, starting_commits: &str) -> Result<Vec<Sha1Hash>>; 79 fn parse_starting_commits(&self, starting_commits: &str) -> Result<Vec<Sha1Hash>>;
79 fn ancestor_of(&self, decendant: &Sha1Hash, ancestor: &Sha1Hash) -> Result<bool>; 80 fn ancestor_of(&self, decendant: &Sha1Hash, ancestor: &Sha1Hash) -> Result<bool>;
80 fn get_git_config_item(&self, item: &str, global: Option<bool>) -> Result<Option<String>>; 81 fn get_git_config_item(&self, item: &str, global: Option<bool>) -> Result<Option<String>>;
@@ -534,15 +535,99 @@ impl RepoActions for Repo {
534 for patch in &patches_to_apply { 535 for patch in &patches_to_apply {
535 let commit_id = get_commit_id_from_patch(patch)?; 536 let commit_id = get_commit_id_from_patch(patch)?;
536 // only create new commits - otherwise make them the tip 537 // only create new commits - otherwise make them the tip
537 if self.does_commit_exist(&commit_id)? { 538 if !self.does_commit_exist(&commit_id)? {
538 self.create_branch_at_commit(branch_name, &commit_id)?; 539 self.create_commit_from_patch(patch)?;
539 } else {
540 apply_patch(self, patch)?;
541 } 540 }
541 self.create_branch_at_commit(branch_name, &commit_id)?;
542 self.checkout(branch_name)?;
542 } 543 }
543 Ok(patches_to_apply) 544 Ok(patches_to_apply)
544 } 545 }
546 fn create_commit_from_patch(&self, patch: &nostr::Event) -> Result<Oid> {
547 let commit_id = get_commit_id_from_patch(patch)?;
548 if self.does_commit_exist(&commit_id)? {
549 return Ok(Oid::from_str(&commit_id)?);
550 }
551 let parent_commit_id = tag_value(patch, "parent-commit")?;
552
553 let parent_commit = self
554 .git_repo
555 .find_commit(Oid::from_str(&parent_commit_id)?)
556 .context("parrent commit doesnt exist")?;
557 let parent_tree = parent_commit.tree()?;
558
559 // let mut apply_opts = git2::ApplyOptions::new();
560 // apply_opts.check(false);
545 561
562 let mut index = self.git_repo.apply_to_tree(
563 &parent_tree,
564 &git2::Diff::from_buffer(patch.content.as_bytes())?,
565 // Some(&mut apply_opts),
566 None,
567 )?;
568 let tree = self
569 .git_repo
570 .find_tree(index.write_tree_to(&self.git_repo)?)?;
571
572 let pgp_sig = if let Ok(pgp_sig) = tag_value(patch, "commit-pgp-sig") {
573 if pgp_sig.is_empty() {
574 None
575 } else {
576 Some(pgp_sig)
577 }
578 } else {
579 None
580 };
581
582 let mut applied_oid = if let Some(pgp_sig) = pgp_sig {
583 let commit_buff = self.git_repo.commit_create_buffer(
584 &extract_sig_from_patch_tags(&patch.tags, "author")?,
585 &extract_sig_from_patch_tags(&patch.tags, "committer")?,
586 tag_value(patch, "description")?.as_str(),
587 &tree,
588 &[&parent_commit],
589 )?;
590 self.git_repo
591 .commit_signed(commit_buff.as_str().unwrap(), pgp_sig.as_str(), None)
592 .context("failed to create signed commit")?
593 } else {
594 self.git_repo
595 .commit(
596 Some("HEAD"),
597 &extract_sig_from_patch_tags(&patch.tags, "author")?,
598 &extract_sig_from_patch_tags(&patch.tags, "committer")?,
599 tag_value(patch, "description")?.as_str(),
600 &tree,
601 &[&parent_commit],
602 )
603 .context("failed to create unsigned commit")?
604 };
605 // I beleive this was added to address a bug where commit author / committer
606 // were identical when in a scenario when they should be different but I dont
607 // think we have a test case for it. surely we should be using the
608 // extract_sig_from_patch_tags outputs to address this?
609 if !applied_oid.to_string().eq(&commit_id) {
610 let commit = self.git_repo.find_commit(applied_oid)?;
611 applied_oid = commit
612 .amend(
613 None,
614 Some(&commit.author()),
615 Some(&commit.committer()),
616 None,
617 None,
618 None,
619 )
620 .context("cannot amend commit to produce new oid")?;
621 }
622 if !applied_oid.to_string().eq(&commit_id) {
623 bail!(
624 "when applied the patch commit id ({}) doesn't match the one specified in the event tag ({})",
625 applied_oid.to_string(),
626 get_commit_id_from_patch(patch)?,
627 );
628 }
629 Ok(applied_oid)
630 }
546 fn parse_starting_commits(&self, starting_commits: &str) -> Result<Vec<Sha1Hash>> { 631 fn parse_starting_commits(&self, starting_commits: &str) -> Result<Vec<Sha1Hash>> {
547 let revspec = self 632 let revspec = self
548 .git_repo 633 .git_repo
@@ -726,139 +811,6 @@ fn git_sig_to_tag_vec(sig: &git2::Signature) -> Vec<String> {
726 ] 811 ]
727} 812}
728 813
729fn apply_patch(git_repo: &Repo, patch: &nostr::Event) -> Result<()> {
730 // check parent commit matches head
731 if !git_repo
732 .get_head_commit()?
733 .to_string()
734 .eq(&tag_value(patch, "parent-commit")?)
735 {
736 bail!(
737 "patch parent ({}) doesnt match current head ({})",
738 tag_value(patch, "parent-commit")?,
739 git_repo.get_head_commit()?
740 );
741 }
742
743 let diff_from_patch = git2::Diff::from_buffer(patch.content.as_bytes()).unwrap();
744
745 let mut apply_opts = git2::ApplyOptions::new();
746 apply_opts.check(false);
747
748 git_repo.git_repo.apply(
749 &diff_from_patch,
750 git2::ApplyLocation::WorkDir,
751 Some(&mut apply_opts),
752 )?;
753 // stage and commit
754 let prev_oid = git_repo.git_repo.head().unwrap().peel_to_commit()?;
755
756 let mut index = git_repo.git_repo.index()?;
757 index.add_all(["."], git2::IndexAddOption::DEFAULT, None)?;
758 index.write()?;
759
760 let pgp_sig = if let Ok(pgp_sig) = tag_value(patch, "commit-pgp-sig") {
761 if pgp_sig.is_empty() {
762 None
763 } else {
764 Some(pgp_sig)
765 }
766 } else {
767 None
768 };
769
770 if let Some(pgp_sig) = pgp_sig {
771 let commit_buff = git_repo.git_repo.commit_create_buffer(
772 &extract_sig_from_patch_tags(&patch.tags, "author")?,
773 &extract_sig_from_patch_tags(&patch.tags, "committer")?,
774 tag_value(patch, "description")?.as_str(),
775 &git_repo.git_repo.find_tree(index.write_tree()?)?,
776 &[&prev_oid],
777 )?;
778 let gpg_commit_id = git_repo.git_repo.commit_signed(
779 commit_buff.as_str().unwrap(),
780 pgp_sig.as_str(),
781 None,
782 )?;
783 git_repo.git_repo.reset(
784 &git_repo.git_repo.find_object(gpg_commit_id, None)?,
785 git2::ResetType::Mixed,
786 None,
787 )?;
788 if gpg_commit_id
789 .to_string()
790 .eq(&get_commit_id_from_patch(patch)?)
791 {
792 return Ok(());
793 }
794 } else {
795 git_repo.git_repo.commit(
796 Some("HEAD"),
797 &extract_sig_from_patch_tags(&patch.tags, "author")?,
798 &extract_sig_from_patch_tags(&patch.tags, "committer")?,
799 tag_value(patch, "description")?.as_str(),
800 &git_repo.git_repo.find_tree(index.write_tree()?)?,
801 &[&prev_oid],
802 )?;
803 }
804 validate_patch_applied(git_repo, patch)
805}
806
807fn validate_patch_applied(git_repo: &Repo, patch: &nostr::Event) -> Result<()> {
808 // end of stage and commit
809 // check commit applied
810 if git_repo
811 .get_head_commit()?
812 .to_string()
813 .eq(&tag_value(patch, "parent-commit")?)
814 {
815 bail!("applying patch failed");
816 }
817
818 let mut revwalk = git_repo.git_repo.revwalk().context("revwalk error")?;
819 revwalk.push_head().context("revwalk.push_head")?;
820
821 for (i, oid) in revwalk.enumerate() {
822 if i == 0 {
823 let old_commit = git_repo
824 .git_repo
825 .find_commit(oid.context("cannot get oid in revwalk")?)
826 .context("cannot find newly added commit oid")?;
827 // create commit using amend which relects the original commit id
828 let updated_commit_oid = old_commit
829 .amend(
830 None,
831 Some(&old_commit.author()),
832 Some(&old_commit.committer()),
833 None,
834 None,
835 None,
836 )
837 .context("cannot amend commit to produce new oid")?;
838 // replace the commit with the wrong oid with the newly created one with the
839 // correct oid
840 git_repo
841 .git_repo
842 .head()
843 .context("cannot get head of git_repo")?
844 .set_target(updated_commit_oid, "ref commit with fix committer details")
845 .context("cannot update branch with fixed commit")?;
846
847 if !updated_commit_oid
848 .to_string()
849 .eq(&get_commit_id_from_patch(patch)?)
850 {
851 bail!(
852 "when applied the patch commit id ({}) doesn't match the one specified in the event tag ({})",
853 updated_commit_oid.to_string(),
854 get_commit_id_from_patch(patch)?,
855 )
856 }
857 }
858 }
859 Ok(())
860}
861
862fn extract_sig_from_patch_tags<'a>( 814fn extract_sig_from_patch_tags<'a>(
863 tags: &'a [nostr::Tag], 815 tags: &'a [nostr::Tag],
864 tag_name: &str, 816 tag_name: &str,
@@ -1674,7 +1626,7 @@ mod tests {
1674 } 1626 }
1675 } 1627 }
1676 1628
1677 mod apply_patch { 1629 mod create_commit_from_patch {
1678 1630
1679 use test_utils::TEST_KEY_1_SIGNER; 1631 use test_utils::TEST_KEY_1_SIGNER;
1680 1632
@@ -1704,31 +1656,10 @@ mod tests {
1704 test_repo.populate()?; 1656 test_repo.populate()?;
1705 let git_repo = Repo::from_path(&test_repo.dir)?; 1657 let git_repo = Repo::from_path(&test_repo.dir)?;
1706 println!("{:?}", &patch_event); 1658 println!("{:?}", &patch_event);
1707 apply_patch(&git_repo, &patch_event)?; 1659 git_repo.create_commit_from_patch(&patch_event)?;
1708 let commit_id = tag_value(&patch_event, "commit")?; 1660 let commit_id = tag_value(&patch_event, "commit")?;
1709 // does commit with id exist? 1661 // does commit with id exist?
1710 assert!(git_repo.does_commit_exist(&commit_id)?); 1662 assert!(git_repo.does_commit_exist(&commit_id)?);
1711 // is commit head
1712 assert_eq!(
1713 test_repo
1714 .git_repo
1715 .head()?
1716 .peel_to_commit()?
1717 .id()
1718 .to_string(),
1719 commit_id,
1720 );
1721 // applied to current checked branch (head hasn't moved to specific commit)
1722 assert_eq!(
1723 test_repo
1724 .git_repo
1725 .head()?
1726 .shorthand()
1727 .context("an object without a shorthand is checked out")?
1728 .to_string(),
1729 "main",
1730 );
1731
1732 Ok(()) 1663 Ok(())
1733 } 1664 }
1734 1665