From 4cbba068d1ddbd14ba41d75def3ae266989aced8 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Tue, 17 Feb 2026 12:44:13 +0000 Subject: fix: apply push-options to patch series generation The title and description push-options were previously only applied to PR creation but not to patch series generation. When force-pushing to update an existing patch series, the custom title and description were ignored. This change passes the title_description parameter to the patch generation function, allowing users to customize patch series cover letters via: git push --force --push-option=title="Title" \ --push-option=description="Description" origin branch Includes integration test for force-pushing patches with custom cover letter. --- src/bin/git_remote_nostr/push.rs | 2 +- tests/git_remote_nostr/push.rs | 119 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs index f20c264..43abc8e 100644 --- a/src/bin/git_remote_nostr/push.rs +++ b/src/bin/git_remote_nostr/push.rs @@ -524,7 +524,7 @@ async fn generate_patches_or_pr_event_or_pr_updates( )) } else { generate_cover_letter_and_patch_events( - None, + title_description.cloned(), git_repo, ahead, signer, diff --git a/tests/git_remote_nostr/push.rs b/tests/git_remote_nostr/push.rs index 8498958..6467dec 100644 --- a/tests/git_remote_nostr/push.rs +++ b/tests/git_remote_nostr/push.rs @@ -2003,6 +2003,125 @@ async fn push_with_escaped_newlines_in_description_creates_pr_with_multiline_des Ok(()) } +#[tokio::test] +#[serial] +async fn force_push_to_existing_patch_series_with_title_description_options_creates_patches_with_custom_cover_letter() +-> Result<()> { + let (events, source_git_repo) = prep_source_repo_and_events_including_proposals().await?; + let _source_path = source_git_repo.dir.to_str().unwrap().to_string(); + + let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = ( + Relay::new(8051, None, None), + Relay::new(8052, None, None), + Relay::new(8053, None, None), + Relay::new(8055, None, None), + Relay::new(8056, None, None), + Relay::new(8057, None, None), + ); + r51.events = events.clone(); + r55.events = events.clone(); + + #[allow(clippy::mutable_key_type)] + let before = r55.events.iter().cloned().collect::>(); + + let cli_tester_handle = std::thread::spawn(move || -> Result { + let branch_name = get_proposal_branch_name_from_events(&events, FEATURE_BRANCH_NAME_1)?; + + let git_repo = clone_git_repo_with_nostr_url()?; + git_repo.checkout_remote_branch(&branch_name)?; + + // Create two new commits to replace the existing patch series + std::fs::write(git_repo.dir.join("new1.txt"), "content 1")?; + git_repo.stage_and_commit("add new1")?; + + std::fs::write(git_repo.dir.join("new2.txt"), "content 2")?; + git_repo.stage_and_commit("add new2")?; + + // Force push with custom title and description + let mut p = CliTester::new_git_with_remote_helper_from_dir( + &git_repo.dir, + [ + "push", + "--force", + "--push-option=title=Custom Patch Title", + "--push-option=description=Custom patch series description", + "origin", + &branch_name, + ], + ); + cli_expect_nostr_fetch(&mut p)?; + p.expect("git servers: listing refs...\r\n")?; + p.expect_eventually_and_print(format!("To {}\r\n", get_nostr_remote_url()?).as_str())?; + let output = p.expect_end_eventually()?; + + for p in [51, 52, 53, 55, 56, 57] { + relay::shutdown_relay(8000 + p)?; + } + + Ok(output) + }); + let _ = join!( + r51.listen_until_close(), + r52.listen_until_close(), + r53.listen_until_close(), + r55.listen_until_close(), + r56.listen_until_close(), + r57.listen_until_close(), + ); + + let output = cli_tester_handle.join().unwrap()?; + + // Verify the output shows the branch was pushed + assert!(!output.is_empty(), "should have output from push"); + + let new_events = r55 + .events + .iter() + .cloned() + .collect::>() + .difference(&before) + .cloned() + .collect::>(); + + // Should create 5 events: 1 cover letter + 4 patches (2 existing + 2 new) + assert_eq!( + new_events.len(), + 5, + "should create 1 cover letter + 4 patch events" + ); + + // Find the cover letter + let cover_letter = new_events + .iter() + .find(|e| e.kind.eq(&Kind::GitPatch) && e.content.contains("[PATCH 0/4]")) + .expect("should have a cover letter event"); + + // Check that the cover letter contains the custom title and description + assert!( + cover_letter.content.contains("Custom Patch Title"), + "cover letter should contain custom title" + ); + + assert!( + cover_letter + .content + .contains("Custom patch series description"), + "cover letter content should contain custom description" + ); + + // Verify patches exist + let patches: Vec<&Event> = new_events + .iter() + .filter(|e| { + e.kind.eq(&Kind::GitPatch) && !e.content.contains("[PATCH 0/4]") // Exclude cover letter + }) + .collect(); + + assert_eq!(patches.len(), 4, "should have 4 patch events"); + + Ok(()) +} + mod push_from_another_maintainer { // TODO that has issued announcement -- cgit v1.2.3