cli: Improve `rad ls`

Distinguish between repos that are simply tracked, vs. repos that are
cloned (and thus forked). By default, we don't show repos that don't
have a local fork.
This commit is contained in:
cloudhead 2023-11-23 13:49:40 +01:00
parent 2917198d1c
commit 53013f306f
No known key found for this signature in database
5 changed files with 76 additions and 15 deletions

View File

@ -58,3 +58,13 @@ Date: Mon Jan 1 14:39:16 2018 +0000
Second commit Second commit
``` ```
Cloned repositories show up in `rad ls`:
```
$ rad ls
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Name RID Visibility Head Description │
├───────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ heartwood rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji public f2de534 Radicle Heartwood Protocol & Stack │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```

View File

@ -1,5 +1,5 @@
Given a private repo `rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu` belonging to Alice, Given a private repo `rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu` belonging to Alice,
Bob tries to clone it, and even though he's connected to Alice, it fails. Bob tries to fetch it, and even though he's connected to Alice, it fails.
``` ~bob ``` ~bob
$ rad track rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu $ rad track rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu
@ -28,10 +28,17 @@ Bob can now fetch the private repo:
$ rad sync rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --fetch $ rad sync rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --fetch
✓ Fetching rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu from z6MknSL…StBU8Vi.. ✓ Fetching rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu from z6MknSL…StBU8Vi..
✓ Fetched repository from 1 seed(s) ✓ Fetched repository from 1 seed(s)
$ rad ls --private $ rad ls --private --all
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Name RID Visibility Head Description │ │ Name RID Visibility Head Description │
├───────────────────────────────────────────────────────────────────────────────────────────────────────────┤ ├───────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ heartwood rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu private f2de534 radicle heartwood protocol & stack │ │ heartwood rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu private f2de534 radicle heartwood protocol & stack │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯
``` ```
Note that since we don't have our own fork of this repo, omitting the `--all` flag shows nothing:
``` ~bob
$ rad ls --private
Nothing to show.
```

View File

@ -16,10 +16,14 @@ Usage
rad ls [<option>...] rad ls [<option>...]
By default, this command shows you all repositories that you have forked or initialized.
If you wish to see all tracked repositories, use the `--all` option.
Options Options
--private Show only private repositories --private Show only private repositories
--public Show only public repositories --public Show only public repositories
--all Show all repositories in storage
--verbose, -v Verbose output --verbose, -v Verbose output
--help Print help --help Print help
"#, "#,
@ -30,6 +34,7 @@ pub struct Options {
verbose: bool, verbose: bool,
public: bool, public: bool,
private: bool, private: bool,
all: bool,
} }
impl Args for Options { impl Args for Options {
@ -40,12 +45,16 @@ impl Args for Options {
let mut verbose = false; let mut verbose = false;
let mut private = false; let mut private = false;
let mut public = false; let mut public = false;
let mut all = false;
while let Some(arg) = parser.next()? { while let Some(arg) = parser.next()? {
match arg { match arg {
Long("help") | Short('h') => { Long("help") | Short('h') => {
return Err(Error::Help.into()); return Err(Error::Help.into());
} }
Long("all") => {
all = true;
}
Long("private") => { Long("private") => {
private = true; private = true;
} }
@ -62,6 +71,7 @@ impl Args for Options {
verbose, verbose,
private, private,
public, public,
all,
}, },
vec![], vec![],
)) ))
@ -71,32 +81,34 @@ impl Args for Options {
pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
let profile = ctx.profile()?; let profile = ctx.profile()?;
let storage = &profile.storage; let storage = &profile.storage;
let mut table = term::Table::new(term::TableOptions::bordered());
let repos = storage.repositories()?; let repos = storage.repositories()?;
let mut table = term::Table::new(term::TableOptions::bordered());
let mut rows = Vec::new();
if repos.is_empty() { if repos.is_empty() {
return Ok(()); return Ok(());
} }
table.push([
"Name".into(),
"RID".into(),
"Visibility".into(),
"Head".into(),
"Description".into(),
]);
table.divider();
for RepositoryInfo { rid, head, doc } in repos { for RepositoryInfo {
rid,
head,
doc,
refs,
} in repos
{
if doc.visibility.is_public() && options.private && !options.public { if doc.visibility.is_public() && options.private && !options.public {
continue; continue;
} }
if !doc.visibility.is_public() && !options.private && options.public { if !doc.visibility.is_public() && !options.private && options.public {
continue; continue;
} }
if refs.is_none() && !options.all {
continue;
}
let proj = doc.project()?; let proj = doc.project()?;
let head = term::format::oid(head).into(); let head = term::format::oid(head).into();
table.push([ rows.push([
term::format::bold(proj.name().to_owned()), term::format::bold(proj.name().to_owned()),
term::format::tertiary(rid.urn()), term::format::tertiary(rid.urn()),
term::format::visibility(&doc.visibility).into(), term::format::visibility(&doc.visibility).into(),
@ -104,7 +116,22 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
term::format::italic(proj.description().to_owned()), term::format::italic(proj.description().to_owned()),
]); ]);
} }
table.print(); rows.sort();
if rows.is_empty() {
term::print(term::format::italic("Nothing to show."));
} else {
table.push([
"Name".into(),
"RID".into(),
"Visibility".into(),
"Head".into(),
"Description".into(),
]);
table.divider();
table.extend(rows);
table.print();
}
Ok(()) Ok(())
} }

View File

@ -178,6 +178,12 @@ impl<const W: usize, T: Cell> Table<W, T> {
self.rows.push(Row::Data(row)); self.rows.push(Row::Data(row));
} }
pub fn extend(&mut self, rows: impl IntoIterator<Item = [T; W]>) {
for row in rows.into_iter() {
self.push(row);
}
}
fn inner(&self, c: Constraint) -> Size { fn inner(&self, c: Constraint) -> Size {
let mut outer = self.outer(c); let mut outer = self.outer(c);

View File

@ -51,6 +51,9 @@ pub struct RepositoryInfo<V> {
pub head: Oid, pub head: Oid,
/// Identity document. /// Identity document.
pub doc: Doc<V>, pub doc: Doc<V>,
/// Local signed refs, if any.
/// Repositories with this set to `None` are ones that are tracked but not forked.
pub refs: Option<refs::SignedRefsAt>,
} }
/// A parsed Git reference. /// A parsed Git reference.
@ -226,7 +229,15 @@ impl Storage {
continue; continue;
} }
}; };
repos.push(RepositoryInfo { rid, head, doc }); // Nb. This will be `None` if they were not found.
let refs = refs::SignedRefsAt::load(self.info.key, &repo)?;
repos.push(RepositoryInfo {
rid,
head,
doc,
refs,
});
} }
Ok(repos) Ok(repos)
} }