diff options
Diffstat (limited to 'src/bin/git_remote_nostr/fetch.rs')
| -rw-r--r-- | src/bin/git_remote_nostr/fetch.rs | 117 |
1 files changed, 75 insertions, 42 deletions
diff --git a/src/bin/git_remote_nostr/fetch.rs b/src/bin/git_remote_nostr/fetch.rs index 2e16297..46e7ad3 100644 --- a/src/bin/git_remote_nostr/fetch.rs +++ b/src/bin/git_remote_nostr/fetch.rs | |||
| @@ -1,11 +1,12 @@ | |||
| 1 | use core::str; | 1 | use core::str; |
| 2 | use std::{ | 2 | use std::{ |
| 3 | collections::HashMap, | ||
| 3 | io::Stdin, | 4 | io::Stdin, |
| 4 | sync::{Arc, Mutex}, | 5 | sync::{Arc, Mutex}, |
| 5 | time::Instant, | 6 | time::Instant, |
| 6 | }; | 7 | }; |
| 7 | 8 | ||
| 8 | use anyhow::{anyhow, bail, Result}; | 9 | use anyhow::{anyhow, bail, Context, Result}; |
| 9 | use auth_git2::GitAuthenticator; | 10 | use auth_git2::GitAuthenticator; |
| 10 | use git2::{Progress, Repository}; | 11 | use git2::{Progress, Repository}; |
| 11 | use ngit::{ | 12 | use ngit::{ |
| @@ -19,7 +20,7 @@ use ngit::{ | |||
| 19 | repo_ref::RepoRef, | 20 | repo_ref::RepoRef, |
| 20 | }; | 21 | }; |
| 21 | use nostr::nips::nip19; | 22 | use nostr::nips::nip19; |
| 22 | use nostr_sdk::ToBech32; | 23 | use nostr_sdk::{Event, ToBech32}; |
| 23 | 24 | ||
| 24 | use crate::utils::{ | 25 | use crate::utils::{ |
| 25 | count_lines_per_msg_vec, fetch_or_list_error_is_not_authentication_failure, | 26 | count_lines_per_msg_vec, fetch_or_list_error_is_not_authentication_failure, |
| @@ -78,65 +79,97 @@ pub async fn run_fetch( | |||
| 78 | 79 | ||
| 79 | fetch_batch.retain(|refstr, _| refstr.contains("refs/heads/pr/")); | 80 | fetch_batch.retain(|refstr, _| refstr.contains("refs/heads/pr/")); |
| 80 | 81 | ||
| 81 | if !fetch_batch.is_empty() { | 82 | fetch_proposals(git_repo, &term, repo_ref, &fetch_batch).await?; |
| 83 | term.flush()?; | ||
| 84 | println!(); | ||
| 85 | Ok(()) | ||
| 86 | } | ||
| 87 | |||
| 88 | pub fn make_commits_for_proposal( | ||
| 89 | git_repo: &Repo, | ||
| 90 | repo_ref: &RepoRef, | ||
| 91 | patches_ancestor_last: &[Event], | ||
| 92 | ) -> Result<String> { | ||
| 93 | let patches_ancestor_first: Vec<&Event> = patches_ancestor_last.iter().rev().collect(); | ||
| 94 | let mut tip_commit_id = if let Ok(parent_commit) = tag_value( | ||
| 95 | patches_ancestor_first | ||
| 96 | .first() | ||
| 97 | .context("proposal should have at least one patch")?, | ||
| 98 | "parent-commit", | ||
| 99 | ) { | ||
| 100 | parent_commit | ||
| 101 | } else { | ||
| 102 | // TODO choose most recent commit on master before patch timestamp so it doesnt | ||
| 103 | // constantly get rebased | ||
| 104 | let (_, hash) = git_repo.get_main_or_master_branch()?; | ||
| 105 | hash.to_string() | ||
| 106 | }; | ||
| 107 | |||
| 108 | for patch in &patches_ancestor_first { | ||
| 109 | let commit_id = git_repo | ||
| 110 | .create_commit_from_patch(patch, Some(tip_commit_id.clone())) | ||
| 111 | .context(format!( | ||
| 112 | "cannot create commit for patch {}", | ||
| 113 | nip19::Nip19Event { | ||
| 114 | event_id: patch.id(), | ||
| 115 | author: Some(patch.author()), | ||
| 116 | kind: Some(patch.kind()), | ||
| 117 | relays: if let Some(relay) = repo_ref.relays.first() { | ||
| 118 | vec![relay.to_string()] | ||
| 119 | } else { | ||
| 120 | vec![] | ||
| 121 | }, | ||
| 122 | } | ||
| 123 | .to_bech32() | ||
| 124 | .unwrap_or_default() | ||
| 125 | ))?; | ||
| 126 | tip_commit_id = commit_id.to_string(); | ||
| 127 | } | ||
| 128 | Ok(tip_commit_id) | ||
| 129 | } | ||
| 130 | |||
| 131 | async fn fetch_proposals( | ||
| 132 | git_repo: &Repo, | ||
| 133 | term: &console::Term, | ||
| 134 | repo_ref: &RepoRef, | ||
| 135 | proposal_refs: &HashMap<String, String>, | ||
| 136 | ) -> Result<()> { | ||
| 137 | if !proposal_refs.is_empty() { | ||
| 82 | let open_proposals = get_open_proposals(git_repo, repo_ref).await?; | 138 | let open_proposals = get_open_proposals(git_repo, repo_ref).await?; |
| 83 | 139 | ||
| 84 | let current_user = get_curent_user(git_repo)?; | 140 | let current_user = get_curent_user(git_repo)?; |
| 85 | 141 | ||
| 86 | for (refstr, oid) in fetch_batch { | 142 | for refstr in proposal_refs.keys() { |
| 87 | if let Some((_, (_, patches))) = | 143 | if let Some((_, (_, patches))) = |
| 88 | find_proposal_and_patches_by_branch_name(&refstr, &open_proposals, ¤t_user) | 144 | find_proposal_and_patches_by_branch_name(refstr, &open_proposals, ¤t_user) |
| 89 | { | 145 | { |
| 90 | if !git_repo.does_commit_exist(&oid)? { | 146 | if let Err(error) = make_commits_for_proposal(git_repo, repo_ref, patches) { |
| 91 | let mut patches_ancestor_first = patches.clone(); | 147 | term.write_line( |
| 92 | patches_ancestor_first.reverse(); | 148 | format!("WARNING: cannot create branch for {refstr}, error: {error}",) |
| 93 | if git_repo.does_commit_exist(&tag_value( | 149 | .as_str(), |
| 94 | patches_ancestor_first.first().unwrap(), | 150 | )?; |
| 95 | "parent-commit", | 151 | break; |
| 96 | )?)? { | ||
| 97 | for patch in &patches_ancestor_first { | ||
| 98 | if let Err(error) = git_repo.create_commit_from_patch(patch) { | ||
| 99 | term.write_line( | ||
| 100 | format!( | ||
| 101 | "WARNING: cannot create branch for {refstr}, error: {error} for patch {}", | ||
| 102 | nip19::Nip19Event { | ||
| 103 | event_id: patch.id(), | ||
| 104 | author: Some(patch.author()), | ||
| 105 | kind: Some(patch.kind()), | ||
| 106 | relays: if let Some(relay) = repo_ref.relays.first() { | ||
| 107 | vec![relay.to_string()] | ||
| 108 | } else { vec![]}, | ||
| 109 | }.to_bech32().unwrap_or_default() | ||
| 110 | ) | ||
| 111 | .as_str(), | ||
| 112 | )?; | ||
| 113 | break; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } else { | ||
| 117 | term.write_line( | ||
| 118 | format!("WARNING: cannot find parent commit for {refstr}").as_str(), | ||
| 119 | )?; | ||
| 120 | } | ||
| 121 | } | 152 | } |
| 122 | } else { | ||
| 123 | term.write_line(format!("WARNING: cannot find proposal for {refstr}").as_str())?; | ||
| 124 | } | 153 | } |
| 125 | } | 154 | } |
| 126 | } | 155 | } |
| 127 | |||
| 128 | term.flush()?; | ||
| 129 | println!(); | ||
| 130 | Ok(()) | 156 | Ok(()) |
| 131 | } | 157 | } |
| 132 | 158 | ||
| 133 | fn fetch_from_git_server( | 159 | pub fn fetch_from_git_server( |
| 134 | git_repo: &Repo, | 160 | git_repo: &Repo, |
| 135 | oids: &[String], | 161 | oids: &[String], |
| 136 | git_server_url: &str, | 162 | git_server_url: &str, |
| 137 | decoded_nostr_url: &NostrUrlDecoded, | 163 | decoded_nostr_url: &NostrUrlDecoded, |
| 138 | term: &console::Term, | 164 | term: &console::Term, |
| 139 | ) -> Result<()> { | 165 | ) -> Result<()> { |
| 166 | let already_have_oids = oids | ||
| 167 | .iter() | ||
| 168 | .all(|oid| git_repo.does_commit_exist(oid).is_ok_and(|outcome| outcome)); | ||
| 169 | if already_have_oids { | ||
| 170 | return Ok(()); | ||
| 171 | } | ||
| 172 | |||
| 140 | let server_url = git_server_url.parse::<CloneUrl>()?; | 173 | let server_url = git_server_url.parse::<CloneUrl>()?; |
| 141 | 174 | ||
| 142 | let protocols_to_attempt = get_read_protocols_to_try(git_repo, &server_url, decoded_nostr_url); | 175 | let protocols_to_attempt = get_read_protocols_to_try(git_repo, &server_url, decoded_nostr_url); |