cli: Allow git revision parameters as COB IDs
This commit allows any git revision to be used instead of the full SHA-1, when specifying COBs. Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
This commit is contained in:
parent
f2d31aa9c0
commit
654a21b2b3
|
|
@ -145,7 +145,7 @@ increased to `1`.
|
|||
Instead, let's accept the proposal:
|
||||
|
||||
```
|
||||
$ rad id accept 0d396a83a5e1dda2b8929f7dc401d19dd1a79fb8 --no-confirm
|
||||
$ rad id accept 0d396a --no-confirm
|
||||
✓ Accepted proposal ✓
|
||||
title: Add Bob
|
||||
description: Add Bob as a delegate
|
||||
|
|
|
|||
|
|
@ -20,6 +20,19 @@ $ rad issue list
|
|||
2e8c1bf3fe0532a314778357c886608a966a34bd "flux capacitor underpowered" ❲unassigned❳
|
||||
```
|
||||
|
||||
Show the issue information issue.
|
||||
|
||||
```
|
||||
$ rad issue show 2e8c1bf
|
||||
title: flux capacitor underpowered
|
||||
state: open
|
||||
tags: []
|
||||
assignees: []
|
||||
|
||||
Flux capacitor power requirements exceed current supply
|
||||
```
|
||||
|
||||
|
||||
Great! Now we've documented the issue for ourselves and others.
|
||||
|
||||
Just like with other project management systems, the issue can be assigned to
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ $ rad comment 191a14e520f2eeff7c0e3ee0a5523c5217eecb89 --message 'I cannot wait
|
|||
Now, let's checkout the patch that we just created:
|
||||
|
||||
```
|
||||
$ rad patch checkout 191a14e520f2eeff7c0e3ee0a5523c5217eecb89
|
||||
$ rad patch checkout 191a14e52
|
||||
✓ Switched to branch patch/191a14e520f
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use std::ffi::OsString;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use nonempty::NonEmpty;
|
||||
use radicle::prelude::Did;
|
||||
|
||||
use crate::git::Rev;
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
use crate::terminal::args::{string, Args, Error, Help};
|
||||
use radicle::cob;
|
||||
use radicle::cob::issue;
|
||||
use radicle::storage::WriteStorage;
|
||||
|
|
@ -32,7 +32,7 @@ Options
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Options {
|
||||
pub id: issue::IssueId,
|
||||
pub id: Rev,
|
||||
pub to: NonEmpty<Did>,
|
||||
}
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ impl Args for Options {
|
|||
use lexopt::prelude::*;
|
||||
|
||||
let mut parser = lexopt::Parser::from_args(args);
|
||||
let mut id: Option<issue::IssueId> = None;
|
||||
let mut id: Option<Rev> = None;
|
||||
let mut to: Vec<Did> = Vec::new();
|
||||
|
||||
while let Some(arg) = parser.next()? {
|
||||
|
|
@ -56,11 +56,8 @@ impl Args for Options {
|
|||
to.push(did);
|
||||
}
|
||||
Value(ref val) if id.is_none() => {
|
||||
let val = val.to_string_lossy();
|
||||
let Ok(val) = issue::IssueId::from_str(&val) else {
|
||||
return Err(anyhow!("invalid Issue ID '{}'", val));
|
||||
};
|
||||
id = Some(val);
|
||||
let val = string(val);
|
||||
id = Some(Rev::from(val));
|
||||
}
|
||||
_ => {
|
||||
return Err(anyhow!(arg.unexpected()));
|
||||
|
|
@ -83,14 +80,14 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
let profile = ctx.profile()?;
|
||||
let (_, id) = radicle::rad::cwd()?;
|
||||
let repo = profile.storage.repository_mut(id)?;
|
||||
let oid = options.id.resolve(&repo.backend)?;
|
||||
let mut issues = issue::Issues::open(&repo)?;
|
||||
let mut issue = issues.get_mut(&options.id).map_err(|e| match e {
|
||||
let mut issue = issues.get_mut(&oid).map_err(|e| match e {
|
||||
cob::store::Error::NotFound(_, _) => anyhow!("issue {} not found", options.id),
|
||||
_ => e.into(),
|
||||
})?;
|
||||
let signer = term::signer(&profile)?;
|
||||
|
||||
issue.assign(options.to.into_iter().map(Did::into), &signer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
use std::ffi::OsString;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use radicle::cob;
|
||||
use radicle::cob::issue::Issues;
|
||||
use radicle::cob::patch::Patches;
|
||||
use radicle::cob::store;
|
||||
|
|
@ -11,7 +9,9 @@ use radicle::cob::thread;
|
|||
use radicle::prelude::*;
|
||||
use radicle::storage;
|
||||
|
||||
use crate::git::Rev;
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::string;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
use crate::terminal::patch::Message;
|
||||
|
||||
|
|
@ -34,25 +34,17 @@ Options
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Options {
|
||||
pub id: cob::ObjectId,
|
||||
pub id: Rev,
|
||||
pub message: Message,
|
||||
pub reply_to: Option<thread::CommentId>,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parse_cob_id(val: OsString) -> anyhow::Result<cob::ObjectId> {
|
||||
let val = val
|
||||
.to_str()
|
||||
.ok_or_else(|| anyhow!("object id specified is not UTF-8"))?;
|
||||
cob::ObjectId::from_str(val).map_err(|_| anyhow!("invalid object id '{}'", val))
|
||||
}
|
||||
|
||||
impl Args for Options {
|
||||
fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
|
||||
use lexopt::prelude::*;
|
||||
|
||||
let mut parser = lexopt::Parser::from_args(args);
|
||||
let mut id: Option<cob::ObjectId> = None;
|
||||
let mut id: Option<Rev> = None;
|
||||
let mut message = Message::default();
|
||||
let mut reply_to = None;
|
||||
|
||||
|
|
@ -76,7 +68,10 @@ impl Args for Options {
|
|||
// Common.
|
||||
Long("help") => return Err(Error::Help.into()),
|
||||
|
||||
Value(val) if id.is_none() => id = Some(parse_cob_id(val)?),
|
||||
Value(val) if id.is_none() => {
|
||||
let val = string(&val);
|
||||
id = Some(Rev::from(val));
|
||||
}
|
||||
_ => return Err(anyhow::anyhow!(arg.unexpected())),
|
||||
}
|
||||
}
|
||||
|
|
@ -103,7 +98,8 @@ fn comment(
|
|||
}
|
||||
|
||||
let mut issues = Issues::open(repo)?;
|
||||
match issues.get_mut(&options.id) {
|
||||
let id = options.id.resolve(&repo.backend)?;
|
||||
match issues.get_mut(&id) {
|
||||
Ok(mut issue) => {
|
||||
let comment_id = options.reply_to.unwrap_or_else(|| {
|
||||
let (comment_id, _) = issue.comments().next().expect("root comment always exists");
|
||||
|
|
@ -119,7 +115,7 @@ fn comment(
|
|||
}
|
||||
|
||||
let mut patches = Patches::open(repo)?;
|
||||
match patches.get_mut(&options.id) {
|
||||
match patches.get_mut(&id) {
|
||||
Ok(mut patch) => {
|
||||
let (revision_id, _) = patch.revisions().last().expect("patch has a revision");
|
||||
let comment_id = patch.comment(*revision_id, message, options.reply_to, &signer)?;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
use std::{ffi::OsString, str::FromStr as _};
|
||||
|
||||
use anyhow::{anyhow, Context as _};
|
||||
use radicle::cob::identity::{self, Proposal, ProposalId, Proposals, Revision, RevisionId};
|
||||
use radicle::cob::identity::{self, Proposal, Proposals, Revision, RevisionId};
|
||||
use radicle::git::Oid;
|
||||
use radicle::identity::Identity;
|
||||
use radicle::prelude::{Did, Doc};
|
||||
use radicle::storage::ReadStorage as _;
|
||||
use radicle_crypto::Verified;
|
||||
|
||||
use crate::git::Rev;
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
use crate::terminal::args::{string, Args, Error, Help};
|
||||
use crate::terminal::Element;
|
||||
use crate::terminal::Interactive;
|
||||
|
||||
|
|
@ -53,11 +54,11 @@ impl Metadata {
|
|||
#[derive(Clone, Debug, Default)]
|
||||
pub enum Operation {
|
||||
Accept {
|
||||
id: ProposalId,
|
||||
id: Rev,
|
||||
rev: Option<RevisionId>,
|
||||
},
|
||||
Reject {
|
||||
id: ProposalId,
|
||||
id: Rev,
|
||||
rev: Option<RevisionId>,
|
||||
},
|
||||
Edit {
|
||||
|
|
@ -67,7 +68,7 @@ pub enum Operation {
|
|||
threshold: Option<usize>,
|
||||
},
|
||||
Update {
|
||||
id: ProposalId,
|
||||
id: Rev,
|
||||
rev: Option<RevisionId>,
|
||||
title: Option<String>,
|
||||
description: Option<String>,
|
||||
|
|
@ -75,22 +76,22 @@ pub enum Operation {
|
|||
threshold: Option<usize>,
|
||||
},
|
||||
Rebase {
|
||||
id: ProposalId,
|
||||
id: Rev,
|
||||
rev: Option<RevisionId>,
|
||||
},
|
||||
Show {
|
||||
id: ProposalId,
|
||||
id: Rev,
|
||||
rev: Option<RevisionId>,
|
||||
show_revisions: bool,
|
||||
},
|
||||
#[default]
|
||||
List,
|
||||
Commit {
|
||||
id: ProposalId,
|
||||
id: Rev,
|
||||
rev: Option<RevisionId>,
|
||||
},
|
||||
Close {
|
||||
id: ProposalId,
|
||||
id: Rev,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -119,7 +120,7 @@ impl Args for Options {
|
|||
|
||||
let mut parser = lexopt::Parser::from_args(args);
|
||||
let mut op: Option<OperationName> = None;
|
||||
let mut id: Option<ProposalId> = None;
|
||||
let mut id: Option<Rev> = None;
|
||||
let mut rev: Option<RevisionId> = None;
|
||||
let mut title: Option<String> = None;
|
||||
let mut description: Option<String> = None;
|
||||
|
|
@ -173,14 +174,8 @@ impl Args for Options {
|
|||
show_revisions = true;
|
||||
}
|
||||
Value(val) if op.is_some() => {
|
||||
let val = val
|
||||
.to_str()
|
||||
.ok_or_else(|| anyhow!("proposal id specified is not UTF-8"))?;
|
||||
|
||||
id = Some(
|
||||
ProposalId::from_str(val)
|
||||
.map_err(|_| anyhow!("invalid proposal id '{}'", val))?,
|
||||
);
|
||||
let val = string(&val);
|
||||
id = Some(Rev::from(val));
|
||||
}
|
||||
_ => {
|
||||
return Err(anyhow!(arg.unexpected()));
|
||||
|
|
@ -245,6 +240,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
let interactive = &options.interactive;
|
||||
match options.op {
|
||||
Operation::Accept { id, rev } => {
|
||||
let id = id.resolve(&repo.backend)?;
|
||||
let mut proposal = proposals.get_mut(&id)?;
|
||||
let (rid, revision) = select(&proposal, rev, &previous, interactive)?;
|
||||
warn_out_of_date(revision, &previous);
|
||||
|
|
@ -257,6 +253,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
}
|
||||
}
|
||||
Operation::Reject { id, rev } => {
|
||||
let id = id.resolve(&repo.backend)?;
|
||||
let mut proposal = proposals.get_mut(&id)?;
|
||||
let (rid, revision) = select(&proposal, rev, &previous, interactive)?;
|
||||
warn_out_of_date(revision, &previous);
|
||||
|
|
@ -311,6 +308,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
delegates,
|
||||
threshold,
|
||||
} => {
|
||||
let id = id.resolve(&repo.backend)?;
|
||||
let mut proposal = proposals.get_mut(&id)?;
|
||||
let (_, revision) = select(&proposal, rev, &previous, interactive)?;
|
||||
|
||||
|
|
@ -354,6 +352,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
}
|
||||
}
|
||||
Operation::Rebase { id, rev } => {
|
||||
let id = id.resolve(&repo.backend)?;
|
||||
// TODO: it would be nice if rebasing also handled fast-forwards nicely.
|
||||
let mut proposal = proposals.get_mut(&id)?;
|
||||
let (_, revision) = select(&proposal, rev, &previous, interactive)?;
|
||||
|
|
@ -407,6 +406,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
t.print();
|
||||
}
|
||||
Operation::Commit { id, rev } => {
|
||||
let id = id.resolve(&repo.backend)?;
|
||||
let mut proposal = proposals.get_mut(&id)?;
|
||||
let (rid, revision) = commit_select(&proposal, rev, &previous, interactive)?;
|
||||
warn_out_of_date(revision, &previous);
|
||||
|
|
@ -419,6 +419,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
}
|
||||
}
|
||||
Operation::Close { id } => {
|
||||
let id = id.resolve(&repo.backend)?;
|
||||
let mut proposal = proposals.get_mut(&id)?;
|
||||
let yes = confirm(interactive, "Are you sure you want to close?");
|
||||
if yes {
|
||||
|
|
@ -432,6 +433,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
rev,
|
||||
show_revisions,
|
||||
} => {
|
||||
let id = id.resolve(&repo.backend)?;
|
||||
let proposal = proposals
|
||||
.get(&id)?
|
||||
.context("No proposal with the given ID exists")?;
|
||||
|
|
|
|||
|
|
@ -6,13 +6,14 @@ use anyhow::{anyhow, Context as _};
|
|||
use radicle::node::Handle;
|
||||
use radicle::prelude::Did;
|
||||
|
||||
use crate::git::Rev;
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
use crate::terminal::args::{string, Args, Error, Help};
|
||||
use crate::terminal::Element;
|
||||
|
||||
use radicle::cob::common::{Reaction, Tag};
|
||||
use radicle::cob::issue;
|
||||
use radicle::cob::issue::{CloseReason, IssueId, Issues, State};
|
||||
use radicle::cob::issue::{CloseReason, Issues, State};
|
||||
use radicle::storage::WriteStorage;
|
||||
use radicle::{cob, Node};
|
||||
|
||||
|
|
@ -71,17 +72,17 @@ pub enum Operation {
|
|||
description: Option<String>,
|
||||
},
|
||||
Show {
|
||||
id: IssueId,
|
||||
id: Rev,
|
||||
},
|
||||
State {
|
||||
id: IssueId,
|
||||
id: Rev,
|
||||
state: State,
|
||||
},
|
||||
Delete {
|
||||
id: IssueId,
|
||||
id: Rev,
|
||||
},
|
||||
React {
|
||||
id: IssueId,
|
||||
id: Rev,
|
||||
reaction: Reaction,
|
||||
},
|
||||
List {
|
||||
|
|
@ -101,7 +102,7 @@ impl Args for Options {
|
|||
|
||||
let mut parser = lexopt::Parser::from_args(args);
|
||||
let mut op: Option<OperationName> = None;
|
||||
let mut id: Option<IssueId> = None;
|
||||
let mut id: Option<Rev> = None;
|
||||
let mut assigned: Option<Assigned> = None;
|
||||
let mut title: Option<String> = None;
|
||||
let mut reaction: Option<Reaction> = None;
|
||||
|
|
@ -161,14 +162,8 @@ impl Args for Options {
|
|||
unknown => anyhow::bail!("unknown operation '{}'", unknown),
|
||||
},
|
||||
Value(val) if op.is_some() => {
|
||||
let val = val
|
||||
.to_str()
|
||||
.ok_or_else(|| anyhow!("issue id specified is not UTF-8"))?;
|
||||
|
||||
id = Some(
|
||||
IssueId::from_str(val)
|
||||
.map_err(|_| anyhow!("invalid issue id '{}'", val))?,
|
||||
);
|
||||
let val = string(&val);
|
||||
id = Some(Rev::from(val));
|
||||
}
|
||||
_ => {
|
||||
return Err(anyhow!(arg.unexpected()));
|
||||
|
|
@ -225,16 +220,19 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
show_issue(&issue)?;
|
||||
}
|
||||
Operation::Show { id } => {
|
||||
let id = id.resolve(&repo.backend)?;
|
||||
let issue = issues
|
||||
.get(&id)?
|
||||
.context("No issue with the given ID exists")?;
|
||||
show_issue(&issue)?;
|
||||
}
|
||||
Operation::State { id, state } => {
|
||||
let id = id.resolve(&repo.backend)?;
|
||||
let mut issue = issues.get_mut(&id)?;
|
||||
issue.lifecycle(state, &signer)?;
|
||||
}
|
||||
Operation::React { id, reaction } => {
|
||||
let id = id.resolve(&repo.backend)?;
|
||||
if let Ok(mut issue) = issues.get_mut(&id) {
|
||||
let (comment_id, _) = term::io::comment_select(&issue).unwrap();
|
||||
issue.react(*comment_id, reaction, &signer)?;
|
||||
|
|
@ -325,6 +323,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
t.print();
|
||||
}
|
||||
Operation::Delete { id } => {
|
||||
let id = id.resolve(&repo.backend)?;
|
||||
issues.remove(&id, &signer)?;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ use std::str::FromStr;
|
|||
|
||||
use anyhow::{anyhow, Context};
|
||||
|
||||
use crate::git::Rev;
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
use crate::terminal::args::{string, Args, Error, Help};
|
||||
use radicle::cob::patch::RevisionIx;
|
||||
use radicle::cob::patch::{Patch, PatchId, Patches};
|
||||
use radicle::git;
|
||||
|
|
@ -72,7 +73,7 @@ pub enum State {
|
|||
}
|
||||
#[derive(Debug)]
|
||||
pub struct Options {
|
||||
pub id: PatchId,
|
||||
pub id: Rev,
|
||||
pub interactive: bool,
|
||||
pub revision: Option<RevisionIx>,
|
||||
}
|
||||
|
|
@ -82,7 +83,7 @@ impl Args for Options {
|
|||
use lexopt::prelude::*;
|
||||
|
||||
let mut parser = lexopt::Parser::from_args(args);
|
||||
let mut id: Option<PatchId> = None;
|
||||
let mut id: Option<Rev> = None;
|
||||
let mut revision: Option<RevisionIx> = None;
|
||||
let mut interactive = false;
|
||||
|
||||
|
|
@ -103,14 +104,8 @@ impl Args for Options {
|
|||
revision = Some(id);
|
||||
}
|
||||
Value(val) => {
|
||||
let val = val
|
||||
.to_str()
|
||||
.ok_or_else(|| anyhow!("patch id specified is not UTF-8"))?;
|
||||
|
||||
id = Some(
|
||||
PatchId::from_str(val)
|
||||
.map_err(|_| anyhow!("invalid patch id '{}'", val))?,
|
||||
);
|
||||
let val = string(&val);
|
||||
id = Some(Rev::from(val));
|
||||
}
|
||||
_ => return Err(anyhow::anyhow!(arg.unexpected())),
|
||||
}
|
||||
|
|
@ -149,10 +144,10 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
//
|
||||
// Get patch information
|
||||
//
|
||||
let patch_id = options.id;
|
||||
let patch_id = options.id.resolve(&repository.backend)?;
|
||||
let mut patch = patches
|
||||
.get_mut(&patch_id)
|
||||
.map_err(|e| anyhow!("couldn't find patch {} locally: {e}", &options.id))?;
|
||||
.map_err(|e| anyhow!("couldn't find patch {} locally: {e}", &options.id.clone()))?;
|
||||
|
||||
let head = repo.head()?;
|
||||
let branch = head
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ use radicle::cob::patch::PatchId;
|
|||
use radicle::{prelude::*, Node};
|
||||
|
||||
use crate::commands::rad_fetch as fetch;
|
||||
use crate::git::Rev;
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
use crate::terminal::args::{string, Args, Error, Help};
|
||||
use crate::terminal::patch::Message;
|
||||
|
||||
pub const HELP: Help = Help {
|
||||
|
|
@ -70,17 +71,17 @@ pub enum Operation {
|
|||
message: Message,
|
||||
},
|
||||
Show {
|
||||
patch_id: PatchId,
|
||||
patch_id: Rev,
|
||||
},
|
||||
Update {
|
||||
patch_id: Option<PatchId>,
|
||||
patch_id: Option<Rev>,
|
||||
message: Message,
|
||||
},
|
||||
Delete {
|
||||
patch_id: PatchId,
|
||||
patch_id: Rev,
|
||||
},
|
||||
Checkout {
|
||||
patch_id: PatchId,
|
||||
patch_id: Rev,
|
||||
},
|
||||
List,
|
||||
}
|
||||
|
|
@ -113,7 +114,7 @@ impl Args for Options {
|
|||
Long("message") | Short('m') => {
|
||||
if message != Message::Blank {
|
||||
// We skip this code when `no-message` is specified.
|
||||
let txt: String = parser.value()?.to_string_lossy().into();
|
||||
let txt: String = term::args::string(&parser.value()?);
|
||||
message.append(&txt);
|
||||
}
|
||||
}
|
||||
|
|
@ -156,17 +157,11 @@ impl Args for Options {
|
|||
"c" | "checkout" => op = Some(OperationName::Checkout),
|
||||
unknown => anyhow::bail!("unknown operation '{}'", unknown),
|
||||
},
|
||||
Value(val) if op == Some(OperationName::Show) && patch_id.is_none() => {
|
||||
patch_id = Some(term::cob::parse_patch_id(val)?);
|
||||
}
|
||||
Value(val) if op == Some(OperationName::Update) && patch_id.is_none() => {
|
||||
patch_id = Some(term::cob::parse_patch_id(val)?);
|
||||
}
|
||||
Value(val) if op == Some(OperationName::Checkout) && patch_id.is_none() => {
|
||||
patch_id = Some(term::cob::parse_patch_id(val)?);
|
||||
}
|
||||
Value(val) if op == Some(OperationName::Delete) && patch_id.is_none() => {
|
||||
patch_id = Some(term::cob::parse_patch_id(val)?);
|
||||
Value(val)
|
||||
if op.is_some() && op != Some(OperationName::List) && patch_id.is_none() =>
|
||||
{
|
||||
let val = string(&val);
|
||||
patch_id = Some(Rev::from(val));
|
||||
}
|
||||
_ => return Err(anyhow::anyhow!(arg.unexpected())),
|
||||
}
|
||||
|
|
@ -218,13 +213,18 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
Operation::List => {
|
||||
list::run(&repository, &profile, Some(workdir))?;
|
||||
}
|
||||
Operation::Show { ref patch_id } => {
|
||||
show::run(&repository, &workdir, patch_id)?;
|
||||
Operation::Show { patch_id } => {
|
||||
let patch_id = patch_id.resolve(&repository.backend)?;
|
||||
show::run(&repository, &workdir, &patch_id)?;
|
||||
}
|
||||
Operation::Update {
|
||||
patch_id,
|
||||
ref patch_id,
|
||||
ref message,
|
||||
} => {
|
||||
let patch_id = patch_id
|
||||
.as_ref()
|
||||
.map(|id| id.resolve(&repository.backend))
|
||||
.transpose()?;
|
||||
update::run(
|
||||
&repository,
|
||||
&profile,
|
||||
|
|
@ -235,10 +235,12 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
)?;
|
||||
}
|
||||
Operation::Delete { patch_id } => {
|
||||
let patch_id = patch_id.resolve(&repository.backend)?;
|
||||
delete::run(&repository, &profile, &patch_id)?;
|
||||
}
|
||||
Operation::Checkout { ref patch_id } => {
|
||||
checkout::run(&repository, &workdir, patch_id)?;
|
||||
Operation::Checkout { patch_id } => {
|
||||
let patch_id = patch_id.resolve(&repository.backend)?;
|
||||
checkout::run(&repository, &workdir, &patch_id)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@ use std::str::FromStr;
|
|||
|
||||
use anyhow::{anyhow, Context};
|
||||
|
||||
use radicle::cob::patch::{PatchId, Patches, RevisionIx, Verdict};
|
||||
use radicle::cob::patch::{Patches, RevisionIx, Verdict};
|
||||
use radicle::prelude::*;
|
||||
use radicle::rad;
|
||||
|
||||
use crate::git::Rev;
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
use crate::terminal::args::{string, Args, Error, Help};
|
||||
use crate::terminal::patch::Message;
|
||||
|
||||
pub const HELP: Help = Help {
|
||||
|
|
@ -46,7 +47,7 @@ Markdown supported.
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Options {
|
||||
pub id: PatchId,
|
||||
pub id: Rev,
|
||||
pub revision: Option<RevisionIx>,
|
||||
pub message: Message,
|
||||
pub sync: bool,
|
||||
|
|
@ -60,7 +61,7 @@ impl Args for Options {
|
|||
use lexopt::prelude::*;
|
||||
|
||||
let mut parser = lexopt::Parser::from_args(args);
|
||||
let mut id: Option<PatchId> = None;
|
||||
let mut id: Option<Rev> = None;
|
||||
let mut revision: Option<RevisionIx> = None;
|
||||
let mut message = Message::default();
|
||||
let mut sync = true;
|
||||
|
|
@ -109,14 +110,7 @@ impl Args for Options {
|
|||
verdict = Some(Verdict::Reject);
|
||||
}
|
||||
Value(val) => {
|
||||
let val = val
|
||||
.to_str()
|
||||
.ok_or_else(|| anyhow!("patch id specified is not UTF-8"))?;
|
||||
|
||||
id = Some(
|
||||
PatchId::from_str(val)
|
||||
.map_err(|_| anyhow!("invalid patch id '{}'", val))?,
|
||||
);
|
||||
id = Some(Rev::from(string(&val)));
|
||||
}
|
||||
_ => return Err(anyhow::anyhow!(arg.unexpected())),
|
||||
}
|
||||
|
|
@ -148,7 +142,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
.context(format!("couldn't load project {id} from local state"))?;
|
||||
let mut patches = Patches::open(&repository)?;
|
||||
|
||||
let patch_id = options.id;
|
||||
let patch_id = options.id.resolve(&repository.backend)?;
|
||||
let mut patch = patches
|
||||
.get_mut(&patch_id)
|
||||
.context(format!("couldn't find patch {patch_id} locally"))?;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//! Git-related functions and types.
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Display;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
|
|
@ -10,6 +11,7 @@ use std::str::FromStr;
|
|||
use anyhow::anyhow;
|
||||
use anyhow::Context as _;
|
||||
|
||||
use radicle::cob::ObjectId;
|
||||
use radicle::crypto::ssh;
|
||||
use radicle::git;
|
||||
use radicle::git::raw as git2;
|
||||
|
|
@ -27,6 +29,35 @@ pub const CONFIG_GPG_FORMAT: &str = "gpg.format";
|
|||
pub const CONFIG_GPG_SSH_PROGRAM: &str = "gpg.ssh.program";
|
||||
pub const CONFIG_GPG_SSH_ALLOWED_SIGNERS: &str = "gpg.ssh.allowedSignersFile";
|
||||
|
||||
/// Git revision parameter. Supports extended SHA-1 syntax.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Rev(String);
|
||||
|
||||
impl Rev {
|
||||
/// Return the revision as a string.
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Resolve the revision to an [`ObjectId`].
|
||||
pub fn resolve(&self, repo: &git2::Repository) -> Result<ObjectId, git2::Error> {
|
||||
let object = repo.revparse_single(self.as_str())?;
|
||||
Ok(ObjectId::from(object.id()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Rev {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Rev {
|
||||
fn from(value: String) -> Self {
|
||||
Rev(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the git repository in the current directory.
|
||||
pub fn repository() -> Result<Repository, anyhow::Error> {
|
||||
match Repository::open(".") {
|
||||
|
|
|
|||
|
|
@ -101,3 +101,7 @@ pub fn addr(val: &OsString) -> anyhow::Result<Address> {
|
|||
let val = val.to_string_lossy();
|
||||
Address::from_str(&val).map_err(|_| anyhow!("invalid address '{}'", val))
|
||||
}
|
||||
|
||||
pub fn string(val: &OsString) -> String {
|
||||
val.to_string_lossy().to_string()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue