diff options
Diffstat (limited to 'src/bin/ngit/sub_commands/checkout.rs')
| -rw-r--r-- | src/bin/ngit/sub_commands/checkout.rs | 102 |
1 files changed, 93 insertions, 9 deletions
diff --git a/src/bin/ngit/sub_commands/checkout.rs b/src/bin/ngit/sub_commands/checkout.rs index 0df1134..47d61ed 100644 --- a/src/bin/ngit/sub_commands/checkout.rs +++ b/src/bin/ngit/sub_commands/checkout.rs | |||
| @@ -1,4 +1,7 @@ | |||
| 1 | use std::collections::HashSet; | 1 | use std::{ |
| 2 | collections::HashSet, | ||
| 3 | process::{Command, Stdio}, | ||
| 4 | }; | ||
| 2 | 5 | ||
| 3 | use anyhow::{Context, Result, bail}; | 6 | use anyhow::{Context, Result, bail}; |
| 4 | use ngit::{ | 7 | use ngit::{ |
| @@ -33,7 +36,17 @@ pub async fn launch(id: &str) -> Result<()> { | |||
| 33 | 36 | ||
| 34 | let repo_coordinates = get_repo_coordinates_when_remote_unknown(&git_repo, &client).await?; | 37 | let repo_coordinates = get_repo_coordinates_when_remote_unknown(&git_repo, &client).await?; |
| 35 | 38 | ||
| 36 | fetching_with_report(git_repo_path, &client, &repo_coordinates).await?; | 39 | let nostr_remote = git_repo |
| 40 | .get_first_nostr_remote_when_in_ngit_binary() | ||
| 41 | .await | ||
| 42 | .ok() | ||
| 43 | .flatten(); | ||
| 44 | |||
| 45 | if let Some((remote_name, _)) = &nostr_remote { | ||
| 46 | run_git_fetch(remote_name)?; | ||
| 47 | } else { | ||
| 48 | fetching_with_report(git_repo_path, &client, &repo_coordinates).await?; | ||
| 49 | } | ||
| 37 | 50 | ||
| 38 | let repo_ref = get_repo_ref_from_cache(Some(git_repo_path), &repo_coordinates).await?; | 51 | let repo_ref = get_repo_ref_from_cache(Some(git_repo_path), &repo_coordinates).await?; |
| 39 | 52 | ||
| @@ -68,16 +81,33 @@ pub async fn launch(id: &str) -> Result<()> { | |||
| 68 | &repo_ref, | 81 | &repo_ref, |
| 69 | &cover_letter, | 82 | &cover_letter, |
| 70 | &most_recent_proposal_patch_chain_or_pr_or_pr_update, | 83 | &most_recent_proposal_patch_chain_or_pr_or_pr_update, |
| 84 | nostr_remote.as_ref().map(|(name, _)| name.as_str()), | ||
| 71 | ) | 85 | ) |
| 72 | } else { | 86 | } else { |
| 73 | checkout_patch( | 87 | checkout_patch( |
| 74 | &git_repo, | 88 | &git_repo, |
| 75 | &cover_letter, | 89 | &cover_letter, |
| 76 | &most_recent_proposal_patch_chain_or_pr_or_pr_update, | 90 | &most_recent_proposal_patch_chain_or_pr_or_pr_update, |
| 91 | nostr_remote.as_ref().map(|(name, _)| name.as_str()), | ||
| 77 | ) | 92 | ) |
| 78 | } | 93 | } |
| 79 | } | 94 | } |
| 80 | 95 | ||
| 96 | fn run_git_fetch(remote_name: &str) -> Result<()> { | ||
| 97 | println!("fetching from {remote_name}..."); | ||
| 98 | let exit_status = Command::new("git") | ||
| 99 | .args(["fetch", remote_name]) | ||
| 100 | .stdout(Stdio::inherit()) | ||
| 101 | .stderr(Stdio::inherit()) | ||
| 102 | .status() | ||
| 103 | .context("failed to run git fetch")?; | ||
| 104 | |||
| 105 | if !exit_status.success() { | ||
| 106 | bail!("git fetch {remote_name} exited with error: {exit_status}"); | ||
| 107 | } | ||
| 108 | Ok(()) | ||
| 109 | } | ||
| 110 | |||
| 81 | fn parse_event_id(id: &str) -> Result<EventId> { | 111 | fn parse_event_id(id: &str) -> Result<EventId> { |
| 82 | if let Ok(nip19) = Nip19::from_bech32(id) { | 112 | if let Ok(nip19) = Nip19::from_bech32(id) { |
| 83 | match nip19 { | 113 | match nip19 { |
| @@ -97,15 +127,15 @@ fn checkout_pr( | |||
| 97 | repo_ref: &RepoRef, | 127 | repo_ref: &RepoRef, |
| 98 | cover_letter: &crate::git_events::CoverLetter, | 128 | cover_letter: &crate::git_events::CoverLetter, |
| 99 | most_recent_proposal_patch_chain_or_pr_or_pr_update: &[nostr::Event], | 129 | most_recent_proposal_patch_chain_or_pr_or_pr_update: &[nostr::Event], |
| 130 | nostr_remote_name: Option<&str>, | ||
| 100 | ) -> Result<()> { | 131 | ) -> Result<()> { |
| 101 | let branch_name = cover_letter.get_branch_name_with_pr_prefix_and_shorthand_id()?; | 132 | let branch_name = cover_letter.get_branch_name_with_pr_prefix_and_shorthand_id()?; |
| 102 | let local_branch_tip = git_repo.get_tip_of_branch(&branch_name).ok(); | ||
| 103 | let proposal_tip_event = most_recent_proposal_patch_chain_or_pr_or_pr_update | 133 | let proposal_tip_event = most_recent_proposal_patch_chain_or_pr_or_pr_update |
| 104 | .first() | 134 | .first() |
| 105 | .context("most_recent_proposal_patch_chain_or_pr_or_pr_update will always contain an event with c tag")?; | 135 | .context("most_recent_proposal_patch_chain_or_pr_or_pr_update will always contain an event with c tag")?; |
| 106 | let proposal_tip = tag_value(proposal_tip_event, "c")?; | 136 | let proposal_tip = tag_value(proposal_tip_event, "c")?; |
| 107 | 137 | ||
| 108 | if let Some(local_branch_tip) = local_branch_tip { | 138 | if let Ok(local_branch_tip) = git_repo.get_tip_of_branch(&branch_name) { |
| 109 | git_repo | 139 | git_repo |
| 110 | .checkout(&branch_name) | 140 | .checkout(&branch_name) |
| 111 | .context("cannot checkout existing proposal branch")?; | 141 | .context("cannot checkout existing proposal branch")?; |
| @@ -121,6 +151,15 @@ fn checkout_pr( | |||
| 121 | } | 151 | } |
| 122 | } | 152 | } |
| 123 | 153 | ||
| 154 | if let Some(remote_name) = nostr_remote_name { | ||
| 155 | let remote_branch = format!("{remote_name}/{branch_name}"); | ||
| 156 | if git_repo.get_tip_of_branch(&remote_branch).is_ok() { | ||
| 157 | checkout_remote_branch_with_tracking(git_repo, remote_name, &branch_name)?; | ||
| 158 | println!("checked out proposal branch '{branch_name}' with tracking to {remote_name}"); | ||
| 159 | return Ok(()); | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 124 | fetch_oid_for_from_servers_for_pr( | 163 | fetch_oid_for_from_servers_for_pr( |
| 125 | &proposal_tip, | 164 | &proposal_tip, |
| 126 | git_repo, | 165 | git_repo, |
| @@ -129,11 +168,7 @@ fn checkout_pr( | |||
| 129 | )?; | 168 | )?; |
| 130 | git_repo.create_branch_at_commit(&branch_name, &proposal_tip)?; | 169 | git_repo.create_branch_at_commit(&branch_name, &proposal_tip)?; |
| 131 | git_repo.checkout(&branch_name)?; | 170 | git_repo.checkout(&branch_name)?; |
| 132 | if local_branch_tip.is_some() { | 171 | println!("created and checked out proposal branch '{branch_name}'"); |
| 133 | println!("checked out proposal branch and pulled updates '{branch_name}'"); | ||
| 134 | } else { | ||
| 135 | println!("created and checked out proposal branch '{branch_name}'"); | ||
| 136 | } | ||
| 137 | Ok(()) | 172 | Ok(()) |
| 138 | } | 173 | } |
| 139 | 174 | ||
| @@ -141,6 +176,7 @@ fn checkout_patch( | |||
| 141 | git_repo: &Repo, | 176 | git_repo: &Repo, |
| 142 | cover_letter: &crate::git_events::CoverLetter, | 177 | cover_letter: &crate::git_events::CoverLetter, |
| 143 | most_recent_proposal_patch_chain_or_pr_or_pr_update: &[nostr::Event], | 178 | most_recent_proposal_patch_chain_or_pr_or_pr_update: &[nostr::Event], |
| 179 | nostr_remote_name: Option<&str>, | ||
| 144 | ) -> Result<()> { | 180 | ) -> Result<()> { |
| 145 | let no_support_for_patches_as_branch = most_recent_proposal_patch_chain_or_pr_or_pr_update | 181 | let no_support_for_patches_as_branch = most_recent_proposal_patch_chain_or_pr_or_pr_update |
| 146 | .iter() | 182 | .iter() |
| @@ -184,6 +220,14 @@ fn checkout_patch( | |||
| 184 | .any(|n| n.eq(&branch_name)); | 220 | .any(|n| n.eq(&branch_name)); |
| 185 | 221 | ||
| 186 | if !branch_exists { | 222 | if !branch_exists { |
| 223 | if let Some(remote_name) = nostr_remote_name { | ||
| 224 | let remote_branch = format!("{remote_name}/{branch_name}"); | ||
| 225 | if git_repo.get_tip_of_branch(&remote_branch).is_ok() { | ||
| 226 | checkout_remote_branch_with_tracking(git_repo, remote_name, &branch_name)?; | ||
| 227 | println!("checked out proposal branch '{branch_name}' with tracking to {remote_name}"); | ||
| 228 | return Ok(()); | ||
| 229 | } | ||
| 230 | } | ||
| 187 | let _ = git_repo | 231 | let _ = git_repo |
| 188 | .apply_patch_chain(&branch_name, most_recent_proposal_patch_chain_or_pr_or_pr_update.to_vec()) | 232 | .apply_patch_chain(&branch_name, most_recent_proposal_patch_chain_or_pr_or_pr_update.to_vec()) |
| 189 | .context("failed to apply patch chain")?; | 233 | .context("failed to apply patch chain")?; |
| @@ -267,3 +311,43 @@ fn fetch_oid_for_from_servers_for_pr( | |||
| 267 | } | 311 | } |
| 268 | Ok(()) | 312 | Ok(()) |
| 269 | } | 313 | } |
| 314 | |||
| 315 | fn checkout_remote_branch_with_tracking( | ||
| 316 | git_repo: &Repo, | ||
| 317 | remote_name: &str, | ||
| 318 | branch_name: &str, | ||
| 319 | ) -> Result<()> { | ||
| 320 | let remote_branch_ref = format!("refs/remotes/{remote_name}/{branch_name}"); | ||
| 321 | let remote_branch = git_repo | ||
| 322 | .git_repo | ||
| 323 | .find_reference(&remote_branch_ref) | ||
| 324 | .context(format!("failed to find remote branch {remote_branch_ref}"))?; | ||
| 325 | let commit = remote_branch | ||
| 326 | .peel_to_commit() | ||
| 327 | .context("failed to peel remote branch to commit")?; | ||
| 328 | |||
| 329 | let mut local_branch = git_repo | ||
| 330 | .git_repo | ||
| 331 | .branch(branch_name, &commit, false) | ||
| 332 | .context("failed to create local branch")?; | ||
| 333 | |||
| 334 | local_branch | ||
| 335 | .set_upstream(Some(&format!("{remote_name}/{branch_name}"))) | ||
| 336 | .context("failed to set upstream tracking")?; | ||
| 337 | |||
| 338 | let local_branch_ref = local_branch.into_reference(); | ||
| 339 | let local_branch_ref_name = local_branch_ref | ||
| 340 | .name() | ||
| 341 | .context("failed to get local branch ref name")?; | ||
| 342 | |||
| 343 | git_repo | ||
| 344 | .git_repo | ||
| 345 | .set_head(local_branch_ref_name) | ||
| 346 | .context("failed to set head to local branch")?; | ||
| 347 | git_repo | ||
| 348 | .git_repo | ||
| 349 | .checkout_head(None) | ||
| 350 | .context("failed to checkout head")?; | ||
| 351 | |||
| 352 | Ok(()) | ||
| 353 | } | ||