From 3eb2354edb8e76428625d5645e110c30aa1ccc2a Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Fri, 18 Jul 2025 11:56:15 +0100 Subject: feat(pr): list PR and PR updates remote will list the refs under `pr/*` namespace. `ngit list` will display in the list of open / draft proposals. it won't yet fetch the related oids to enable fetching or checking out the branch. --- src/bin/git_remote_nostr/list.rs | 56 +++++++++++--- src/bin/git_remote_nostr/utils.rs | 25 +++--- src/bin/ngit/sub_commands/list.rs | 157 ++++++++++++++++++++++++++------------ src/lib/client.rs | 37 +++++++-- src/lib/git_events.rs | 50 +++++++----- 5 files changed, 233 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/bin/git_remote_nostr/list.rs b/src/bin/git_remote_nostr/list.rs index b9fb0c0..7bdf170 100644 --- a/src/bin/git_remote_nostr/list.rs +++ b/src/bin/git_remote_nostr/list.rs @@ -11,7 +11,7 @@ use ngit::{ self, nostr_url::{CloneUrl, NostrUrlDecoded, ServerProtocol}, }, - git_events::event_to_cover_letter, + git_events::{KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, event_to_cover_letter, tag_value}, login::get_curent_user, repo_ref::{self, is_grasp_server}, }; @@ -122,6 +122,16 @@ async fn get_open_and_draft_proposals_state( // without trusting commit_id we must apply each patch which requires the oid of // the parent so we much do a fetch + + // As we are fetching from git servers we mighgt as well get oids from pull + // request too + // TODO get Pull Request and Pull Request Update Events add these to + // refs/nostr/ + // TODO prepare PRs and PRS oids to try and fetch from repo servers that are or + // clone urls in PR/update event we are using anyway. TODO after we tried + // and failed to get them from these server we should fallback to fetch them + // from listed clone urls in PR/update but not during list, only during fetch + for (git_server_url, (oids_from_git_servers, is_grasp_server)) in remote_states { if fetch_from_git_server( git_repo, @@ -144,7 +154,7 @@ async fn get_open_and_draft_proposals_state( let mut state = HashMap::new(); let open_and_draft_proposals = get_open_or_draft_proposals(git_repo, repo_ref).await?; let current_user = get_curent_user(git_repo)?; - for (_, (proposal, patches)) in open_and_draft_proposals { + for (_, (proposal, events_to_apply)) in open_and_draft_proposals { if let Ok(cl) = event_to_cover_letter(&proposal) { if let Ok(mut branch_name) = cl.get_branch_name_with_pr_prefix_and_shorthand_id() { branch_name = if let Some(public_key) = current_user { @@ -156,15 +166,43 @@ async fn get_open_and_draft_proposals_state( } else { branch_name }; - match make_commits_for_proposal(git_repo, repo_ref, &patches) { - Ok(tip) => { - state.insert(format!("refs/heads/{branch_name}"), tip); + // if events_to_apply contains a PR or PR Update event it should be the only + // event in the Vec + if let Some(pr_or_pr_update) = events_to_apply + .iter() + .find(|e| e.kind.eq(&KIND_PULL_REQUEST) || e.kind.eq(&KIND_PULL_REQUEST_UPDATE)) + { + match tag_value(pr_or_pr_update, "c") { + Ok(tip) => { + state.insert(format!("refs/heads/{branch_name}"), tip); + } + Err(_) => { + let _ = term.write_line( + format!( + "WARNING: failed to fetch branch {branch_name} error: {} event poorly formatted", + if pr_or_pr_update.kind.eq(&KIND_PULL_REQUEST) { + "PR" + } else { + "PR update" + } + ) + .as_str(), + ); + } } - Err(error) => { - let _ = term.write_line( - format!("WARNING: failed to fetch branch {branch_name} error: {error}") + } else { + match make_commits_for_proposal(git_repo, repo_ref, &events_to_apply) { + Ok(tip) => { + state.insert(format!("refs/heads/{branch_name}"), tip); + } + Err(error) => { + let _ = term.write_line( + format!( + "WARNING: failed to fetch branch {branch_name} error: {error}" + ) .as_str(), - ); + ); + } } } } diff --git a/src/bin/git_remote_nostr/utils.rs b/src/bin/git_remote_nostr/utils.rs index dc75872..d0b4e7e 100644 --- a/src/bin/git_remote_nostr/utils.rs +++ b/src/bin/git_remote_nostr/utils.rs @@ -10,7 +10,7 @@ use anyhow::{Context, Result, bail}; use git2::Repository; use ngit::{ client::{ - get_all_proposal_patch_events_from_cache, get_events_from_local_cache, + get_all_proposal_patch_pr_pr_update_events_from_cache, get_events_from_local_cache, get_proposals_and_revisions_from_cache, }, git::{ @@ -18,7 +18,7 @@ use ngit::{ nostr_url::{CloneUrl, NostrUrlDecoded, ServerProtocol}, }, git_events::{ - event_is_revision_root, get_most_recent_patch_with_ancestors, + event_is_revision_root, get_pr_tip_event_or_most_recent_patch_with_ancestors, is_event_proposal_root_for_branch, status_kinds, }, repo_ref::RepoRef, @@ -140,12 +140,15 @@ pub async fn get_open_or_draft_proposals( Kind::GitStatusOpen }; if [Kind::GitStatusOpen, Kind::GitStatusDraft].contains(&status) { - if let Ok(commits_events) = - get_all_proposal_patch_events_from_cache(git_repo_path, repo_ref, &proposal.id) - .await + if let Ok(commits_events) = get_all_proposal_patch_pr_pr_update_events_from_cache( + git_repo_path, + repo_ref, + &proposal.id, + ) + .await { if let Ok(most_recent_proposal_patch_chain) = - get_most_recent_patch_with_ancestors(commits_events.clone()) + get_pr_tip_event_or_most_recent_patch_with_ancestors(commits_events.clone()) { open_or_draft_proposals .insert(proposal.id, (proposal, most_recent_proposal_patch_chain)); @@ -172,11 +175,15 @@ pub async fn get_all_proposals( let mut all_proposals = HashMap::new(); for proposal in proposals { - if let Ok(commits_events) = - get_all_proposal_patch_events_from_cache(git_repo_path, repo_ref, &proposal.id).await + if let Ok(commits_events) = get_all_proposal_patch_pr_pr_update_events_from_cache( + git_repo_path, + repo_ref, + &proposal.id, + ) + .await { if let Ok(most_recent_proposal_patch_chain) = - get_most_recent_patch_with_ancestors(commits_events.clone()) + get_pr_tip_event_or_most_recent_patch_with_ancestors(commits_events.clone()) { all_proposals.insert(proposal.id, (proposal, most_recent_proposal_patch_chain)); } diff --git a/src/bin/ngit/sub_commands/list.rs b/src/bin/ngit/sub_commands/list.rs index 0330be1..a90b28e 100644 --- a/src/bin/ngit/sub_commands/list.rs +++ b/src/bin/ngit/sub_commands/list.rs @@ -3,10 +3,12 @@ use std::{io::Write, ops::Add}; use anyhow::{Context, Result, bail}; use ngit::{ client::{ - Params, get_all_proposal_patch_events_from_cache, get_proposals_and_revisions_from_cache, + Params, get_all_proposal_patch_pr_pr_update_events_from_cache, + get_proposals_and_revisions_from_cache, }, git_events::{ - get_commit_id_from_patch, get_most_recent_patch_with_ancestors, status_kinds, tag_value, + KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, get_commit_id_from_patch, + get_pr_tip_event_or_most_recent_patch_with_ancestors, status_kinds, tag_value, }, }; use nostr_sdk::Kind; @@ -184,21 +186,22 @@ pub async fn launch() -> Result<()> { let cover_letter = event_to_cover_letter(proposals_for_status[selected_index]) .context("failed to extract proposal details from proposal root event")?; - let commits_events: Vec = get_all_proposal_patch_events_from_cache( - git_repo_path, - &repo_ref, - &proposals_for_status[selected_index].id, - ) - .await?; + let commits_events: Vec = + get_all_proposal_patch_pr_pr_update_events_from_cache( + git_repo_path, + &repo_ref, + &proposals_for_status[selected_index].id, + ) + .await?; - let Ok(most_recent_proposal_patch_chain) = - get_most_recent_patch_with_ancestors(commits_events.clone()) + let Ok(most_recent_proposal_patch_chain_or_pr_or_pr_update) = + get_pr_tip_event_or_most_recent_patch_with_ancestors(commits_events.clone()) else { if Interactor::default().confirm( PromptConfirmParms::default() .with_default(true) .with_prompt( - "failed to find any patches on this proposal. choose another proposal?", + "failed to find any PR or patch events on this proposal. choose another proposal?", ), )? { continue; @@ -208,15 +211,37 @@ pub async fn launch() -> Result<()> { // for commit in &most_recent_proposal_patch_chain { // println!("recent_event: {:?}", commit.as_json()); // } + if most_recent_proposal_patch_chain_or_pr_or_pr_update + .iter() + .any(|e| [KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE].contains(&e.kind)) + { + match Interactor::default().choice( + PromptChoiceParms::default() + .with_prompt("this is new PR event kind which ngit doesnt yet support") + .with_default(0) + .with_choices(vec!["back to proposals".to_string()]), + )? { + 0 => continue, + _ => { + bail!("unexpected choice") + } + }; + } - let binding_patch_text_ref = format!("{} commits", most_recent_proposal_patch_chain.len()); - let patch_text_ref = if most_recent_proposal_patch_chain.len().gt(&1) { + let binding_patch_text_ref = format!( + "{} commits", + most_recent_proposal_patch_chain_or_pr_or_pr_update.len() + ); + let patch_text_ref = if most_recent_proposal_patch_chain_or_pr_or_pr_update + .len() + .gt(&1) + { binding_patch_text_ref.as_str() } else { "1 commit" }; - let no_support_for_patches_as_branch = most_recent_proposal_patch_chain + let no_support_for_patches_as_branch = most_recent_proposal_patch_chain_or_pr_or_pr_update .iter() .any(|event| !patch_supports_commit_ids(event)); @@ -253,8 +278,13 @@ pub async fn launch() -> Result<()> { )?; continue; } - 1 => launch_git_am_with_patches(most_recent_proposal_patch_chain), - 2 => save_patches_to_dir(most_recent_proposal_patch_chain, &git_repo), + 1 => { + launch_git_am_with_patches(most_recent_proposal_patch_chain_or_pr_or_pr_update) + } + 2 => save_patches_to_dir( + most_recent_proposal_patch_chain_or_pr_or_pr_update, + &git_repo, + ), 3 => continue, _ => { bail!("unexpected choice") @@ -277,9 +307,11 @@ pub async fn launch() -> Result<()> { .eq(&cover_letter.get_branch_name_with_pr_prefix_and_shorthand_id()?); let proposal_base_commit = str_to_sha1(&tag_value( - most_recent_proposal_patch_chain.last().context( - "there should be at least one patch as we have already checked for this", - )?, + most_recent_proposal_patch_chain_or_pr_or_pr_update + .last() + .context( + "there should be at least one patch as we have already checked for this", + )?, "parent-commit", )?) .context("failed to get valid parent commit id from patch")?; @@ -300,8 +332,8 @@ pub async fn launch() -> Result<()> { ], ))? { 0 | 3 => continue, - 1 => launch_git_am_with_patches(most_recent_proposal_patch_chain), - 2 => save_patches_to_dir(most_recent_proposal_patch_chain, &git_repo), + 1 => launch_git_am_with_patches(most_recent_proposal_patch_chain_or_pr_or_pr_update), + 2 => save_patches_to_dir(most_recent_proposal_patch_chain_or_pr_or_pr_update, &git_repo), _ => { bail!("unexpected choice") } @@ -309,9 +341,13 @@ pub async fn launch() -> Result<()> { } let proposal_tip = str_to_sha1( - &get_commit_id_from_patch(most_recent_proposal_patch_chain.first().context( - "there should be at least one patch as we have already checked for this", - )?) + &get_commit_id_from_patch( + most_recent_proposal_patch_chain_or_pr_or_pr_update + .first() + .context( + "there should be at least one patch as we have already checked for this", + )?, + ) .context("failed to get valid commit_id from patch")?, ) .context("failed to get valid commit_id from patch")?; @@ -325,7 +361,7 @@ pub async fn launch() -> Result<()> { .choice(PromptChoiceParms::default().with_default(0).with_choices(vec![ format!( "create and checkout proposal branch ({} ahead {} behind '{main_branch_name}')", - most_recent_proposal_patch_chain.len(), + most_recent_proposal_patch_chain_or_pr_or_pr_update.len(), proposal_behind_main.len(), ), format!("apply to current branch with `git am`"), @@ -337,7 +373,7 @@ pub async fn launch() -> Result<()> { let _ = git_repo .apply_patch_chain( &cover_letter.get_branch_name_with_pr_prefix_and_shorthand_id()?, - most_recent_proposal_patch_chain, + most_recent_proposal_patch_chain_or_pr_or_pr_update, ) .context("failed to apply patch chain")?; @@ -347,8 +383,8 @@ pub async fn launch() -> Result<()> { ); Ok(()) } - 1 => launch_git_am_with_patches(most_recent_proposal_patch_chain), - 2 => save_patches_to_dir(most_recent_proposal_patch_chain, &git_repo), + 1 => launch_git_am_with_patches(most_recent_proposal_patch_chain_or_pr_or_pr_update), + 2 => save_patches_to_dir(most_recent_proposal_patch_chain_or_pr_or_pr_update, &git_repo), 3 => continue, _ => { bail!("unexpected choice") @@ -382,7 +418,7 @@ pub async fn launch() -> Result<()> { .with_choices(vec![ format!( "checkout proposal branch ({} ahead {} behind '{main_branch_name}')", - most_recent_proposal_patch_chain.len(), + most_recent_proposal_patch_chain_or_pr_or_pr_update.len(), proposal_behind_main.len(), ), format!("apply to current branch with `git am`"), @@ -401,8 +437,13 @@ pub async fn launch() -> Result<()> { ); Ok(()) } - 1 => launch_git_am_with_patches(most_recent_proposal_patch_chain), - 2 => save_patches_to_dir(most_recent_proposal_patch_chain, &git_repo), + 1 => { + launch_git_am_with_patches(most_recent_proposal_patch_chain_or_pr_or_pr_update) + } + 2 => save_patches_to_dir( + most_recent_proposal_patch_chain_or_pr_or_pr_update, + &git_repo, + ), 3 => continue, _ => { bail!("unexpected choice") @@ -414,11 +455,14 @@ pub async fn launch() -> Result<()> { git_repo.get_commits_ahead_behind(&master_tip, &local_branch_tip)?; // new appendments to proposal - if let Some(index) = most_recent_proposal_patch_chain.iter().position(|patch| { - get_commit_id_from_patch(patch) - .unwrap_or_default() - .eq(&local_branch_tip.to_string()) - }) { + if let Some(index) = most_recent_proposal_patch_chain_or_pr_or_pr_update + .iter() + .position(|patch| { + get_commit_id_from_patch(patch) + .unwrap_or_default() + .eq(&local_branch_tip.to_string()) + }) + { return match Interactor::default().choice( PromptChoiceParms::default() .with_default(0) @@ -437,7 +481,7 @@ pub async fn launch() -> Result<()> { let _ = git_repo .apply_patch_chain( &cover_letter.get_branch_name_with_pr_prefix_and_shorthand_id()?, - most_recent_proposal_patch_chain, + most_recent_proposal_patch_chain_or_pr_or_pr_update, ) .context("failed to apply patch chain")?; println!( @@ -448,8 +492,13 @@ pub async fn launch() -> Result<()> { ); Ok(()) } - 1 => launch_git_am_with_patches(most_recent_proposal_patch_chain), - 2 => save_patches_to_dir(most_recent_proposal_patch_chain, &git_repo), + 1 => { + launch_git_am_with_patches(most_recent_proposal_patch_chain_or_pr_or_pr_update) + } + 2 => save_patches_to_dir( + most_recent_proposal_patch_chain_or_pr_or_pr_update, + &git_repo, + ), 3 => continue, _ => { bail!("unexpected choice") @@ -467,7 +516,7 @@ pub async fn launch() -> Result<()> { }) { println!( "updated proposal available ({} ahead {} behind '{main_branch_name}'). existing version is {} ahead {} behind '{main_branch_name}'", - most_recent_proposal_patch_chain.len(), + most_recent_proposal_patch_chain_or_pr_or_pr_update.len(), proposal_behind_main.len(), local_ahead_of_main.len(), local_beind_main.len(), @@ -492,11 +541,11 @@ pub async fn launch() -> Result<()> { git_repo.checkout( &cover_letter.get_branch_name_with_pr_prefix_and_shorthand_id()?, )?; - let chain_length = most_recent_proposal_patch_chain.len(); + let chain_length = most_recent_proposal_patch_chain_or_pr_or_pr_update.len(); let _ = git_repo .apply_patch_chain( &cover_letter.get_branch_name_with_pr_prefix_and_shorthand_id()?, - most_recent_proposal_patch_chain, + most_recent_proposal_patch_chain_or_pr_or_pr_update, ) .context("failed to apply patch chain")?; println!( @@ -520,8 +569,13 @@ pub async fn launch() -> Result<()> { ); Ok(()) } - 2 => launch_git_am_with_patches(most_recent_proposal_patch_chain), - 3 => save_patches_to_dir(most_recent_proposal_patch_chain, &git_repo), + 2 => { + launch_git_am_with_patches(most_recent_proposal_patch_chain_or_pr_or_pr_update) + } + 3 => save_patches_to_dir( + most_recent_proposal_patch_chain_or_pr_or_pr_update, + &git_repo, + ), 4 => continue, _ => { bail!("unexpected choice") @@ -581,7 +635,7 @@ pub async fn launch() -> Result<()> { if git_repo.does_commit_exist(&proposal_tip.to_string())? { println!( "you have previously applied the latest version of the proposal ({} ahead {} behind '{main_branch_name}') but your local proposal branch has amended or rebased it ({} ahead {} behind '{main_branch_name}')", - most_recent_proposal_patch_chain.len(), + most_recent_proposal_patch_chain_or_pr_or_pr_update.len(), proposal_behind_main.len(), local_ahead_of_main.len(), local_beind_main.len(), @@ -594,7 +648,7 @@ pub async fn launch() -> Result<()> { "your local proposal branch ({} ahead {} behind '{main_branch_name}') has conflicting changes with the latest published proposal ({} ahead {} behind '{main_branch_name}')", local_ahead_of_main.len(), local_beind_main.len(), - most_recent_proposal_patch_chain.len(), + most_recent_proposal_patch_chain_or_pr_or_pr_update.len(), proposal_behind_main.len(), ); @@ -639,11 +693,11 @@ pub async fn launch() -> Result<()> { &cover_letter.get_branch_name_with_pr_prefix_and_shorthand_id()?, &proposal_base_commit.to_string(), )?; - let chain_length = most_recent_proposal_patch_chain.len(); + let chain_length = most_recent_proposal_patch_chain_or_pr_or_pr_update.len(); let _ = git_repo .apply_patch_chain( &cover_letter.get_branch_name_with_pr_prefix_and_shorthand_id()?, - most_recent_proposal_patch_chain, + most_recent_proposal_patch_chain_or_pr_or_pr_update, ) .context("failed to apply patch chain")?; @@ -658,8 +712,11 @@ pub async fn launch() -> Result<()> { ); Ok(()) } - 2 => launch_git_am_with_patches(most_recent_proposal_patch_chain), - 3 => save_patches_to_dir(most_recent_proposal_patch_chain, &git_repo), + 2 => launch_git_am_with_patches(most_recent_proposal_patch_chain_or_pr_or_pr_update), + 3 => save_patches_to_dir( + most_recent_proposal_patch_chain_or_pr_or_pr_update, + &git_repo, + ), 4 => continue, _ => { bail!("unexpected choice") diff --git a/src/lib/client.rs b/src/lib/client.rs index ae3b414..1f3b08c 100644 --- a/src/lib/client.rs +++ b/src/lib/client.rs @@ -1811,7 +1811,7 @@ pub async fn get_proposals_and_revisions_from_cache( git_repo_path, vec![ nostr::Filter::default() - .kind(nostr::Kind::GitPatch) + .kinds([nostr::Kind::GitPatch, KIND_PULL_REQUEST]) .custom_tags( nostr::SingleLetterTag::lowercase(nostr_sdk::Alphabet::A), repo_coordinates @@ -1823,7 +1823,7 @@ pub async fn get_proposals_and_revisions_from_cache( ) .await? .iter() - .filter(|e| event_is_patch_set_root(e)) + .filter(|e| event_is_patch_set_root(e) || e.kind.eq(&KIND_PULL_REQUEST)) .cloned() .collect::>(); proposals.sort_by_key(|e| e.created_at); @@ -1831,7 +1831,7 @@ pub async fn get_proposals_and_revisions_from_cache( Ok(proposals) } -pub async fn get_all_proposal_patch_events_from_cache( +pub async fn get_all_proposal_patch_pr_pr_update_events_from_cache( git_repo_path: &Path, repo_ref: &RepoRef, proposal_id: &nostr::EventId, @@ -1840,10 +1840,21 @@ pub async fn get_all_proposal_patch_events_from_cache( git_repo_path, vec![ nostr::Filter::default() - .kind(nostr::Kind::GitPatch) + .kinds([ + nostr::Kind::GitPatch, + KIND_PULL_REQUEST, + KIND_PULL_REQUEST_UPDATE, + ]) .event(*proposal_id), nostr::Filter::default() - .kind(nostr::Kind::GitPatch) + .kinds([ + nostr::Kind::GitPatch, + KIND_PULL_REQUEST, + KIND_PULL_REQUEST_UPDATE, + ]) + .custom_tag(SingleLetterTag::uppercase(Alphabet::E), *proposal_id), + nostr::Filter::default() + .kinds([nostr::Kind::GitPatch, KIND_PULL_REQUEST]) .id(*proposal_id), ], ) @@ -1876,8 +1887,20 @@ pub async fn get_all_proposal_patch_events_from_cache( git_repo_path, vec![ nostr::Filter::default() - .kind(nostr::Kind::GitPatch) - .events(revision_roots) + .kinds([ + nostr::Kind::GitPatch, + KIND_PULL_REQUEST, + KIND_PULL_REQUEST_UPDATE, + ]) + .events(revision_roots.clone()) + .authors(permissioned_users.clone()), + nostr::Filter::default() + .kinds([ + nostr::Kind::GitPatch, + KIND_PULL_REQUEST, + KIND_PULL_REQUEST_UPDATE, + ]) + .custom_tags(SingleLetterTag::uppercase(Alphabet::E), revision_roots) .authors(permissioned_users.clone()), ], ) diff --git a/src/lib/git_events.rs b/src/lib/git_events.rs index 80793bd..7b25daf 100644 --- a/src/lib/git_events.rs +++ b/src/lib/git_events.rs @@ -70,11 +70,16 @@ pub fn event_is_patch_set_root(event: &Event) -> bool { } pub fn event_is_revision_root(event: &Event) -> bool { - event.kind.eq(&Kind::GitPatch) + (event.kind.eq(&Kind::GitPatch) && event .tags .iter() - .any(|t| t.as_slice().len() > 1 && t.as_slice()[1].eq("revision-root")) + .any(|t| t.as_slice().len() > 1 && t.as_slice()[1].eq("revision-root"))) + || (event.kind.eq(&KIND_PULL_REQUEST) + && event + .tags + .iter() + .any(|t| t.as_slice().len() > 1 && t.as_slice()[0].eq("e"))) } pub fn patch_supports_commit_ids(event: &Event) -> bool { @@ -534,13 +539,22 @@ pub fn commit_msg_from_patch_oneliner(patch: &nostr::Event) -> Result { } pub fn event_to_cover_letter(event: &nostr::Event) -> Result { - if !event_is_patch_set_root(event) { + if !event.kind.eq(&KIND_PULL_REQUEST) && !event_is_patch_set_root(event) { bail!("event is not a patch set root event (root patch or cover letter)") } - let title = commit_msg_from_patch_oneliner(event)?; - let full = commit_msg_from_patch(event)?; - let description = full[title.len()..].trim().to_string(); + let title = if event.kind.eq(&KIND_PULL_REQUEST) { + tag_value(event, "subject").unwrap_or("untitled".to_owned()) + } else { + commit_msg_from_patch_oneliner(event)? + }; + let description = if event.kind.eq(&KIND_PULL_REQUEST) { + event.content.clone() + } else { + commit_msg_from_patch(event)?[title.len()..] + .trim() + .to_string() + }; Ok(CoverLetter { title: title.clone(), @@ -572,25 +586,25 @@ fn safe_branch_name_for_pr(s: &str) -> String { .collect() } -pub fn get_most_recent_patch_with_ancestors( - mut patches: Vec, +pub fn get_pr_tip_event_or_most_recent_patch_with_ancestors( + mut proposal_events: Vec, ) -> Result> { - patches.sort_by_key(|e| e.created_at); + proposal_events.sort_by_key(|e| e.created_at); - let youngest_patch = patches.last().context("no patches found")?; + let youngest = proposal_events.last().context("no proposal events found")?; - let patches_with_youngest_created_at: Vec<&nostr::Event> = patches + let events_with_youngest_created_at: Vec<&nostr::Event> = proposal_events .iter() - .filter(|p| p.created_at.eq(&youngest_patch.created_at)) + .filter(|p| p.created_at.eq(&youngest.created_at)) .collect(); let mut res = vec![]; - let mut event_id_to_search = patches_with_youngest_created_at + let mut event_id_to_search = events_with_youngest_created_at .clone() .iter() .find(|p| { - !patches_with_youngest_created_at.iter().any(|p2| { + !events_with_youngest_created_at.iter().any(|p2| { if let Ok(reply_to) = get_event_parent_id(p2) { reply_to.eq(&p.id.to_string()) } else { @@ -598,16 +612,18 @@ pub fn get_most_recent_patch_with_ancestors( } }) }) - .context("failed to find patches_with_youngest_created_at")? + .context("failed to find events_with_youngest_created_at")? .id .to_string(); - while let Some(event) = patches + while let Some(event) = proposal_events .iter() .find(|e| e.id.to_string().eq(&event_id_to_search)) { res.push(event.clone()); - if event_is_patch_set_root(event) { + if [KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE].contains(&event.kind) + || event_is_patch_set_root(event) + { break; } event_id_to_search = get_event_parent_id(event).unwrap_or_default(); -- cgit v1.2.3