From e130b4dc06ca02a035519e1ea86ffeafc788866f Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Thu, 4 Jul 2024 11:37:04 +0100 Subject: [PATCH] radicle: custom upstream remote for patches When creating a new upstream for the patch workflow, the remote name is always assumed to be `rad`. It's possible for users to use another remote name but still use a `rad://` URL for pushing. To allow for this, the function that creates the upstream entry now takes a remote name. In the remote helper, `rad patch checkout`, and `rad patch set` the remote can be optionally specified and fall back to the `rad` remote name as a default. Signed-off-by: Fintan Halpenny X-Clacks-Overhead: GNU Terry Pratchett --- radicle-cli/src/commands/patch.rs | 30 ++++++++++++++++++++-- radicle-cli/src/commands/patch/checkout.rs | 9 ++++++- radicle-remote-helper/src/lib.rs | 10 +++++--- radicle-remote-helper/src/push.rs | 5 +++- radicle/src/rad.rs | 8 ++---- 5 files changed, 48 insertions(+), 14 deletions(-) diff --git a/radicle-cli/src/commands/patch.rs b/radicle-cli/src/commands/patch.rs index ce38d921..bf54a7e3 100644 --- a/radicle-cli/src/commands/patch.rs +++ b/radicle-cli/src/commands/patch.rs @@ -38,6 +38,7 @@ use anyhow::anyhow; use radicle::cob::patch::PatchId; use radicle::cob::{patch, Label}; +use radicle::git::RefString; use radicle::patch::cache::Patches as _; use radicle::storage::git::transport; use radicle::{prelude::*, Node}; @@ -155,8 +156,13 @@ Checkout options --revision Checkout the given revision of the patch --name Provide a name for the branch to checkout + --remote Provide the git remote to use as the upstream -f, --force Checkout the head of the revision, even if the branch already exists +Set options + + --remote Provide the git remote to use as the upstream + Other options --repo Operate on the given repository (default: cwd) @@ -269,6 +275,7 @@ pub enum Operation { }, Set { patch_id: Rev, + remote: Option, }, Cache { patch_id: Option, @@ -332,6 +339,7 @@ impl Args for Options { let mut undo = false; let mut reply_to: Option = None; let mut checkout_opts = checkout::Options::default(); + let mut remote: Option = None; let mut assign_opts = AssignOptions::default(); let mut label_opts = LabelOptions::default(); let mut review_op = review::Operation::default(); @@ -495,6 +503,11 @@ impl Args for Options { checkout_opts.name = Some(term::args::refstring("name", val)?); } + Long("remote") if op == Some(OperationName::Checkout) => { + let val = parser.value()?; + checkout_opts.remote = Some(term::args::refstring("remote", val)?); + } + // Assign options. Short('a') | Long("add") if matches!(op, Some(OperationName::Assign)) => { assign_opts.add.insert(term::args::did(&parser.value()?)?); @@ -523,6 +536,12 @@ impl Args for Options { label_opts.delete.insert(label); } + // Set options. + Long("remote") if op == Some(OperationName::Set) => { + let val = parser.value()?; + remote = Some(term::args::refstring("remote", val)?); + } + // List options. Long("all") => { filter = None; @@ -690,6 +709,7 @@ impl Args for Options { }, OperationName::Set => Operation::Set { patch_id: patch_id.ok_or_else(|| anyhow!("a patch must be provided"))?, + remote, }, OperationName::Cache => Operation::Cache { patch_id }, }; @@ -897,7 +917,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { let patch_id = patch_id.resolve(&repository.backend)?; label::run(&patch_id, add, delete, &profile, &repository)?; } - Operation::Set { patch_id } => { + Operation::Set { patch_id, remote } => { let patches = profile.patches(&repository)?; let patch_id = patch_id.resolve(&repository.backend)?; let patch = patches @@ -906,7 +926,13 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { let workdir = workdir.ok_or(anyhow!( "this command must be run from a repository checkout" ))?; - radicle::rad::setup_patch_upstream(&patch_id, *patch.head(), &workdir, true)?; + radicle::rad::setup_patch_upstream( + &patch_id, + *patch.head(), + &workdir, + remote.as_ref().unwrap_or(&radicle::rad::REMOTE_NAME), + true, + )?; } Operation::Cache { patch_id } => { let patch_id = patch_id diff --git a/radicle-cli/src/commands/patch/checkout.rs b/radicle-cli/src/commands/patch/checkout.rs index 748f6b17..f5f77671 100644 --- a/radicle-cli/src/commands/patch/checkout.rs +++ b/radicle-cli/src/commands/patch/checkout.rs @@ -14,6 +14,7 @@ use crate::terminal as term; #[derive(Debug, Default)] pub struct Options { pub name: Option, + pub remote: Option, pub force: bool, } @@ -97,7 +98,13 @@ pub fn run( )); spinner.finish(); - if let Some(branch) = rad::setup_patch_upstream(patch_id, revision.head(), working, false)? { + if let Some(branch) = rad::setup_patch_upstream( + patch_id, + revision.head(), + working, + opts.remote.as_ref().unwrap_or(&radicle::rad::REMOTE_NAME), + false, + )? { let tracking = branch .name()? .ok_or_else(|| anyhow!("failed to create tracking branch: invalid name"))?; diff --git a/radicle-remote-helper/src/lib.rs b/radicle-remote-helper/src/lib.rs index 165a27e1..62748fb6 100644 --- a/radicle-remote-helper/src/lib.rs +++ b/radicle-remote-helper/src/lib.rs @@ -90,18 +90,18 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> { // module is aware of that. cli::Paint::set_terminal(io::stderr()); - let url: Url = { + let (remote, url): (Option, Url) = { let args = env::args().skip(1).take(2).collect::>(); match args.as_slice() { - [url] => url.parse(), - [_, url] => url.parse(), + [url] => (None, url.parse()?), + [remote, url] => (git::RefString::try_from(remote.as_str()).ok(), url.parse()?), _ => { return Err(Error::InvalidArguments(args)); } } - }?; + }; let stored = profile.storage.repository_mut(url.repo)?; if stored.is_empty()? { @@ -174,6 +174,8 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> { return push::run( vec![refspec.to_string()], &working, + // N.b. assume the default remote if there was no remote + remote.unwrap_or((*radicle::rad::REMOTE_NAME).clone()), url, &stored, &profile, diff --git a/radicle-remote-helper/src/push.rs b/radicle-remote-helper/src/push.rs index d479ce33..6bc65723 100644 --- a/radicle-remote-helper/src/push.rs +++ b/radicle-remote-helper/src/push.rs @@ -156,6 +156,7 @@ impl FromStr for Command { pub fn run( mut specs: Vec, working: &Path, + remote: git::RefString, url: Url, stored: &storage::git::Repository, profile: &Profile, @@ -222,6 +223,7 @@ pub fn run( if dst == &*rad::PATCHES_REFNAME { patch_open( src, + &remote, &nid, &working, stored, @@ -376,6 +378,7 @@ pub fn run( /// Open a new patch. fn patch_open( src: &git::RefStr, + upstream: &git::RefString, nid: &NodeId, working: &git::raw::Repository, stored: &storage::git::Repository, @@ -463,7 +466,7 @@ fn patch_open( // Setup current branch so that pushing updates the patch. if let Some(branch) = - rad::setup_patch_upstream(&patch, commit.id().into(), working, false)? + rad::setup_patch_upstream(&patch, commit.id().into(), working, upstream, false)? { if let Some(name) = branch.name()? { if profile.hints() { diff --git a/radicle/src/rad.rs b/radicle/src/rad.rs index 90731906..07917a74 100644 --- a/radicle/src/rad.rs +++ b/radicle/src/rad.rs @@ -341,6 +341,7 @@ pub fn setup_patch_upstream<'a>( patch: &ObjectId, patch_head: git::Oid, working: &'a git::raw::Repository, + remote: &git::RefString, force: bool, ) -> Result>, git::ext::Error> { let head = working.head()?; @@ -377,12 +378,7 @@ pub fn setup_patch_upstream<'a>( if let Some(name) = name { if force || branch.upstream().is_err() { - git::set_upstream( - working, - &*REMOTE_NAME, - name.as_str(), - git::refs::patch(patch), - )?; + git::set_upstream(working, remote, name.as_str(), git::refs::patch(patch))?; } } Ok(Some(git::raw::Branch::wrap(remote_branch)))