cli: Show timeline in `patch show`
This commit is contained in:
parent
ed4c266a51
commit
6b44955cd9
|
|
@ -51,14 +51,16 @@ $ rad patch
|
||||||
```
|
```
|
||||||
```
|
```
|
||||||
$ rad patch show 191a14e520f2eeff7c0e3ee0a5523c5217eecb89
|
$ rad patch show 191a14e520f2eeff7c0e3ee0a5523c5217eecb89
|
||||||
╭──────────────────────────────────────────────────────────────────╮
|
╭─────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ Title Define power requirements │
|
│ Title Define power requirements │
|
||||||
│ Patch 191a14e520f2eeff7c0e3ee0a5523c5217eecb89 │
|
│ Patch 191a14e520f2eeff7c0e3ee0a5523c5217eecb89 │
|
||||||
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
|
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
|
||||||
│ Status open │
|
│ Status open │
|
||||||
│ │
|
│ │
|
||||||
│ See details. │
|
│ See details. │
|
||||||
╰──────────────────────────────────────────────────────────────────╯
|
├─────────────────────────────────────────────────────────────────────────────────────────┤
|
||||||
|
│ ● opened by did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (you) 3 months ago │
|
||||||
|
╰─────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
|
||||||
commit 3e674d1a1df90807e934f9ae5da2591dd6848a33
|
commit 3e674d1a1df90807e934f9ae5da2591dd6848a33
|
||||||
Author: radicle <radicle@localhost>
|
Author: radicle <radicle@localhost>
|
||||||
|
|
|
||||||
|
|
@ -49,14 +49,16 @@ $ rad patch
|
||||||
│ ● opened by did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (you) 3 months ago │
|
│ ● opened by did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (you) 3 months ago │
|
||||||
╰─────────────────────────────────────────────────────────────────────────────────────────╯
|
╰─────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
$ rad patch show a07ef7743a32a2e902672ea3526d1db6ee08108a
|
$ rad patch show a07ef7743a32a2e902672ea3526d1db6ee08108a
|
||||||
╭──────────────────────────────────────────────────────────────────╮
|
╭─────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ Title Define power requirements │
|
│ Title Define power requirements │
|
||||||
│ Patch a07ef7743a32a2e902672ea3526d1db6ee08108a │
|
│ Patch a07ef7743a32a2e902672ea3526d1db6ee08108a │
|
||||||
│ Author did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk │
|
│ Author did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk │
|
||||||
│ Status open │
|
│ Status open │
|
||||||
│ │
|
│ │
|
||||||
│ See details. │
|
│ See details. │
|
||||||
╰──────────────────────────────────────────────────────────────────╯
|
├─────────────────────────────────────────────────────────────────────────────────────────┤
|
||||||
|
│ ● opened by did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (you) 3 months ago │
|
||||||
|
╰─────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
|
||||||
commit 3e674d1a1df90807e934f9ae5da2591dd6848a33
|
commit 3e674d1a1df90807e934f9ae5da2591dd6848a33
|
||||||
Author: radicle <radicle@localhost>
|
Author: radicle <radicle@localhost>
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
Operation::Show { patch_id } => {
|
Operation::Show { patch_id } => {
|
||||||
let patch_id = patch_id.resolve(&repository.backend)?;
|
let patch_id = patch_id.resolve(&repository.backend)?;
|
||||||
show::run(&repository, &workdir, &patch_id)?;
|
show::run(&profile, &repository, &workdir, &patch_id)?;
|
||||||
}
|
}
|
||||||
Operation::Update {
|
Operation::Update {
|
||||||
ref patch_id,
|
ref patch_id,
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ pub fn pretty_sync_status(
|
||||||
/// For example '<oid> (branch1[, branch2])'.
|
/// For example '<oid> (branch1[, branch2])'.
|
||||||
pub fn pretty_commit_version(
|
pub fn pretty_commit_version(
|
||||||
revision_oid: &Oid,
|
revision_oid: &Oid,
|
||||||
repo: &Option<git::raw::Repository>,
|
repo: Option<&git::raw::Repository>,
|
||||||
) -> anyhow::Result<term::Line> {
|
) -> anyhow::Result<term::Line> {
|
||||||
let oid = term::format::secondary(term::format::oid(*revision_oid));
|
let oid = term::format::secondary(term::format::oid(*revision_oid));
|
||||||
let mut line = term::Line::new(oid);
|
let mut line = term::Line::new(oid);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
|
||||||
use radicle::cob::patch::{Patch, PatchId, Patches, Verdict};
|
use radicle::cob::patch::{Patch, PatchId, Patches, Revision, Verdict};
|
||||||
use radicle::git;
|
use radicle::git;
|
||||||
use radicle::prelude::*;
|
use radicle::prelude::*;
|
||||||
use radicle::profile::Profile;
|
use radicle::profile::Profile;
|
||||||
|
|
@ -40,65 +40,82 @@ pub fn run(
|
||||||
}
|
}
|
||||||
|
|
||||||
for (id, patch) in &mut own {
|
for (id, patch) in &mut own {
|
||||||
print(&me, id, patch, &workdir, repository)?;
|
widget(&me, id, patch, workdir.as_ref(), repository)?.print();
|
||||||
}
|
}
|
||||||
for (id, patch) in &mut other {
|
for (id, patch) in &mut other {
|
||||||
print(profile.id(), id, patch, &workdir, repository)?;
|
widget(profile.id(), id, patch, workdir.as_ref(), repository)?.print();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Print patch details.
|
pub fn header(
|
||||||
fn print(
|
patch_id: &PatchId,
|
||||||
|
patch: &Patch,
|
||||||
|
workdir: Option<&git::raw::Repository>,
|
||||||
|
repository: &Repository,
|
||||||
|
revision: &Revision,
|
||||||
|
) -> anyhow::Result<term::Line> {
|
||||||
|
let target_head = common::patch_merge_target_oid(patch.target(), repository)?;
|
||||||
|
let header = term::Line::spaced([
|
||||||
|
term::format::bold(patch.title()).into(),
|
||||||
|
term::format::highlight(term::format::cob(patch_id)).into(),
|
||||||
|
term::format::dim(format!("R{}", patch.version())).into(),
|
||||||
|
])
|
||||||
|
.space()
|
||||||
|
.extend(common::pretty_commit_version(&revision.head(), workdir)?)
|
||||||
|
.space()
|
||||||
|
.extend(common::pretty_sync_status(
|
||||||
|
repository.raw(),
|
||||||
|
revision.head().into(),
|
||||||
|
target_head,
|
||||||
|
)?);
|
||||||
|
|
||||||
|
Ok(header)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Patch widget.
|
||||||
|
pub fn widget<'a>(
|
||||||
whoami: &PublicKey,
|
whoami: &PublicKey,
|
||||||
patch_id: &PatchId,
|
patch_id: &PatchId,
|
||||||
patch: &Patch,
|
patch: &Patch,
|
||||||
workdir: &Option<git::raw::Repository>,
|
workdir: Option<&git::raw::Repository>,
|
||||||
repository: &Repository,
|
repository: &Repository,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<term::VStack<'a>> {
|
||||||
let target_head = common::patch_merge_target_oid(patch.target(), repository)?;
|
let (_, revision) = patch
|
||||||
|
.latest()
|
||||||
|
.ok_or_else(|| anyhow!("patch is malformed: no revisions found"))?;
|
||||||
|
let header = header(patch_id, patch, workdir, repository, revision)?;
|
||||||
|
let mut widget = term::VStack::default()
|
||||||
|
.child(header)
|
||||||
|
.divider()
|
||||||
|
.border(Some(term::colors::FAINT));
|
||||||
|
|
||||||
|
for line in timeline(whoami, patch_id, patch, repository)? {
|
||||||
|
widget.push(line);
|
||||||
|
}
|
||||||
|
Ok(widget)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn timeline(
|
||||||
|
whoami: &PublicKey,
|
||||||
|
patch_id: &PatchId,
|
||||||
|
patch: &Patch,
|
||||||
|
repository: &Repository,
|
||||||
|
) -> anyhow::Result<Vec<term::Line>> {
|
||||||
let you = patch.author().id().as_key() == whoami;
|
let you = patch.author().id().as_key() == whoami;
|
||||||
let mut author_info = term::Line::spaced([
|
let mut open = term::Line::spaced([
|
||||||
term::format::positive("●").into(),
|
term::format::positive("●").into(),
|
||||||
term::format::default("opened by").into(),
|
term::format::default("opened by").into(),
|
||||||
term::format::tertiary(patch.author().id()).into(),
|
term::format::tertiary(patch.author().id()).into(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if you {
|
if you {
|
||||||
author_info.push(term::Label::space());
|
open.push(term::Label::space());
|
||||||
author_info.push(term::format::primary("(you)"));
|
open.push(term::format::primary("(you)"));
|
||||||
}
|
}
|
||||||
author_info.push(term::Label::space());
|
let mut timeline = vec![(patch.timestamp(), open)];
|
||||||
author_info.push(term::format::dim(term::format::timestamp(
|
|
||||||
&patch.timestamp(),
|
|
||||||
)));
|
|
||||||
|
|
||||||
let (_, revision) = patch
|
|
||||||
.latest()
|
|
||||||
.ok_or_else(|| anyhow!("patch is malformed: no revisions found"))?;
|
|
||||||
let mut widget = term::VStack::default()
|
|
||||||
.child(
|
|
||||||
term::Line::spaced([
|
|
||||||
term::format::bold(patch.title()).into(),
|
|
||||||
term::format::highlight(term::format::cob(patch_id)).into(),
|
|
||||||
term::format::dim(format!("R{}", patch.version())).into(),
|
|
||||||
])
|
|
||||||
.space()
|
|
||||||
.extend(common::pretty_commit_version(&revision.head(), workdir)?)
|
|
||||||
.space()
|
|
||||||
.extend(common::pretty_sync_status(
|
|
||||||
repository.raw(),
|
|
||||||
revision.head().into(),
|
|
||||||
target_head,
|
|
||||||
)?),
|
|
||||||
)
|
|
||||||
.divider()
|
|
||||||
.child(author_info)
|
|
||||||
.border(Some(term::colors::FAINT));
|
|
||||||
|
|
||||||
let mut timeline = Vec::new();
|
|
||||||
for (revision_id, revision) in patch.revisions() {
|
for (revision_id, revision) in patch.revisions() {
|
||||||
// Don't show an "update" line for the first revision.
|
// Don't show an "update" line for the first revision.
|
||||||
if **revision_id != **patch_id {
|
if **revision_id != **patch_id {
|
||||||
|
|
@ -176,12 +193,12 @@ fn print(
|
||||||
}
|
}
|
||||||
timeline.sort_by_key(|(t, _)| *t);
|
timeline.sort_by_key(|(t, _)| *t);
|
||||||
|
|
||||||
|
let mut lines = Vec::new();
|
||||||
for (time, mut line) in timeline.into_iter() {
|
for (time, mut line) in timeline.into_iter() {
|
||||||
line.push(term::Label::space());
|
line.push(term::Label::space());
|
||||||
line.push(term::format::dim(term::format::timestamp(&time)));
|
line.push(term::format::dim(term::format::timestamp(&time)));
|
||||||
widget.push(line);
|
lines.push(line);
|
||||||
}
|
}
|
||||||
widget.print();
|
|
||||||
|
|
||||||
Ok(())
|
Ok(lines)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,11 +35,13 @@ fn show_patch_diff(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(
|
pub fn run(
|
||||||
storage: &Repository,
|
profile: &Profile,
|
||||||
|
stored: &Repository,
|
||||||
|
// TODO: Should be optional.
|
||||||
workdir: &git::raw::Repository,
|
workdir: &git::raw::Repository,
|
||||||
patch_id: &PatchId,
|
patch_id: &PatchId,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let patches = patch::Patches::open(storage)?;
|
let patches = patch::Patches::open(stored)?;
|
||||||
let Some(patch) = patches.get(patch_id)? else {
|
let Some(patch) = patches.get(patch_id)? else {
|
||||||
anyhow::bail!("Patch `{patch_id}` not found");
|
anyhow::bail!("Patch `{patch_id}` not found");
|
||||||
};
|
};
|
||||||
|
|
@ -66,7 +68,7 @@ pub fn run(
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let description = patch.description().trim();
|
let description = patch.description().trim();
|
||||||
let meta = VStack::default()
|
let mut widget = VStack::default()
|
||||||
.border(Some(term::colors::FAINT))
|
.border(Some(term::colors::FAINT))
|
||||||
.child(attrs)
|
.child(attrs)
|
||||||
.children(if !description.is_empty() {
|
.children(if !description.is_empty() {
|
||||||
|
|
@ -76,12 +78,16 @@ pub fn run(
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
});
|
})
|
||||||
|
.divider();
|
||||||
|
|
||||||
meta.print();
|
for line in list::timeline(profile.id(), patch_id, &patch, stored)? {
|
||||||
|
widget.push(line);
|
||||||
|
}
|
||||||
|
widget.print();
|
||||||
term::blank();
|
term::blank();
|
||||||
|
|
||||||
show_patch_diff(&patch, storage, workdir)?;
|
show_patch_diff(&patch, stored, workdir)?;
|
||||||
term::blank();
|
term::blank();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue