upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/sub_commands/list.rs171
-rw-r--r--src/sub_commands/push.rs35
-rw-r--r--src/sub_commands/send.rs5
3 files changed, 135 insertions, 76 deletions
diff --git a/src/sub_commands/list.rs b/src/sub_commands/list.rs
index b556d5a..88ad52f 100644
--- a/src/sub_commands/list.rs
+++ b/src/sub_commands/list.rs
@@ -13,8 +13,8 @@ use crate::{
13 git::{str_to_sha1, Repo, RepoActions}, 13 git::{str_to_sha1, Repo, RepoActions},
14 repo_ref::{self, RepoRef, REPO_REF_KIND}, 14 repo_ref::{self, RepoRef, REPO_REF_KIND},
15 sub_commands::send::{ 15 sub_commands::send::{
16 commit_msg_from_patch_oneliner, event_is_cover_letter, event_to_cover_letter, 16 commit_msg_from_patch_oneliner, event_is_cover_letter, event_is_revision_root,
17 patch_supports_commit_ids, PATCH_KIND, 17 event_to_cover_letter, patch_supports_commit_ids, PATCH_KIND,
18 }, 18 },
19 Cli, 19 Cli,
20}; 20};
@@ -53,13 +53,17 @@ pub async fn launch(_cli_args: &Cli, _args: &SubCommandArgs) -> Result<()> {
53 53
54 println!("finding proposals..."); 54 println!("finding proposals...");
55 55
56 let proposal_events: Vec<nostr::Event> = 56 let proposal_events_and_revisions: Vec<nostr::Event> =
57 find_proposal_events(&client, &repo_ref, &root_commit.to_string()).await?; 57 find_proposal_events(&client, &repo_ref, &root_commit.to_string()).await?;
58 58
59 if proposal_events.is_empty() { 59 if proposal_events_and_revisions.is_empty() {
60 println!("no proposals found... create one? try `ngit send`"); 60 println!("no proposals found... create one? try `ngit send`");
61 return Ok(()); 61 return Ok(());
62 } 62 }
63 let proposal_events: Vec<&nostr::Event> = proposal_events_and_revisions
64 .iter()
65 .filter(|e| !event_is_revision_root(e))
66 .collect::<Vec<&nostr::Event>>();
63 67
64 loop { 68 loop {
65 let selected_index = Interactor::default().choice( 69 let selected_index = Interactor::default().choice(
@@ -81,17 +85,32 @@ pub async fn launch(_cli_args: &Cli, _args: &SubCommandArgs) -> Result<()> {
81 ), 85 ),
82 )?; 86 )?;
83 87
84 let cover_letter = event_to_cover_letter(&proposal_events[selected_index]) 88 let cover_letter = event_to_cover_letter(proposal_events[selected_index])
85 .context("cannot extract proposal details from proposal root event")?; 89 .context("cannot extract proposal details from proposal root event")?;
86 90
87 println!("finding commits..."); 91 println!("finding commits...");
88 92
89 let commits_events: Vec<nostr::Event> = find_commits_for_proposal_root_event( 93 let commits_events: Vec<nostr::Event> = find_commits_for_proposal_root_events(
90 &client, 94 &client,
91 &proposal_events[selected_index], 95 &[
96 vec![proposal_events[selected_index]],
97 proposal_events_and_revisions
98 .iter()
99 .filter(|e| {
100 e.tags.iter().any(|t| {
101 t.as_vec().len().gt(&1)
102 && t.as_vec()[1].eq(&proposal_events[selected_index].id.to_string())
103 })
104 })
105 .collect::<Vec<&nostr::Event>>(),
106 ]
107 .concat(),
92 &repo_ref, 108 &repo_ref,
93 ) 109 )
94 .await?; 110 .await?;
111 // for commit in &commits_events {
112 // println!("cevent: {:?}", commit.as_json());
113 // }
95 114
96 let Ok(most_recent_proposal_patch_chain) = 115 let Ok(most_recent_proposal_patch_chain) =
97 get_most_recent_patch_with_ancestors(commits_events.clone()) 116 get_most_recent_patch_with_ancestors(commits_events.clone())
@@ -107,6 +126,9 @@ pub async fn launch(_cli_args: &Cli, _args: &SubCommandArgs) -> Result<()> {
107 } 126 }
108 return Ok(()); 127 return Ok(());
109 }; 128 };
129 // for commit in &most_recent_proposal_patch_chain {
130 // println!("recent_event: {:?}", commit.as_json());
131 // }
110 132
111 let binding_patch_text_ref = format!("{} commits", most_recent_proposal_patch_chain.len()); 133 let binding_patch_text_ref = format!("{} commits", most_recent_proposal_patch_chain.len());
112 let patch_text_ref = if most_recent_proposal_patch_chain.len().gt(&1) { 134 let patch_text_ref = if most_recent_proposal_patch_chain.len().gt(&1) {
@@ -354,59 +376,63 @@ pub async fn launch(_cli_args: &Cli, _args: &SubCommandArgs) -> Result<()> {
354 .unwrap_or_default() 376 .unwrap_or_default()
355 .eq(&local_branch_tip.to_string()) 377 .eq(&local_branch_tip.to_string())
356 }) { 378 }) {
379 println!(
380 "updated proposal available ({} ahead {} behind '{main_branch_name}'). existing version is {} ahead {} behind '{main_branch_name}'",
381 most_recent_proposal_patch_chain.len(),
382 proposal_behind_main.len(),
383 local_ahead_of_main.len(),
384 local_beind_main.len(),
385 );
357 return match Interactor::default().choice( 386 return match Interactor::default().choice(
358 PromptChoiceParms::default().with_default(0) 387 PromptChoiceParms::default()
359 .with_choices( 388 .with_default(0)
360 vec![ 389 .with_choices(vec![
361 format!( 390 format!("checkout and overwrite existing proposal branch"),
362 "checkout new version of proposal branch ({} ahead {} behind '{main_branch_name}'), replacing old version ({} ahead {} behind '{main_branch_name}')", 391 format!("checkout existing outdated proposal branch"),
363 most_recent_proposal_patch_chain.len(), 392 format!("apply to current branch with `git am`"),
364 proposal_behind_main.len(), 393 format!("download to ./patches"),
365 local_ahead_of_main.len(), 394 "back".to_string(),
366 local_beind_main.len(), 395 ]),
367 ),
368 format!(
369 "checkout existing outdated proposal branch ({} ahead {} behind '{main_branch_name}')",
370 local_ahead_of_main.len(),
371 local_beind_main.len(),
372 ),
373 format!("apply to current branch with `git am`"),
374 format!("download to ./patches"),
375 "back".to_string(),
376 ],
377 )
378 )? { 396 )? {
379 0 => { 397 0 => {
380 check_clean(&git_repo)?; 398 check_clean(&git_repo)?;
381 git_repo.create_branch_at_commit(&cover_letter.branch_name, &proposal_base_commit.to_string())?; 399 git_repo.create_branch_at_commit(
382 git_repo.checkout(&cover_letter.branch_name)?; 400 &cover_letter.branch_name,
383 let chain_length = most_recent_proposal_patch_chain.len(); 401 &proposal_base_commit.to_string(),
384 let _ = git_repo 402 )?;
385 .apply_patch_chain(&cover_letter.branch_name, most_recent_proposal_patch_chain) 403 git_repo.checkout(&cover_letter.branch_name)?;
386 .context("cannot apply patch chain")?; 404 let chain_length = most_recent_proposal_patch_chain.len();
387 format!( 405 let _ = git_repo
388 "checked out new version of proposal ({} ahead {} behind '{main_branch_name}'), replacing old version ({} ahead {} behind '{main_branch_name}')", 406 .apply_patch_chain(
389 chain_length, 407 &cover_letter.branch_name,
390 proposal_behind_main.len(), 408 most_recent_proposal_patch_chain,
391 local_ahead_of_main.len(), 409 )
392 local_beind_main.len(), 410 .context("cannot apply patch chain")?;
393 ); 411 println!(
394 Ok(()) 412 "checked out new version of proposal ({} ahead {} behind '{main_branch_name}'), replacing old version ({} ahead {} behind '{main_branch_name}')",
395 }, 413 chain_length,
396 1 => { 414 proposal_behind_main.len(),
397 check_clean(&git_repo)?; 415 local_ahead_of_main.len(),
398 git_repo.checkout(&cover_letter.branch_name)?; 416 local_beind_main.len(),
399 format!( 417 );
400 "checked out old proposal in existing branch ({} ahead {} behind '{main_branch_name}')", 418 Ok(())
401 local_ahead_of_main.len(), 419 }
402 local_beind_main.len(), 420 1 => {
403 ); 421 check_clean(&git_repo)?;
404 Ok(()) 422 git_repo.checkout(&cover_letter.branch_name)?;
405 }, 423 println!(
406 2 => {launch_git_am_with_patches(most_recent_proposal_patch_chain)}, 424 "checked out old proposal in existing branch ({} ahead {} behind '{main_branch_name}')",
407 3 => {save_patches_to_dir(most_recent_proposal_patch_chain, &git_repo)}, 425 local_ahead_of_main.len(),
408 4 => { continue }, 426 local_beind_main.len(),
409 _ => { bail!("unexpected choice")} 427 );
428 Ok(())
429 }
430 2 => launch_git_am_with_patches(most_recent_proposal_patch_chain),
431 3 => save_patches_to_dir(most_recent_proposal_patch_chain, &git_repo),
432 4 => continue,
433 _ => {
434 bail!("unexpected choice")
435 }
410 }; 436 };
411 } 437 }
412 438
@@ -699,11 +725,11 @@ pub fn get_most_recent_patch_with_ancestors(
699) -> Result<Vec<nostr::Event>> { 725) -> Result<Vec<nostr::Event>> {
700 patches.sort_by_key(|e| e.created_at); 726 patches.sort_by_key(|e| e.created_at);
701 727
702 let first_patch = patches.first().context("no patches found")?; 728 let youngest_patch = patches.last().context("no patches found")?;
703 729
704 let patches_with_youngest_created_at: Vec<&nostr::Event> = patches 730 let patches_with_youngest_created_at: Vec<&nostr::Event> = patches
705 .iter() 731 .iter()
706 .filter(|p| p.created_at.eq(&first_patch.created_at)) 732 .filter(|p| p.created_at.eq(&youngest_patch.created_at))
707 .collect(); 733 .collect();
708 734
709 let mut res = vec![]; 735 let mut res = vec![];
@@ -787,10 +813,10 @@ pub async fn find_proposal_events(
787 .collect::<Vec<nostr::Event>>()) 813 .collect::<Vec<nostr::Event>>())
788} 814}
789 815
790pub async fn find_commits_for_proposal_root_event( 816pub async fn find_commits_for_proposal_root_events(
791 #[cfg(test)] client: &crate::client::MockConnect, 817 #[cfg(test)] client: &crate::client::MockConnect,
792 #[cfg(not(test))] client: &Client, 818 #[cfg(not(test))] client: &Client,
793 proposal_root_event: &nostr::Event, 819 proposal_root_events: &Vec<&nostr::Event>,
794 repo_ref: &RepoRef, 820 repo_ref: &RepoRef,
795) -> Result<Vec<nostr::Event>> { 821) -> Result<Vec<nostr::Event>> {
796 let mut patch_events: Vec<nostr::Event> = client 822 let mut patch_events: Vec<nostr::Event> = client
@@ -799,10 +825,12 @@ pub async fn find_commits_for_proposal_root_event(
799 vec![ 825 vec![
800 nostr::Filter::default() 826 nostr::Filter::default()
801 .kind(nostr::Kind::Custom(PATCH_KIND)) 827 .kind(nostr::Kind::Custom(PATCH_KIND))
802 // this requires every patch to reference the root event 828 .events(
803 // this will not pick up v2,v3 patch sets 829 proposal_root_events
804 // TODO: fetch commits for v2.. patch sets 830 .iter()
805 .event(proposal_root_event.id), 831 .map(|e| e.id)
832 .collect::<Vec<nostr::EventId>>(),
833 ),
806 ], 834 ],
807 ) 835 )
808 .await 836 .await
@@ -811,14 +839,19 @@ pub async fn find_commits_for_proposal_root_event(
811 .filter(|e| { 839 .filter(|e| {
812 e.kind.as_u64() == PATCH_KIND 840 e.kind.as_u64() == PATCH_KIND
813 && e.tags.iter().any(|t| { 841 && e.tags.iter().any(|t| {
814 t.as_vec().len() > 2 && t.as_vec()[1].eq(&proposal_root_event.id.to_string()) 842 t.as_vec().len() > 2
843 && proposal_root_events
844 .iter()
845 .any(|e| t.as_vec()[1].eq(&e.id.to_string()))
815 }) 846 })
816 }) 847 })
817 .map(std::borrow::ToOwned::to_owned) 848 .map(std::borrow::ToOwned::to_owned)
818 .collect(); 849 .collect();
819 850
820 if !event_is_cover_letter(proposal_root_event) { 851 for e in proposal_root_events {
821 patch_events.push(proposal_root_event.clone()); 852 if !event_is_cover_letter(e) && !patch_events.iter().any(|e2| e2.id.eq(&e.id)) {
853 patch_events.push(e.to_owned().clone());
854 }
822 } 855 }
823 Ok(patch_events) 856 Ok(patch_events)
824} 857}
diff --git a/src/sub_commands/push.rs b/src/sub_commands/push.rs
index 73bdb38..dd32b2c 100644
--- a/src/sub_commands/push.rs
+++ b/src/sub_commands/push.rs
@@ -12,10 +12,10 @@ use crate::{
12 repo_ref::{self, RepoRef}, 12 repo_ref::{self, RepoRef},
13 sub_commands::{ 13 sub_commands::{
14 list::{ 14 list::{
15 find_commits_for_proposal_root_event, find_proposal_events, get_commit_id_from_patch, 15 find_commits_for_proposal_root_events, find_proposal_events, get_commit_id_from_patch,
16 get_most_recent_patch_with_ancestors, tag_value, 16 get_most_recent_patch_with_ancestors, tag_value,
17 }, 17 },
18 send::{event_to_cover_letter, generate_patch_event, send_events}, 18 send::{event_is_revision_root, event_to_cover_letter, generate_patch_event, send_events},
19 }, 19 },
20 Cli, 20 Cli,
21}; 21};
@@ -149,12 +149,17 @@ pub async fn fetch_proposal_root_and_most_recent_patch_chain(
149) -> Result<(nostr::Event, Vec<nostr::Event>)> { 149) -> Result<(nostr::Event, Vec<nostr::Event>)> {
150 println!("finding proposal root event..."); 150 println!("finding proposal root event...");
151 151
152 let proposal_events: Vec<nostr::Event> = 152 let proposal_events_and_revisions: Vec<nostr::Event> =
153 find_proposal_events(client, repo_ref, &root_commit.to_string()) 153 find_proposal_events(client, repo_ref, &root_commit.to_string())
154 .await 154 .await
155 .context("cannot get proposal events for repo")?; 155 .context("cannot get proposal events for repo")?;
156 156
157 let proposal_root_event: nostr::Event = proposal_events 157 let proposal_events: Vec<&nostr::Event> = proposal_events_and_revisions
158 .iter()
159 .filter(|e| !event_is_revision_root(e))
160 .collect::<Vec<&nostr::Event>>();
161
162 let proposal_root_event: &nostr::Event = proposal_events
158 .iter() 163 .iter()
159 .find(|e| { 164 .find(|e| {
160 event_to_cover_letter(e).is_ok_and(|cl| cl.branch_name.eq(branch_name)) 165 event_to_cover_letter(e).is_ok_and(|cl| cl.branch_name.eq(branch_name))
@@ -166,8 +171,24 @@ pub async fn fetch_proposal_root_and_most_recent_patch_chain(
166 171
167 println!("found proposal root event. finding commits..."); 172 println!("found proposal root event. finding commits...");
168 173
169 let commits_events: Vec<nostr::Event> = 174 let commits_events: Vec<nostr::Event> = find_commits_for_proposal_root_events(
170 find_commits_for_proposal_root_event(client, &proposal_root_event, repo_ref).await?; 175 client,
176 &[
177 vec![proposal_root_event],
178 proposal_events_and_revisions
179 .iter()
180 .filter(|e| {
181 e.tags.iter().any(|t| {
182 t.as_vec().len().gt(&1)
183 && t.as_vec()[1].eq(&proposal_root_event.id.to_string())
184 })
185 })
186 .collect::<Vec<&nostr::Event>>(),
187 ]
188 .concat(),
189 repo_ref,
190 )
191 .await?;
171 192
172 Ok((proposal_root_event, commits_events)) 193 Ok((proposal_root_event.clone(), commits_events))
173} 194}
diff --git a/src/sub_commands/send.rs b/src/sub_commands/send.rs
index c9c81ee..1ccb1f4 100644
--- a/src/sub_commands/send.rs
+++ b/src/sub_commands/send.rs
@@ -613,6 +613,11 @@ pub fn event_is_patch_set_root(event: &nostr::Event) -> bool {
613 event.kind.as_u64().eq(&PATCH_KIND) && event.iter_tags().any(|t| t.as_vec()[1].eq("root")) 613 event.kind.as_u64().eq(&PATCH_KIND) && event.iter_tags().any(|t| t.as_vec()[1].eq("root"))
614} 614}
615 615
616pub fn event_is_revision_root(event: &nostr::Event) -> bool {
617 event.kind.as_u64().eq(&PATCH_KIND)
618 && event.iter_tags().any(|t| t.as_vec()[1].eq("revision-root"))
619}
620
616pub fn patch_supports_commit_ids(event: &nostr::Event) -> bool { 621pub fn patch_supports_commit_ids(event: &nostr::Event) -> bool {
617 event.kind.as_u64().eq(&PATCH_KIND) 622 event.kind.as_u64().eq(&PATCH_KIND)
618 && event 623 && event