cli: add revision option to rad patch checkout
Add the `--revision` option for `rad patch checkout`. This allows the user to specify a particular revision of the patch to checkout. If the revision is not specified it will use the latest revision of the patch. Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com> X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
parent
81dc849bce
commit
6510705521
|
|
@ -0,0 +1,51 @@
|
||||||
|
We may want to checkout a particular revision of a patch.
|
||||||
|
So first, let's add another change to the patch and a `LICENSE` file.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ touch LICENSE
|
||||||
|
$ git add LICENSE
|
||||||
|
$ git commit --message "Add LICENSE, just for the business"
|
||||||
|
[patch/6ff4f09 639f44a] Add LICENSE, just for the business
|
||||||
|
1 file changed, 0 insertions(+), 0 deletions(-)
|
||||||
|
create mode 100644 LICENSE
|
||||||
|
$ git push rad -o patch.message="Add LICENSE, just for the business"
|
||||||
|
```
|
||||||
|
|
||||||
|
We can see the list of revisions of the patch by `show`ing it:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ rad patch show 6ff4f09
|
||||||
|
╭─────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Title Define power requirements │
|
||||||
|
│ Patch 6ff4f09c1b5a81347981f59b02ef43a31a07cdae │
|
||||||
|
│ Author z6MknSL…StBU8Vi (you) │
|
||||||
|
│ Head 639f44a25145a37f747f3c84265037a9461e44c5 │
|
||||||
|
│ Branches patch/6ff4f09 │
|
||||||
|
│ Commits ahead 3, behind 0 │
|
||||||
|
│ Status open │
|
||||||
|
│ │
|
||||||
|
│ See details. │
|
||||||
|
├─────────────────────────────────────────────────────────────────────┤
|
||||||
|
│ 639f44a Add LICENSE, just for the business │
|
||||||
|
│ 27857ec Add README, just for the fun │
|
||||||
|
│ 3e674d1 Define power requirements │
|
||||||
|
├─────────────────────────────────────────────────────────────────────┤
|
||||||
|
│ ● opened by z6MknSL…StBU8Vi (you) now │
|
||||||
|
│ ↑ updated to 0c0942e2ff2488617d950ede15567ca39a29972e (27857ec) now │
|
||||||
|
│ ↑ updated to 2bf48de79e371014f084b5501ecc9c9c4182e7fc (639f44a) now │
|
||||||
|
╰─────────────────────────────────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
So, let's checkout the previous revision, `0c0942e2`:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ rad patch checkout 6ff4f09 --revision 0c0942e2 -f
|
||||||
|
✓ Switched to branch patch/6ff4f09
|
||||||
|
```
|
||||||
|
|
||||||
|
And we can confirm that the current commit corresponds to `27857ec`:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git rev-parse HEAD
|
||||||
|
27857ec9eb04c69cacab516e8bf4b5fd36090f66
|
||||||
|
```
|
||||||
|
|
@ -87,6 +87,7 @@ Ready options
|
||||||
|
|
||||||
Checkout options
|
Checkout options
|
||||||
|
|
||||||
|
--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
|
||||||
-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
|
||||||
|
|
||||||
|
|
@ -155,7 +156,8 @@ pub enum Operation {
|
||||||
patch_id: Rev,
|
patch_id: Rev,
|
||||||
},
|
},
|
||||||
Checkout {
|
Checkout {
|
||||||
revision_id: Rev,
|
patch_id: Rev,
|
||||||
|
revision_id: Option<Rev>,
|
||||||
opts: checkout::Options,
|
opts: checkout::Options,
|
||||||
},
|
},
|
||||||
Comment {
|
Comment {
|
||||||
|
|
@ -259,6 +261,13 @@ impl Args for Options {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checkout options
|
// Checkout options
|
||||||
|
Long("revision") if op == Some(OperationName::Checkout) => {
|
||||||
|
let val = parser.value()?;
|
||||||
|
let rev = term::args::oid(&val)?;
|
||||||
|
|
||||||
|
revision_id = Some(rev);
|
||||||
|
}
|
||||||
|
|
||||||
Long("force") | Short('f') if op == Some(OperationName::Checkout) => {
|
Long("force") | Short('f') if op == Some(OperationName::Checkout) => {
|
||||||
checkout_opts.force = true;
|
checkout_opts.force = true;
|
||||||
}
|
}
|
||||||
|
|
@ -356,7 +365,8 @@ impl Args for Options {
|
||||||
patch_id: patch_id.ok_or_else(|| anyhow!("a patch id must be provided"))?,
|
patch_id: patch_id.ok_or_else(|| anyhow!("a patch id must be provided"))?,
|
||||||
},
|
},
|
||||||
OperationName::Checkout => Operation::Checkout {
|
OperationName::Checkout => Operation::Checkout {
|
||||||
revision_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"))?,
|
||||||
|
revision_id,
|
||||||
opts: checkout_opts,
|
opts: checkout_opts,
|
||||||
},
|
},
|
||||||
OperationName::Comment => Operation::Comment {
|
OperationName::Comment => Operation::Comment {
|
||||||
|
|
@ -437,10 +447,19 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
let patch_id = patch_id.resolve::<PatchId>(&repository.backend)?;
|
let patch_id = patch_id.resolve::<PatchId>(&repository.backend)?;
|
||||||
delete::run(&patch_id, &profile, &repository)?;
|
delete::run(&patch_id, &profile, &repository)?;
|
||||||
}
|
}
|
||||||
Operation::Checkout { revision_id, opts } => {
|
Operation::Checkout {
|
||||||
let revision_id = revision_id.resolve::<radicle::git::Oid>(&repository.backend)?;
|
patch_id,
|
||||||
|
revision_id,
|
||||||
|
opts,
|
||||||
|
} => {
|
||||||
|
let patch_id = patch_id.resolve::<radicle::git::Oid>(&repository.backend)?;
|
||||||
|
let revision_id = revision_id
|
||||||
|
.map(|rev| rev.resolve::<radicle::git::Oid>(&repository.backend))
|
||||||
|
.transpose()?
|
||||||
|
.map(patch::RevisionId::from);
|
||||||
checkout::run(
|
checkout::run(
|
||||||
&patch::RevisionId::from(revision_id),
|
&patch::PatchId::from(patch_id),
|
||||||
|
revision_id,
|
||||||
&repository,
|
&repository,
|
||||||
&workdir,
|
&workdir,
|
||||||
opts,
|
opts,
|
||||||
|
|
|
||||||
|
|
@ -18,39 +18,36 @@ pub struct Options {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
fn branch(&self, id: PatchId) -> anyhow::Result<RefString> {
|
fn branch(&self, id: &PatchId) -> anyhow::Result<RefString> {
|
||||||
match &self.name {
|
match &self.name {
|
||||||
Some(refname) => Ok(Qualified::from_refstr(refname)
|
Some(refname) => Ok(Qualified::from_refstr(refname)
|
||||||
.map_or_else(|| refname.clone(), |q| q.to_ref_string())),
|
.map_or_else(|| refname.clone(), |q| q.to_ref_string())),
|
||||||
// SAFETY: Patch IDs are valid refstrings.
|
// SAFETY: Patch IDs are valid refstrings.
|
||||||
None => {
|
None => Ok(
|
||||||
Ok(git::refname!("patch")
|
git::refname!("patch").join(RefString::try_from(term::format::cob(id)).unwrap())
|
||||||
.join(RefString::try_from(term::format::cob(&id)).unwrap()))
|
),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(
|
pub fn run(
|
||||||
revision_id: &RevisionId,
|
patch_id: &PatchId,
|
||||||
|
revision_id: Option<RevisionId>,
|
||||||
stored: &Repository,
|
stored: &Repository,
|
||||||
working: &git::raw::Repository,
|
working: &git::raw::Repository,
|
||||||
opts: Options,
|
opts: Options,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let patches = patch::Patches::open(stored)?;
|
let patches = patch::Patches::open(stored)?;
|
||||||
|
|
||||||
let (patch_id, patch, _, revision) = patches
|
let patch = patches
|
||||||
.find_by_revision(revision_id)?
|
.get(patch_id)?
|
||||||
.ok_or_else(|| anyhow!("Patch revision `{revision_id}` not found"))?;
|
.ok_or_else(|| anyhow!("Patch `{patch_id}` not found"))?;
|
||||||
let (root, _) = patch.root();
|
|
||||||
// If we passed in the root revision, it's more likely that the user was specifying the
|
let revision = match revision_id {
|
||||||
// patch itself. Hence, we checkout the latest update on the patch instead of that specific
|
Some(id) => patch
|
||||||
// revision.
|
.revision(&id)
|
||||||
let revision = if *revision_id == root {
|
.ok_or_else(|| anyhow!("Patch revision `{id}` not found"))?,
|
||||||
let (_, revision) = patch.latest();
|
None => patch.latest().1,
|
||||||
revision
|
|
||||||
} else {
|
|
||||||
&revision
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut spinner = term::spinner("Performing checkout...");
|
let mut spinner = term::spinner("Performing checkout...");
|
||||||
|
|
@ -89,7 +86,7 @@ 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, 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"))?;
|
||||||
|
|
|
||||||
|
|
@ -440,6 +440,33 @@ fn rad_patch_checkout() {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rad_patch_checkout_revision() {
|
||||||
|
let mut environment = Environment::new();
|
||||||
|
let profile = environment.profile("alice");
|
||||||
|
let working = tempfile::tempdir().unwrap();
|
||||||
|
let home = &profile.home;
|
||||||
|
|
||||||
|
// Setup a test repository.
|
||||||
|
fixtures::repository(working.path());
|
||||||
|
|
||||||
|
test("examples/rad-init.md", working.path(), Some(home), []).unwrap();
|
||||||
|
test(
|
||||||
|
"examples/rad-patch-checkout.md",
|
||||||
|
working.path(),
|
||||||
|
Some(home),
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
test(
|
||||||
|
"examples/rad-patch-checkout-revision.md",
|
||||||
|
working.path(),
|
||||||
|
Some(home),
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rad_patch_checkout_force() {
|
fn rad_patch_checkout_force() {
|
||||||
let mut environment = Environment::new();
|
let mut environment = Environment::new();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue