diff options
Diffstat (limited to 'src/bin')
| -rw-r--r-- | src/bin/ngit/cli.rs | 8 | ||||
| -rw-r--r-- | src/bin/ngit/main.rs | 1 | ||||
| -rw-r--r-- | src/bin/ngit/sub_commands/apply.rs | 122 | ||||
| -rw-r--r-- | src/bin/ngit/sub_commands/mod.rs | 1 |
4 files changed, 132 insertions, 0 deletions
diff --git a/src/bin/ngit/cli.rs b/src/bin/ngit/cli.rs index 5c1a097..8f55274 100644 --- a/src/bin/ngit/cli.rs +++ b/src/bin/ngit/cli.rs | |||
| @@ -111,6 +111,14 @@ pub enum Commands { | |||
| 111 | /// Proposal event-id (hex) or nevent (bech32) | 111 | /// Proposal event-id (hex) or nevent (bech32) |
| 112 | id: String, | 112 | id: String, |
| 113 | }, | 113 | }, |
| 114 | /// apply proposal patches to current branch | ||
| 115 | Apply { | ||
| 116 | /// Proposal event-id or nevent | ||
| 117 | id: String, | ||
| 118 | /// Output patches to stdout instead of applying | ||
| 119 | #[arg(long)] | ||
| 120 | stdout: bool, | ||
| 121 | }, | ||
| 114 | /// update repo git servers to reflect nostr state (add, update or delete | 122 | /// update repo git servers to reflect nostr state (add, update or delete |
| 115 | /// remote refs) | 123 | /// remote refs) |
| 116 | Sync(sub_commands::sync::SubCommandArgs), | 124 | Sync(sub_commands::sync::SubCommandArgs), |
diff --git a/src/bin/ngit/main.rs b/src/bin/ngit/main.rs index 2c9e10f..ab57f89 100644 --- a/src/bin/ngit/main.rs +++ b/src/bin/ngit/main.rs | |||
| @@ -54,6 +54,7 @@ async fn main() { | |||
| 54 | Commands::Send(args) => sub_commands::send::launch(&cli, args, false).await, | 54 | Commands::Send(args) => sub_commands::send::launch(&cli, args, false).await, |
| 55 | Commands::Sync(args) => sub_commands::sync::launch(args).await, | 55 | Commands::Sync(args) => sub_commands::sync::launch(args).await, |
| 56 | Commands::Checkout { id } => sub_commands::checkout::launch(id).await, | 56 | Commands::Checkout { id } => sub_commands::checkout::launch(id).await, |
| 57 | Commands::Apply { id, stdout } => sub_commands::apply::launch(id, *stdout).await, | ||
| 57 | } | 58 | } |
| 58 | } else { | 59 | } else { |
| 59 | // Handle the case where no command is provided | 60 | // Handle the case where no command is provided |
diff --git a/src/bin/ngit/sub_commands/apply.rs b/src/bin/ngit/sub_commands/apply.rs new file mode 100644 index 0000000..d32cd4f --- /dev/null +++ b/src/bin/ngit/sub_commands/apply.rs | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | use std::io::Write; | ||
| 2 | |||
| 3 | use anyhow::{Context, Result, bail}; | ||
| 4 | use ngit::client::get_all_proposal_patch_pr_pr_update_events_from_cache; | ||
| 5 | use ngit::git_events::get_pr_tip_event_or_most_recent_patch_with_ancestors; | ||
| 6 | use nostr::nips::nip19::Nip19; | ||
| 7 | use nostr_sdk::{EventId, FromBech32}; | ||
| 8 | |||
| 9 | use crate::client::{Client, Connect, get_repo_ref_from_cache}; | ||
| 10 | use crate::git::{Repo, RepoActions}; | ||
| 11 | use crate::repo_ref::get_repo_coordinates_when_remote_unknown; | ||
| 12 | |||
| 13 | pub async fn launch(id: &str, stdout: bool) -> Result<()> { | ||
| 14 | let event_id = parse_event_id(id)?; | ||
| 15 | |||
| 16 | let git_repo = Repo::discover().context("failed to find a git repository")?; | ||
| 17 | let git_repo_path = git_repo.get_path()?; | ||
| 18 | |||
| 19 | let client = Client::new(ngit::client::Params::with_git_config_relay_defaults(&Some( | ||
| 20 | &git_repo, | ||
| 21 | ))); | ||
| 22 | |||
| 23 | let repo_coordinates = get_repo_coordinates_when_remote_unknown(&git_repo, &client).await?; | ||
| 24 | |||
| 25 | crate::client::fetching_with_report(git_repo_path, &client, &repo_coordinates).await?; | ||
| 26 | |||
| 27 | let repo_ref = get_repo_ref_from_cache(Some(git_repo_path), &repo_coordinates).await?; | ||
| 28 | |||
| 29 | let proposals_and_revisions: Vec<nostr::Event> = | ||
| 30 | ngit::client::get_proposals_and_revisions_from_cache( | ||
| 31 | git_repo_path, | ||
| 32 | repo_ref.coordinates(), | ||
| 33 | ) | ||
| 34 | .await?; | ||
| 35 | |||
| 36 | let proposal = proposals_and_revisions | ||
| 37 | .iter() | ||
| 38 | .find(|e| e.id == event_id) | ||
| 39 | .context(format!( | ||
| 40 | "proposal with id {} not found in cache", | ||
| 41 | event_id.to_hex() | ||
| 42 | ))?; | ||
| 43 | |||
| 44 | let commits_events: Vec<nostr::Event> = get_all_proposal_patch_pr_pr_update_events_from_cache( | ||
| 45 | git_repo_path, | ||
| 46 | &repo_ref, | ||
| 47 | &proposal.id, | ||
| 48 | ) | ||
| 49 | .await?; | ||
| 50 | |||
| 51 | let patches = get_pr_tip_event_or_most_recent_patch_with_ancestors(commits_events.clone()) | ||
| 52 | .context("failed to find any PR or patch events on this proposal")?; | ||
| 53 | |||
| 54 | if patches | ||
| 55 | .iter() | ||
| 56 | .any(|e| [ngit::git_events::KIND_PULL_REQUEST, ngit::git_events::KIND_PULL_REQUEST_UPDATE].contains(&e.kind)) | ||
| 57 | { | ||
| 58 | bail!( | ||
| 59 | "this proposal uses PR format (not patches). Use `ngit checkout {}` instead.", | ||
| 60 | event_id.to_hex() | ||
| 61 | ); | ||
| 62 | } | ||
| 63 | |||
| 64 | if stdout { | ||
| 65 | output_patches_to_stdout(patches); | ||
| 66 | } else { | ||
| 67 | launch_git_am_with_patches(patches)?; | ||
| 68 | } | ||
| 69 | |||
| 70 | Ok(()) | ||
| 71 | } | ||
| 72 | |||
| 73 | fn parse_event_id(id: &str) -> Result<EventId> { | ||
| 74 | if let Ok(nip19) = Nip19::from_bech32(id) { | ||
| 75 | match nip19 { | ||
| 76 | Nip19::Event(e) => return Ok(e.event_id), | ||
| 77 | Nip19::EventId(event_id) => return Ok(event_id), | ||
| 78 | _ => {} | ||
| 79 | } | ||
| 80 | } | ||
| 81 | if let Ok(event_id) = EventId::from_hex(id) { | ||
| 82 | return Ok(event_id); | ||
| 83 | } | ||
| 84 | bail!("invalid event-id or nevent: {id}") | ||
| 85 | } | ||
| 86 | |||
| 87 | fn output_patches_to_stdout(mut patches: Vec<nostr::Event>) { | ||
| 88 | patches.reverse(); | ||
| 89 | for patch in patches { | ||
| 90 | print!("{}\n\n", patch.content); | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | fn launch_git_am_with_patches(mut patches: Vec<nostr::Event>) -> Result<()> { | ||
| 95 | println!("applying to current branch with `git am`"); | ||
| 96 | patches.reverse(); | ||
| 97 | |||
| 98 | let mut am = std::process::Command::new("git") | ||
| 99 | .arg("am") | ||
| 100 | .stdin(std::process::Stdio::piped()) | ||
| 101 | .stdout(std::process::Stdio::inherit()) | ||
| 102 | .stderr(std::process::Stdio::inherit()) | ||
| 103 | .spawn() | ||
| 104 | .context("failed to spawn git am")?; | ||
| 105 | |||
| 106 | let stdin = am | ||
| 107 | .stdin | ||
| 108 | .as_mut() | ||
| 109 | .context("git am process failed to take stdin")?; | ||
| 110 | |||
| 111 | for patch in patches { | ||
| 112 | stdin | ||
| 113 | .write(format!("{}\n\n", patch.content).as_bytes()) | ||
| 114 | .context("failed to write patch content into git am stdin buffer")?; | ||
| 115 | } | ||
| 116 | stdin.flush()?; | ||
| 117 | let output = am | ||
| 118 | .wait_with_output() | ||
| 119 | .context("failed to read git am stdout")?; | ||
| 120 | print!("{:?}", output.stdout); | ||
| 121 | Ok(()) | ||
| 122 | } | ||
diff --git a/src/bin/ngit/sub_commands/mod.rs b/src/bin/ngit/sub_commands/mod.rs index e9f91db..6b94248 100644 --- a/src/bin/ngit/sub_commands/mod.rs +++ b/src/bin/ngit/sub_commands/mod.rs | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | pub mod apply; | ||
| 1 | pub mod checkout; | 2 | pub mod checkout; |
| 2 | pub mod create; | 3 | pub mod create; |
| 3 | pub mod export_keys; | 4 | pub mod export_keys; |