Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
radicle: custom upstream remote for patches
Merged fintohaps opened 1 year ago

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.

5 files changed +48 -14 00639182 e130b4dc
modified 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 <id>        Checkout the given revision of the patch
        --name <string>        Provide a name for the branch to checkout
+
        --remote <string>      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 <string>      Provide the git remote to use as the upstream
+

Other options

        --repo <rid>           Operate on the given repository (default: cwd)
@@ -269,6 +275,7 @@ pub enum Operation {
    },
    Set {
        patch_id: Rev,
+
        remote: Option<RefString>,
    },
    Cache {
        patch_id: Option<Rev>,
@@ -332,6 +339,7 @@ impl Args for Options {
        let mut undo = false;
        let mut reply_to: Option<Rev> = None;
        let mut checkout_opts = checkout::Options::default();
+
        let mut remote: Option<RefString> = 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
modified 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<RefString>,
+
    pub remote: Option<RefString>,
    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"))?;
modified 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<git::RefString>, Url) = {
        let args = env::args().skip(1).take(2).collect::<Vec<_>>();

        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,
modified radicle-remote-helper/src/push.rs
@@ -156,6 +156,7 @@ impl FromStr for Command {
pub fn run(
    mut specs: Vec<String>,
    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<G: Signer>(
    src: &git::RefStr,
+
    upstream: &git::RefString,
    nid: &NodeId,
    working: &git::raw::Repository,
    stored: &storage::git::Repository,
@@ -463,7 +466,7 @@ fn patch_open<G: Signer>(

            // 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() {
modified radicle/src/rad.rs
@@ -339,6 +339,7 @@ pub fn setup_patch_upstream<'a>(
    patch: &ObjectId,
    patch_head: git::Oid,
    working: &'a git::raw::Repository,
+
    remote: &git::RefString,
    force: bool,
) -> Result<Option<git::raw::Branch<'a>>, git::ext::Error> {
    let head = working.head()?;
@@ -370,12 +371,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)))