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 <fintan.halpenny@gmail.com> X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
parent
00639182a7
commit
e130b4dc06
|
|
@ -38,6 +38,7 @@ use anyhow::anyhow;
|
||||||
|
|
||||||
use radicle::cob::patch::PatchId;
|
use radicle::cob::patch::PatchId;
|
||||||
use radicle::cob::{patch, Label};
|
use radicle::cob::{patch, Label};
|
||||||
|
use radicle::git::RefString;
|
||||||
use radicle::patch::cache::Patches as _;
|
use radicle::patch::cache::Patches as _;
|
||||||
use radicle::storage::git::transport;
|
use radicle::storage::git::transport;
|
||||||
use radicle::{prelude::*, Node};
|
use radicle::{prelude::*, Node};
|
||||||
|
|
@ -155,8 +156,13 @@ Checkout options
|
||||||
|
|
||||||
--revision <id> Checkout the given revision of the patch
|
--revision <id> Checkout the given revision of the patch
|
||||||
--name <string> Provide a name for the branch to checkout
|
--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
|
-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
|
Other options
|
||||||
|
|
||||||
--repo <rid> Operate on the given repository (default: cwd)
|
--repo <rid> Operate on the given repository (default: cwd)
|
||||||
|
|
@ -269,6 +275,7 @@ pub enum Operation {
|
||||||
},
|
},
|
||||||
Set {
|
Set {
|
||||||
patch_id: Rev,
|
patch_id: Rev,
|
||||||
|
remote: Option<RefString>,
|
||||||
},
|
},
|
||||||
Cache {
|
Cache {
|
||||||
patch_id: Option<Rev>,
|
patch_id: Option<Rev>,
|
||||||
|
|
@ -332,6 +339,7 @@ impl Args for Options {
|
||||||
let mut undo = false;
|
let mut undo = false;
|
||||||
let mut reply_to: Option<Rev> = None;
|
let mut reply_to: Option<Rev> = None;
|
||||||
let mut checkout_opts = checkout::Options::default();
|
let mut checkout_opts = checkout::Options::default();
|
||||||
|
let mut remote: Option<RefString> = None;
|
||||||
let mut assign_opts = AssignOptions::default();
|
let mut assign_opts = AssignOptions::default();
|
||||||
let mut label_opts = LabelOptions::default();
|
let mut label_opts = LabelOptions::default();
|
||||||
let mut review_op = review::Operation::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)?);
|
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.
|
// Assign options.
|
||||||
Short('a') | Long("add") if matches!(op, Some(OperationName::Assign)) => {
|
Short('a') | Long("add") if matches!(op, Some(OperationName::Assign)) => {
|
||||||
assign_opts.add.insert(term::args::did(&parser.value()?)?);
|
assign_opts.add.insert(term::args::did(&parser.value()?)?);
|
||||||
|
|
@ -523,6 +536,12 @@ impl Args for Options {
|
||||||
label_opts.delete.insert(label);
|
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.
|
// List options.
|
||||||
Long("all") => {
|
Long("all") => {
|
||||||
filter = None;
|
filter = None;
|
||||||
|
|
@ -690,6 +709,7 @@ impl Args for Options {
|
||||||
},
|
},
|
||||||
OperationName::Set => Operation::Set {
|
OperationName::Set => Operation::Set {
|
||||||
patch_id: patch_id.ok_or_else(|| anyhow!("a patch must be provided"))?,
|
patch_id: patch_id.ok_or_else(|| anyhow!("a patch must be provided"))?,
|
||||||
|
remote,
|
||||||
},
|
},
|
||||||
OperationName::Cache => Operation::Cache { patch_id },
|
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)?;
|
let patch_id = patch_id.resolve(&repository.backend)?;
|
||||||
label::run(&patch_id, add, delete, &profile, &repository)?;
|
label::run(&patch_id, add, delete, &profile, &repository)?;
|
||||||
}
|
}
|
||||||
Operation::Set { patch_id } => {
|
Operation::Set { patch_id, remote } => {
|
||||||
let patches = profile.patches(&repository)?;
|
let patches = profile.patches(&repository)?;
|
||||||
let patch_id = patch_id.resolve(&repository.backend)?;
|
let patch_id = patch_id.resolve(&repository.backend)?;
|
||||||
let patch = patches
|
let patch = patches
|
||||||
|
|
@ -906,7 +926,13 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
let workdir = workdir.ok_or(anyhow!(
|
let workdir = workdir.ok_or(anyhow!(
|
||||||
"this command must be run from a repository checkout"
|
"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 } => {
|
Operation::Cache { patch_id } => {
|
||||||
let patch_id = patch_id
|
let patch_id = patch_id
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ use crate::terminal as term;
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
pub name: Option<RefString>,
|
pub name: Option<RefString>,
|
||||||
|
pub remote: Option<RefString>,
|
||||||
pub force: bool,
|
pub force: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,7 +98,13 @@ pub fn run(
|
||||||
));
|
));
|
||||||
spinner.finish();
|
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
|
let tracking = branch
|
||||||
.name()?
|
.name()?
|
||||||
.ok_or_else(|| anyhow!("failed to create tracking branch: invalid name"))?;
|
.ok_or_else(|| anyhow!("failed to create tracking branch: invalid name"))?;
|
||||||
|
|
|
||||||
|
|
@ -90,18 +90,18 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> {
|
||||||
// module is aware of that.
|
// module is aware of that.
|
||||||
cli::Paint::set_terminal(io::stderr());
|
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<_>>();
|
let args = env::args().skip(1).take(2).collect::<Vec<_>>();
|
||||||
|
|
||||||
match args.as_slice() {
|
match args.as_slice() {
|
||||||
[url] => url.parse(),
|
[url] => (None, url.parse()?),
|
||||||
[_, url] => url.parse(),
|
[remote, url] => (git::RefString::try_from(remote.as_str()).ok(), url.parse()?),
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::InvalidArguments(args));
|
return Err(Error::InvalidArguments(args));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}?;
|
};
|
||||||
|
|
||||||
let stored = profile.storage.repository_mut(url.repo)?;
|
let stored = profile.storage.repository_mut(url.repo)?;
|
||||||
if stored.is_empty()? {
|
if stored.is_empty()? {
|
||||||
|
|
@ -174,6 +174,8 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> {
|
||||||
return push::run(
|
return push::run(
|
||||||
vec![refspec.to_string()],
|
vec![refspec.to_string()],
|
||||||
&working,
|
&working,
|
||||||
|
// N.b. assume the default remote if there was no remote
|
||||||
|
remote.unwrap_or((*radicle::rad::REMOTE_NAME).clone()),
|
||||||
url,
|
url,
|
||||||
&stored,
|
&stored,
|
||||||
&profile,
|
&profile,
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,7 @@ impl FromStr for Command {
|
||||||
pub fn run(
|
pub fn run(
|
||||||
mut specs: Vec<String>,
|
mut specs: Vec<String>,
|
||||||
working: &Path,
|
working: &Path,
|
||||||
|
remote: git::RefString,
|
||||||
url: Url,
|
url: Url,
|
||||||
stored: &storage::git::Repository,
|
stored: &storage::git::Repository,
|
||||||
profile: &Profile,
|
profile: &Profile,
|
||||||
|
|
@ -222,6 +223,7 @@ pub fn run(
|
||||||
if dst == &*rad::PATCHES_REFNAME {
|
if dst == &*rad::PATCHES_REFNAME {
|
||||||
patch_open(
|
patch_open(
|
||||||
src,
|
src,
|
||||||
|
&remote,
|
||||||
&nid,
|
&nid,
|
||||||
&working,
|
&working,
|
||||||
stored,
|
stored,
|
||||||
|
|
@ -376,6 +378,7 @@ pub fn run(
|
||||||
/// Open a new patch.
|
/// Open a new patch.
|
||||||
fn patch_open<G: Signer>(
|
fn patch_open<G: Signer>(
|
||||||
src: &git::RefStr,
|
src: &git::RefStr,
|
||||||
|
upstream: &git::RefString,
|
||||||
nid: &NodeId,
|
nid: &NodeId,
|
||||||
working: &git::raw::Repository,
|
working: &git::raw::Repository,
|
||||||
stored: &storage::git::Repository,
|
stored: &storage::git::Repository,
|
||||||
|
|
@ -463,7 +466,7 @@ fn patch_open<G: Signer>(
|
||||||
|
|
||||||
// Setup current branch so that pushing updates the patch.
|
// Setup current branch so that pushing updates the patch.
|
||||||
if let Some(branch) =
|
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 let Some(name) = branch.name()? {
|
||||||
if profile.hints() {
|
if profile.hints() {
|
||||||
|
|
|
||||||
|
|
@ -341,6 +341,7 @@ pub fn setup_patch_upstream<'a>(
|
||||||
patch: &ObjectId,
|
patch: &ObjectId,
|
||||||
patch_head: git::Oid,
|
patch_head: git::Oid,
|
||||||
working: &'a git::raw::Repository,
|
working: &'a git::raw::Repository,
|
||||||
|
remote: &git::RefString,
|
||||||
force: bool,
|
force: bool,
|
||||||
) -> Result<Option<git::raw::Branch<'a>>, git::ext::Error> {
|
) -> Result<Option<git::raw::Branch<'a>>, git::ext::Error> {
|
||||||
let head = working.head()?;
|
let head = working.head()?;
|
||||||
|
|
@ -377,12 +378,7 @@ pub fn setup_patch_upstream<'a>(
|
||||||
|
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
if force || branch.upstream().is_err() {
|
if force || branch.upstream().is_err() {
|
||||||
git::set_upstream(
|
git::set_upstream(working, remote, name.as_str(), git::refs::patch(patch))?;
|
||||||
working,
|
|
||||||
&*REMOTE_NAME,
|
|
||||||
name.as_str(),
|
|
||||||
git::refs::patch(patch),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Some(git::raw::Branch::wrap(remote_branch)))
|
Ok(Some(git::raw::Branch::wrap(remote_branch)))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue