Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cli: Remove rad-inspect `tree' dependency
Slack Coder committed 2 years ago
commit 67a8ee9e86884b6e908c971720b51d85acc5b4ff
parent f91ef2963e4da79dccbcc11e4fb1792f6b857107
5 files changed +191 -91
modified radicle-cli/examples/rad-clone-all.md
@@ -31,28 +31,27 @@ Let's check that we have all the namespaces in storage:

```
$ rad inspect --refs
-
.
-
|-- z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
-
|   `-- refs
-
|       |-- heads
-
|       |   `-- master
-
|       `-- rad
-
|           |-- id
-
|           `-- sigrefs
-
|-- z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
-
|   `-- refs
-
|       |-- heads
-
|       |   `-- master
-
|       `-- rad
-
|           |-- id
-
|           `-- sigrefs
-
`-- z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z
-
    `-- refs
-
        |-- heads
-
        |   `-- master
-
        `-- rad
-
            |-- id
-
            `-- sigrefs
+
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+
└── refs
+
    ├── heads
+
    │   └── master
+
    └── rad
+
        ├── id
+
        └── sigrefs
+
z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
+
└── refs
+
    ├── heads
+
    │   └── master
+
    └── rad
+
        ├── id
+
        └── sigrefs
+
z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z
+
└── refs
+
    ├── heads
+
    │   └── master
+
    └── rad
+
        ├── id
+
        └── sigrefs
```

We can then setup a git remote for `bob`:
modified radicle-cli/examples/rad-fork.md
@@ -5,14 +5,13 @@ NID. This is demonstrated below where our NID is

```
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --refs
-
.
-
`-- z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
-
    `-- refs
-
        |-- heads
-
        |   `-- master
-
        `-- rad
-
            |-- id
-
            `-- sigrefs
+
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+
└── refs
+
    ├── heads
+
    │   └── master
+
    └── rad
+
        ├── id
+
        └── sigrefs
```

To remedy this, we can use the `rad fork` command for the project we
@@ -28,21 +27,20 @@ have a copy of the main set of refs:

```
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --refs
-
.
-
|-- z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
-
|   `-- refs
-
|       |-- heads
-
|       |   `-- master
-
|       `-- rad
-
|           |-- id
-
|           `-- sigrefs
-
`-- z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
-
    `-- refs
-
        |-- heads
-
        |   `-- master
-
        `-- rad
-
            |-- id
-
            `-- sigrefs
+
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+
└── refs
+
    ├── heads
+
    │   └── master
+
    └── rad
+
        ├── id
+
        └── sigrefs
+
z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
+
└── refs
+
    ├── heads
+
    │   └── master
+
    └── rad
+
        ├── id
+
        └── sigrefs
```

We are now able to setup a remote in our own working copy of the
modified radicle-cli/examples/rad-inspect.md
@@ -17,14 +17,13 @@ It's also possible to display all of the repository's git references:

```
$ rad inspect --refs
-
.
-
`-- z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
-
    `-- refs
-
        |-- heads
-
        |   `-- master
-
        `-- rad
-
            |-- id
-
            `-- sigrefs
+
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+
└── refs
+
    ├── heads
+
    │   └── master
+
    └── rad
+
        ├── id
+
        └── sigrefs
```

Or display the repository identity's payload:
modified radicle-cli/examples/rad-merge-via-push.md
@@ -30,21 +30,20 @@ And some remote refs:

```
$ rad inspect --refs
-
.
-
`-- z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
-
    `-- refs
-
        |-- cobs
-
        |   `-- xyz.radicle.patch
-
        |       |-- 0ec956c94256fa101db4c32956ce195a1aa0edf2
-
        |       `-- 928d76e22ef98a8406f2e4e4bcc8878533bbdfe0
-
        |-- heads
-
        |   |-- master
-
        |   `-- patches
-
        |       |-- 0ec956c94256fa101db4c32956ce195a1aa0edf2
-
        |       `-- 928d76e22ef98a8406f2e4e4bcc8878533bbdfe0
-
        `-- rad
-
            |-- id
-
            `-- sigrefs
+
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+
└── refs
+
    ├── cobs
+
    │   └── xyz.radicle.patch
+
    │       ├── 0ec956c94256fa101db4c32956ce195a1aa0edf2
+
    │       └── 928d76e22ef98a8406f2e4e4bcc8878533bbdfe0
+
    ├── heads
+
    │   ├── master
+
    │   └── patches
+
    │       ├── 0ec956c94256fa101db4c32956ce195a1aa0edf2
+
    │       └── 928d76e22ef98a8406f2e4e4bcc8878533bbdfe0
+
    └── rad
+
        ├── id
+
        └── sigrefs
```

Then let's merge the changes into `master`.
@@ -86,16 +85,15 @@ And so were the remote branches:

```
$ rad inspect --refs
-
.
-
`-- z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
-
    `-- refs
-
        |-- cobs
-
        |   `-- xyz.radicle.patch
-
        |       |-- 0ec956c94256fa101db4c32956ce195a1aa0edf2
-
        |       `-- 928d76e22ef98a8406f2e4e4bcc8878533bbdfe0
-
        |-- heads
-
        |   `-- master
-
        `-- rad
-
            |-- id
-
            `-- sigrefs
+
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+
└── refs
+
    ├── cobs
+
    │   └── xyz.radicle.patch
+
    │       ├── 0ec956c94256fa101db4c32956ce195a1aa0edf2
+
    │       └── 928d76e22ef98a8406f2e4e4bcc8878533bbdfe0
+
    ├── heads
+
    │   └── master
+
    └── rad
+
        ├── id
+
        └── sigrefs
```
modified radicle-cli/src/commands/inspect.rs
@@ -1,7 +1,7 @@
#![allow(clippy::or_fun_call)]
+
use std::collections::HashMap;
use std::ffi::OsString;
use std::path::{Path, PathBuf};
-
use std::process::{Command, Stdio};
use std::str::FromStr;

use anyhow::{anyhow, Context as _};
@@ -11,6 +11,7 @@ use json_color::{Color, Colorizer};
use radicle::crypto::{Unverified, Verified};
use radicle::identity::Untrusted;
use radicle::identity::{Doc, Id};
+
use radicle::storage::git::Storage;
use radicle::storage::{ReadRepository, ReadStorage};

use crate::terminal as term;
@@ -34,7 +35,7 @@ Options

    --id        Return the repository identifier (RID)
    --payload   Inspect the repository's identity payload
-
    --refs      Inspect the repository's refs on the local device (requires `tree`)
+
    --refs      Inspect the repository's refs on the local device
    --history   Show the history of the repository identity document
    --help      Print help
"#,
@@ -127,15 +128,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {

    match options.target {
        Target::Refs => {
-
            let path = storage.path_of(&id).join("refs").join("namespaces");
-

-
            Command::new("tree")
-
                .current_dir(path)
-
                .args(["--noreport", "--prune"])
-
                .stdout(Stdio::inherit())
-
                .stderr(Stdio::inherit())
-
                .spawn()?
-
                .wait()?;
+
            refs(storage, id)?;
        }
        Target::Payload => {
            println!(
@@ -216,3 +209,116 @@ fn colorizer() -> Colorizer {
        .key(Color::Blue)
        .build()
}
+

+
fn refs(storage: &Storage, id: Id) -> anyhow::Result<()> {
+
    let repo = storage.repository(id)?;
+
    let mut refs = Vec::new();
+
    for r in repo.references()? {
+
        let r = r?;
+
        if let Some(namespace) = r.namespace {
+
            refs.push(format!("{}/{}", namespace, r.name));
+
        }
+
    }
+

+
    print!("{}", tree(refs));
+

+
    Ok(())
+
}
+

+
/// Show the list of given git references as a newline terminated tree `String` similar to the tree command.
+
fn tree(mut refs: Vec<String>) -> String {
+
    refs.sort();
+

+
    // List of references with additional unique entries for each 'directory'.
+
    //
+
    // i.e. "refs/heads/master" becomes ["refs"], ["refs", "heads"], and ["refs", "heads",
+
    // "master"].
+
    let mut refs_expanded: Vec<Vec<String>> = Vec::new();
+
    // Number of entries per Git 'directory'.
+
    let mut ref_entries: HashMap<Vec<String>, usize> = HashMap::new();
+
    let mut last: Vec<String> = Vec::new();
+

+
    for r in refs {
+
        let r: Vec<String> = r.split('/').map(|s| s.to_string()).collect();
+

+
        for (i, v) in r.iter().enumerate() {
+
            let last_v = last.get(i);
+
            if Some(v) != last_v {
+
                last = r.clone().iter().take(i + 1).map(String::from).collect();
+

+
                refs_expanded.push(last.clone());
+

+
                let mut dir = last.clone();
+
                dir.pop();
+
                if dir.is_empty() {
+
                    continue;
+
                }
+

+
                if let Some(num) = ref_entries.get_mut(&dir) {
+
                    *num += 1;
+
                } else {
+
                    ref_entries.insert(dir, 1);
+
                }
+
            }
+
        }
+
    }
+
    let mut tree = String::default();
+

+
    for mut ref_components in refs_expanded {
+
        // Better to explode when things do not go as expected.
+
        let name = ref_components.pop().expect("non-empty vector");
+
        if ref_components.is_empty() {
+
            tree.push_str(&format!("{name}\n"));
+
            continue;
+
        }
+

+
        for i in 1..ref_components.len() {
+
            let parent: Vec<String> = ref_components.iter().take(i).cloned().collect();
+

+
            let num = ref_entries.get(&parent).unwrap_or(&0);
+
            if *num == 0 {
+
                tree.push_str("    ");
+
            } else {
+
                tree.push_str("│   ");
+
            }
+
        }
+

+
        if let Some(num) = ref_entries.get_mut(&ref_components) {
+
            if *num == 1 {
+
                tree.push_str(&format!("└── {name}\n"));
+
            } else {
+
                tree.push_str(&format!("├── {name}\n"));
+
            }
+
            *num -= 1;
+
        }
+
    }
+

+
    tree
+
}
+

+
#[cfg(test)]
+
mod test {
+
    use super::*;
+

+
    #[test]
+
    fn test_tree() {
+
        let arg = vec![
+
            String::from("z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/refs/heads/master"),
+
            String::from("z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/refs/rad/id"),
+
            String::from("z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/refs/rad/sigrefs"),
+
        ];
+
        let exp = r#"
+
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+
└── refs
+
    ├── heads
+
    │   └── master
+
    └── rad
+
        ├── id
+
        └── sigrefs
+
"#
+
        .trim_start();
+

+
        assert_eq!(tree(arg), exp);
+
        assert_eq!(tree(vec![String::new()]), "\n");
+
    }
+
}