cli: Add `rad patch archive --undo`

Allows archived patches to be reverted to open state.
This commit is contained in:
Sebastian Martinez 2024-03-15 13:50:28 +01:00 committed by cloudhead
parent ea31040ec6
commit 0581c017ae
No known key found for this signature in database
3 changed files with 45 additions and 9 deletions

View File

@ -57,7 +57,7 @@ Usage
rad patch list [--all|--merged|--open|--archived|--draft|--authored] [--author <did>]... [<option>...]
rad patch show <patch-id> [<option>...]
rad patch diff <patch-id> [<option>...]
rad patch archive <patch-id> [<option>...]
rad patch archive <patch-id> [--undo] [<option>...]
rad patch update <patch-id> [<option>...]
rad patch checkout <patch-id> [<option>...]
rad patch review <patch-id> [--accept | --reject] [-m [<string>]] [-d | --delete] [<option>...]
@ -109,6 +109,10 @@ Assign options
-d, --delete <did> Delete an assignee from the patch (may be specified multiple times).
Note: --add will take precedence over --delete
Archive options
--undo Unarchive a patch
Label options
-a, --add <label> Add a label to the patch (may be specified multiple times).
@ -204,6 +208,7 @@ pub enum Operation {
},
Archive {
patch_id: Rev,
undo: bool,
},
Ready {
patch_id: Rev,
@ -347,6 +352,11 @@ impl Args for Options {
undo = true;
}
// Archive options.
Long("undo") if op == Some(OperationName::Archive) => {
undo = true;
}
// Update options.
Short('b') | Long("base") if op == Some(OperationName::Update) => {
let val = parser.value()?;
@ -591,6 +601,7 @@ impl Args for Options {
},
OperationName::Archive => Operation::Archive {
patch_id: patch_id.ok_or_else(|| anyhow!("a patch id must be provided"))?,
undo,
},
OperationName::Checkout => Operation::Checkout {
patch_id: patch_id.ok_or_else(|| anyhow!("a patch must be provided"))?,
@ -726,9 +737,9 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
&workdir,
)?;
}
Operation::Archive { ref patch_id } => {
Operation::Archive { ref patch_id, undo } => {
let patch_id = patch_id.resolve::<PatchId>(&repository.backend)?;
archive::run(&patch_id, &profile, &repository)?;
archive::run(&patch_id, undo, &profile, &repository)?;
}
Operation::Ready { ref patch_id, undo } => {
let patch_id = patch_id.resolve::<PatchId>(&repository.backend)?;

View File

@ -3,13 +3,23 @@ use super::*;
use radicle::prelude::*;
use radicle::storage::git::Repository;
pub fn run(patch_id: &PatchId, profile: &Profile, repository: &Repository) -> anyhow::Result<()> {
pub fn run(
patch_id: &PatchId,
undo: bool,
profile: &Profile,
repository: &Repository,
) -> anyhow::Result<()> {
let signer = term::signer(profile)?;
let mut patches = profile.patches_mut(repository)?;
let Ok(mut patch) = patches.get_mut(patch_id) else {
anyhow::bail!("Patch `{patch_id}` not found");
};
patch.archive(&signer)?;
if undo {
patch.unarchive(&signer)?;
} else {
patch.archive(&signer)?;
}
Ok(())
}

View File

@ -2242,11 +2242,25 @@ where
}
/// Archive a patch.
pub fn archive<G: Signer>(&mut self, signer: &G) -> Result<EntryId, Error> {
self.lifecycle(Lifecycle::Archived, signer)
pub fn archive<G: Signer>(&mut self, signer: &G) -> Result<bool, Error> {
self.lifecycle(Lifecycle::Archived, signer)?;
Ok(true)
}
/// Mark a patch as ready to be reviewed. Returns `false` if the patch was not a draft.
/// Mark an archived patch as ready to be reviewed again.
/// Returns `false` if the patch was not archived.
pub fn unarchive<G: Signer>(&mut self, signer: &G) -> Result<bool, Error> {
if !self.is_archived() {
return Ok(false);
}
self.lifecycle(Lifecycle::Open, signer)?;
Ok(true)
}
/// Mark a patch as ready to be reviewed.
/// Returns `false` if the patch was not a draft.
pub fn ready<G: Signer>(&mut self, signer: &G) -> Result<bool, Error> {
if !self.is_draft() {
return Ok(false);
@ -2256,7 +2270,8 @@ where
Ok(true)
}
/// Mark an open patch as a draft. Returns `false` if the patch was not open and free of merges.
/// Mark an open patch as a draft.
/// Returns `false` if the patch was not open and free of merges.
pub fn unready<G: Signer>(&mut self, signer: &G) -> Result<bool, Error> {
if !matches!(self.state(), State::Open { conflicts } if conflicts.is_empty()) {
return Ok(false);