rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 heartwood3d352f23e6f7c7170864e3f453f0d744ab85bd3a
{
"request": "trigger",
"version": 1,
"event_type": "push",
"repository": {
"id": "rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5",
"name": "heartwood",
"description": "Radicle Heartwood Protocol & Stack",
"private": false,
"default_branch": "master",
"delegates": [
"did:key:z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT",
"did:key:z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW",
"did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"did:key:z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz",
"did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz"
]
},
"pusher": {
"id": "did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz",
"alias": "lorenz"
},
"before": "3d352f23e6f7c7170864e3f453f0d744ab85bd3a",
"after": "3d352f23e6f7c7170864e3f453f0d744ab85bd3a",
"branch": "master",
"commits": [
"3d352f23e6f7c7170864e3f453f0d744ab85bd3a",
"54fd8c40a0b64e061d0ec4f32b9471b36ec11ee0",
"174792813aaa0d51de721c6dfe6d299ff7e03ab6",
"0aaa81f82a60d35310c09b518ffb283bf1bb1af4"
]
}
{
"response": "triggered",
"run_id": {
"id": "9ffd5085-5f5f-4839-84f2-2fbd061cca0a"
},
"info_url": "https://cci.rad.levitte.org//9ffd5085-5f5f-4839-84f2-2fbd061cca0a.html"
}
Started at: 2025-08-04 17:05:03.890532+02:00
Commands:
$ rad clone rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 .
✓ Creating checkout in ./...
✓ Remote cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT added
✓ Remote-tracking branch cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT/master created for z6MksFq…bS9wzpT
✓ Remote cloudhead@z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW added
✓ Remote-tracking branch cloudhead@z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW/master created for z6MktaN…hzPZRZW
✓ Remote fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM added
✓ Remote-tracking branch fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM/master created for z6Mkire…SQZ3voM
✓ Remote erikli@z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz added
✓ Remote-tracking branch erikli@z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz/master created for z6MkgFq…FGAnBGz
✓ Remote lorenz@z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz added
✓ Remote-tracking branch lorenz@z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz/master created for z6MkkPv…WX5sTEz
✓ Repository successfully cloned under /opt/radcis/ci.rad.levitte.org/cci/state/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/
╭────────────────────────────────────╮
│ heartwood │
│ Radicle Heartwood Protocol & Stack │
│ 112 issues · 11 patches │
╰────────────────────────────────────╯
Run `cd ./.` to go to the repository directory.
Exit code: 0
$ git config advice.detachedHead false
Exit code: 0
$ git checkout 3d352f23e6f7c7170864e3f453f0d744ab85bd3a
HEAD is now at 3d352f23 canonical: Support Annotated Tags
Exit code: 0
$ git show 3d352f23e6f7c7170864e3f453f0d744ab85bd3a
commit 3d352f23e6f7c7170864e3f453f0d744ab85bd3a
Author: Lorenz Leutgeb <lorenz.leutgeb@radicle.xyz>
Date: Wed Jul 23 22:04:40 2025 +0200
canonical: Support Annotated Tags
The logic for computing canonical references conflated the semantics of
annotated and lighweight tags, yielding confusing/wrong results. The
main culprit was the call to `peel_to_commit` in
`impl ReadRepository for Repository`, peeling tag objects away.
To resolve this, we separate out quroum computation per object type: one
implementation for commits, and one for tags.
Also move move canonical error types and methods into their own
module to have a cleaner file structure for the main logic.
Capture the `BTreeMap<Oid, u8>` type into a `Votes` struct, with its own API.
We were getting the objects from the repository twice – once in `Canonical::new`
and once again in `ensure_commit_or_tag` – so the latter was removed.
Use an `enum CanonicalObject` to specify which object types the canonical
process supports.
During `converges`, separate commits and tags, and ensure that we are only
looking at objects of one type. Note, that Fintan think this is actually the
*wrong* place to do it because we already filter the objects – so we should
keep track of that information higher up the callstack.
Co-authored-by: Fintan Halpenny <fintan.halpenny@gmail.com>
diff --git a/crates/radicle-cli/examples/git/git-push-amend.md b/crates/radicle-cli/examples/git/git-push-amend.md
index 1dd146f5..4fe8d313 100644
--- a/crates/radicle-cli/examples/git/git-push-amend.md
+++ b/crates/radicle-cli/examples/git/git-push-amend.md
@@ -21,7 +21,7 @@ $ git commit --amend -m "Neue Änderungen" --allow-empty -q
``` ~alice (stderr)
$ git push rad master -f
-✓ Canonical reference refs/heads/master updated to 9170c8795d3a78f0381a0ffafb20ea69fb0f5b6b
+✓ Canonical reference refs/heads/master updated to target commit 9170c8795d3a78f0381a0ffafb20ea69fb0f5b6b
✓ Synced with 1 seed(s)
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+ fb25886...9170c87 master -> master (forced update)
diff --git a/crates/radicle-cli/examples/git/git-push-canonical-annotated-tags.md b/crates/radicle-cli/examples/git/git-push-canonical-annotated-tags.md
new file mode 100644
index 00000000..6b01e9ef
--- /dev/null
+++ b/crates/radicle-cli/examples/git/git-push-canonical-annotated-tags.md
@@ -0,0 +1,231 @@
+In this example, we will show how we can make other references become canonical.
+To illustrate, we will use annotated Git tags as an example. The storage of the repository
+should look something like this by the end of the example:
+
+~~~
+storage/z6cFWeWpnZNHh9rUW8phgA3b5yGt/refs
+├── heads
+│ └── main
+├── namespaces
+│ ├── z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+│ │ └── refs
+│ │ ├── cobs
+│ │ │ └── xyz.radicle.id
+│ │ │ └── 865c48204bd7bb7f088b8db90ffdccb48cfa0a50
+│ │ ├── heads
+│ │ │ └── master
+│ │ ├── tags
+│ │ │ ├── v1.0-hotfix
+│ │ │ └── v1.0
+│ │ └── rad
+│ │ ├── id
+│ │ └── sigrefs
+│ └── z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
+│ └── refs
+│ ├── heads
+│ │ └── master
+│ ├── tags
+│ │ ├── v1.0-hotfix
+│ │ └── v1.0
+│ └── rad
+│ ├── id
+│ └── sigrefs
+├── rad
+│ └── id
+└── tags
+ ├── v1.0-hotfix
+ └── v1.0
+~~~
+
+Noting that there are tags under `refs/tags` now.
+
+To start, Alice will add a new payload to the repository identity. The
+identifier for this payload is `xyz.radicle.crefs`. It contains a single field
+with the key `rules`, and the value for this key is an array of rules. In this
+case, we will have two rules: one for `refs/tags/*` and one for `refs/tags/qa/*`
+(see RIP-0004 for more information on the rules).
+
+``` ~alice
+$ rad id update --title "Add canonical reference rules" --payload xyz.radicle.crefs rules '{ "refs/tags/*": { "threshold": 1, "allow": "delegates" }, "refs/tags/qa/*": { "threshold": 1, "allow": "delegates" }}'
+✓ Identity revision [..] created
+╭────────────────────────────────────────────────────────────────────────╮
+│ Title Add canonical reference rules │
+│ Revision c3349f07bfe6a82bbeb2989d2de4a918408f9831 │
+│ Blob 85fa09e2de93b825d5231778dbb34143004a4bca │
+│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
+│ State accepted │
+│ Quorum yes │
+├────────────────────────────────────────────────────────────────────────┤
+│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
+╰────────────────────────────────────────────────────────────────────────╯
+
+@@ -1,13 +1,25 @@
+ {
+ "payload": {
++ "xyz.radicle.crefs": {
++ "rules": {
++ "refs/tags/*": {
++ "allow": "delegates",
++ "threshold": 1
++ },
++ "refs/tags/qa/*": {
++ "allow": "delegates",
++ "threshold": 1
++ }
++ }
++ },
+ "xyz.radicle.project": {
+ "defaultBranch": "master",
+ "description": "Radicle Heartwood Protocol & Stack",
+ "name": "heartwood"
+ }
+ },
+ "delegates": [
+ "did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
+ ],
+ "threshold": 1
+ }
+```
+
+Now, Alice will create an annotated tag and push it:
+
+``` ~alice
+$ git tag -a -m "Hotfix for release 1" v1.0-hotfix
+$ git cat-file -t v1.0-hotfix
+tag
+```
+
+``` ~alice (stderr)
+$ git push rad --tags
+✓ Canonical reference refs/tags/v1.0-hotfix updated to target tag ac51a0746a5e8311829bc481202909a1e3acc0c2
+✓ Synced with 1 seed(s)
+To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+ * [new tag] v1.0-hotfix -> v1.0-hotfix
+```
+
+Notice that the output included a message about a canonical reference being
+updated:
+
+~~~
+✓ Canonical reference refs/tags/v1.0-hotfix updated to target tag ac51a0746a5e8311829bc481202909a1e3acc0c2
+~~~
+
+On the other side, Bob performs a fetch and now has the tags locally:
+
+``` ~bob (stderr)
+$ cd heartwood
+$ git fetch rad
+From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji
+ * [new tag] v1.0-hotfix -> rad/tags/v1.0-hotfix
+ * [new tag] v1.0-hotfix -> v1.0-hotfix
+```
+
+Since Alice crated an annotated tag, resolving the reference on Bob's end yields an object of type 'tag'.
+
+``` ~bob
+$ git cat-file -t v1.0-hotfix
+tag
+```
+
+In the next portion of this example, we want to show that using a `threshold` of
+`2` requires both delegates. To do this, Bob creates a `master` reference, Alice
+adds him as a remote, and adds him to the identity delegates, as well as setting
+the `threshold` to `2` for the `refs/tags/*` rule:
+
+``` ~bob
+$ rad remote add z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --name alice
+✓ Follow policy updated for z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (alice)
+Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from the network, found 1 potential seed(s).
+✓ Target met: 1 seed(s)
+✓ Remote alice added
+✓ Remote-tracking branch alice/master created for z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+$ git push rad master
+```
+
+``` ~alice
+$ rad remote add z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --name bob
+✓ Follow policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (bob)
+Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from the network, found 1 potential seed(s).
+✓ Target met: 1 seed(s)
+✓ Remote bob added
+✓ Remote-tracking branch bob/master created for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
+$ rad id update --title "Add Bob" --delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --no-confirm -q
+27ab0d77a95581c59ca9d30e679ceb06a9f758db
+$ rad id update --title "Update canonical reference rules" --payload xyz.radicle.crefs rules '{ "refs/tags/*": { "threshold": 2, "allow": "delegates" }, "refs/tags/qa/*": { "threshold": 1, "allow": "delegates" } }' -q
+dace164ba43fa51802697ec28d0b1965a9d7808b
+```
+
+**Note:** here we have to specify all the rules again to update the `threshold`.
+In reality, you can use `rad id update --edit` and edit the payload in your
+editor instead.
+
+``` ~bob
+$ rad sync -f
+Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from the network, found 1 potential seed(s).
+✓ Target met: 1 seed(s)
+🌱 Fetched from z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+$ rad id accept dace164ba43fa51802697ec28d0b1965a9d7808b -q
+```
+
+When Bob creates a new tag and pushes it, we see that there's a warning that
+no quorum was found for the new tag:
+
+``` ~bob
+$ git tag -l
+v1.0-hotfix
+$ git rev-parse v1.0-hotfix
+ac51a0746a5e8311829bc481202909a1e3acc0c2
+```
+
+``` ~bob (stderr)
+$ git tag -a -m "Release 2" v2.0
+$ git push rad --tags
+warn: could not determine target for canonical reference 'refs/tags/v2.0', no object with at least 2 vote(s) found (threshold not met)
+warn: it is recommended to find an object (either commit or tag) to agree upon
+✓ Synced with 1 seed(s)
+To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
+ * [new tag] v1.0-hotfix -> v1.0-hotfix
+ * [new tag] v2.0 -> v2.0
+```
+
+Alice can then fetch and checkout the new tag, create one on her side, and push
+it:
+
+``` ~alice (stderr)
+$ git fetch bob
+From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
+ * [new tag] v1.0-hotfix -> bob/tags/v1.0-hotfix
+ * [new tag] v2.0 -> bob/tags/v2.0
+```
+
+At this point Alice might check out `v2.0` and consider whether she agrees with Bob.
+Let's say that Alice agrees, so she copies the tag to her repository using `git tag`:
+
+``` ~alice
+$ git tag v2.0 bob/tags/v2.0
+```
+
+``` ~alice (stderr)
+$ git push rad --tags
+✓ Canonical reference refs/tags/v2.0 updated to target tag 89f935f27a16f8ed97915ade4accab8fe48057aa
+✓ Synced with 1 seed(s)
+To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+ * [new tag] v2.0 -> v2.0
+```
+
+Now that Bob has also pushed this tag, we can see that the tag was made
+canonical.
+
+For the final portion of the example, we will show that both delegates aren't
+required for pushing tags that match the rule `refs/tags/qa/*`. To show this,
+Bob will create a lightweight tag and push it, and we should see that the canonical
+reference is created:
+
+``` ~bob (stderr)
+$ git tag qa/v2.1
+$ git push rad --tags
+✓ Canonical reference refs/tags/qa/v2.1 updated to target commit f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
+✓ Synced with 1 seed(s)
+To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
+ * [new tag] qa/v2.1 -> qa/v2.1
+```
diff --git a/crates/radicle-cli/examples/git/git-push-canonical-tags.md b/crates/radicle-cli/examples/git/git-push-canonical-lightweight-tags.md
similarity index 87%
rename from crates/radicle-cli/examples/git/git-push-canonical-tags.md
rename to crates/radicle-cli/examples/git/git-push-canonical-lightweight-tags.md
index 7a4d4418..7fd323eb 100644
--- a/crates/radicle-cli/examples/git/git-push-canonical-tags.md
+++ b/crates/radicle-cli/examples/git/git-push-canonical-lightweight-tags.md
@@ -1,5 +1,5 @@
In this example, we will show how we can make other references become canonical.
-To illustrate, we will use Git tags as an example. The storage of the repository
+To illustrate, we will use lightweight Git tags as an example. The storage of the repository
should look something like this by the end of the example:
~~~
@@ -95,7 +95,7 @@ $ git tag v1.0-hotfix
``` ~alice (stderr)
$ git push rad --tags
-✓ Canonical reference refs/tags/v1.0-hotfix updated to f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
+✓ Canonical reference refs/tags/v1.0-hotfix updated to target commit f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
✓ Synced with 1 seed(s)
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
* [new tag] v1.0-hotfix -> v1.0-hotfix
@@ -105,7 +105,7 @@ Notice that the output included a message about a canonical reference being
updated:
~~~
-✓ Canonical reference refs/tags/v1.0-hotfix updated to f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
+✓ Canonical reference refs/tags/v1.0-hotfix updated to target commit f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
~~~
On the other side, Bob performs a fetch and now has the tags locally:
@@ -118,6 +118,13 @@ From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji
* [new tag] v1.0-hotfix -> v1.0-hotfix
```
+Since Alice crated a lightweight tag, resolving the reference on Bob's end yields an object of type 'commit'.
+
+``` ~bob
+$ git cat-file -t v1.0-hotfix
+commit
+```
+
In the next portion of this example, we want to show that using a `threshold` of
`2` requires both delegates. To do this, Bob creates a `master` reference, Alice
adds him as a remote, and adds him to the identity delegates, as well as setting
@@ -164,8 +171,8 @@ no quorum was found for the new tag:
``` ~bob (stderr)
$ git tag v2.0
$ git push rad --tags
-warn: could not determine commit for canonical reference 'refs/tags/v2.0', no commit with at least 2 vote(s) found (threshold not met)
-warn: it is recommended to find a commit to agree upon
+warn: could not determine target for canonical reference 'refs/tags/v2.0', no object with at least 2 vote(s) found (threshold not met)
+warn: it is recommended to find an object (either commit or tag) to agree upon
✓ Synced with 1 seed(s)
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
* [new tag] v1.0-hotfix -> v1.0-hotfix
@@ -182,14 +189,16 @@ From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WR
* [new tag] v2.0 -> bob/tags/v2.0
```
+At this point Alice might check out `v2.0` and consider whether she agrees with Bob.
+Let's say that Alice agrees, so she copies the tag to her repository using `git tag`:
+
``` ~alice
-$ git checkout bob/tags/v2.0
-$ git tag v2.0
+$ git tag v2.0 bob/tags/v2.0
```
``` ~alice (stderr)
$ git push rad --tags
-✓ Canonical reference refs/tags/v2.0 updated to f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
+✓ Canonical reference refs/tags/v2.0 updated to target commit f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
✓ Synced with 1 seed(s)
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
* [new tag] v2.0 -> v2.0
@@ -206,7 +215,7 @@ reference is created:
``` ~bob (stderr)
$ git tag qa/v2.1
$ git push rad --tags
-✓ Canonical reference refs/tags/qa/v2.1 updated to f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
+✓ Canonical reference refs/tags/qa/v2.1 updated to target commit f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
✓ Synced with 1 seed(s)
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
* [new tag] qa/v2.1 -> qa/v2.1
diff --git a/crates/radicle-cli/examples/git/git-push-converge.md b/crates/radicle-cli/examples/git/git-push-converge.md
index a73a3776..0696c280 100644
--- a/crates/radicle-cli/examples/git/git-push-converge.md
+++ b/crates/radicle-cli/examples/git/git-push-converge.md
@@ -91,8 +91,8 @@ commit:
``` ~alice (stderr)
$ git push rad -f
-warn: could not determine commit for canonical reference 'refs/heads/master', no commit with at least 3 vote(s) found (threshold not met)
-warn: it is recommended to find a commit to agree upon
+warn: could not determine target for canonical reference 'refs/heads/master', no object with at least 3 vote(s) found (threshold not met)
+warn: it is recommended to find an object (either commit or tag) to agree upon
✓ Synced with 2 seed(s)
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
d09e634..0f9bd80 master -> master
@@ -116,7 +116,7 @@ become the canonical `master`.
``` ~bob (stderr)
$ git push rad
-✓ Canonical reference refs/heads/master updated to 3a75f66dd0020c9a0355cc6ec21f15de989e2001
+✓ Canonical reference refs/heads/master updated to target commit 3a75f66dd0020c9a0355cc6ec21f15de989e2001
✓ Synced with 2 seed(s)
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
2a37862..0f9bd80 master -> master
@@ -137,7 +137,7 @@ HEAD is now at 0f9bd80 Merge remote-tracking branch 'eve/master'
``` ~eve (stderr)
$ git push rad
-✓ Canonical reference refs/heads/master updated to 0f9bd8035c04b3f73f5408e73e8454879b20800b
+✓ Canonical reference refs/heads/master updated to target commit 0f9bd8035c04b3f73f5408e73e8454879b20800b
✓ Synced with 2 seed(s)
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z
3a75f66..0f9bd80 master -> master
diff --git a/crates/radicle-cli/examples/git/git-push-diverge.md b/crates/radicle-cli/examples/git/git-push-diverge.md
index 39543d7d..424eab75 100644
--- a/crates/radicle-cli/examples/git/git-push-diverge.md
+++ b/crates/radicle-cli/examples/git/git-push-diverge.md
@@ -61,7 +61,7 @@ f2de534 Second commit
```
``` ~alice RAD_SOCKET=/dev/null (stderr)
$ git push rad
-✓ Canonical reference refs/heads/master updated to f6cff86594495e9beccfeda7c20173e55c1dd9fc
+✓ Canonical reference refs/heads/master updated to target commit f6cff86594495e9beccfeda7c20173e55c1dd9fc
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
f2de534..f6cff86 master -> master
```
@@ -74,7 +74,7 @@ $ git reset --hard HEAD^ -q
```
``` ~alice RAD_SOCKET=/dev/null (stderr)
$ git push -f
-✓ Canonical reference refs/heads/master updated to 319a7dc3b195368ded4b099f8c90bbb80addccd3
+✓ Canonical reference refs/heads/master updated to target commit 319a7dc3b195368ded4b099f8c90bbb80addccd3
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+ f6cff86...319a7dc master -> master (forced update)
```
diff --git a/crates/radicle-cli/examples/git/git-push-rollback.md b/crates/radicle-cli/examples/git/git-push-rollback.md
index 8cc297b8..7b23a0a7 100644
--- a/crates/radicle-cli/examples/git/git-push-rollback.md
+++ b/crates/radicle-cli/examples/git/git-push-rollback.md
@@ -35,7 +35,7 @@ Fast-forward
``` ~alice (stderr)
$ git push rad
-✓ Canonical reference refs/heads/master updated to 319a7dc3b195368ded4b099f8c90bbb80addccd3
+✓ Canonical reference refs/heads/master updated to target commit 319a7dc3b195368ded4b099f8c90bbb80addccd3
✓ Synced with 1 seed(s)
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
f2de534..319a7dc master -> master
@@ -54,7 +54,7 @@ push and the new canonical head becomes the previous commit again:
``` ~alice (stderr)
$ git push rad -f
-✓ Canonical reference refs/heads/master updated to f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
+✓ Canonical reference refs/heads/master updated to target commit f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
✓ Synced with 1 seed(s)
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+ 319a7dc...f2de534 master -> master (forced update)
diff --git a/crates/radicle-cli/examples/rad-id-threshold.md b/crates/radicle-cli/examples/rad-id-threshold.md
index fb53f26d..8860b53e 100644
--- a/crates/radicle-cli/examples/rad-id-threshold.md
+++ b/crates/radicle-cli/examples/rad-id-threshold.md
@@ -64,7 +64,7 @@ $ git commit -v -m "Define power requirements"
``` ~alice (stderr) RAD_SOCKET=/dev/null
$ git push rad master
-✓ Canonical reference refs/heads/master updated to f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
+✓ Canonical reference refs/heads/master updated to target commit 3e674d1a1df90807e934f9ae5da2591dd6848a33
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
f2de534..3e674d1 master -> master
```
diff --git a/crates/radicle-cli/examples/rad-merge-after-update.md b/crates/radicle-cli/examples/rad-merge-after-update.md
index 929cf569..945dd57f 100644
--- a/crates/radicle-cli/examples/rad-merge-after-update.md
+++ b/crates/radicle-cli/examples/rad-merge-after-update.md
@@ -16,7 +16,7 @@ $ git commit --amend --allow-empty -q -m "Amended change"
$ git checkout master -q
$ git merge feature/1 -q
$ git push rad master
-✓ Canonical reference refs/heads/master updated to 954bcdb5008447ce294a61a21d7eb87afbe7f4a6
+✓ Canonical reference refs/heads/master updated to target commit 954bcdb5008447ce294a61a21d7eb87afbe7f4a6
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
f2de534..954bcdb master -> master
```
diff --git a/crates/radicle-cli/examples/rad-merge-no-ff.md b/crates/radicle-cli/examples/rad-merge-no-ff.md
index 58cca5ff..39999cea 100644
--- a/crates/radicle-cli/examples/rad-merge-no-ff.md
+++ b/crates/radicle-cli/examples/rad-merge-no-ff.md
@@ -37,7 +37,7 @@ Finally, we push master and expect the patch to be merged.
``` (stderr) RAD_SOCKET=/dev/null
$ git push rad master
✓ Patch 696ec5508494692899337afe6713fe1796d0315c merged
-✓ Canonical reference refs/heads/master updated to 737a10cfa29111afeb0d43cf3545cee386b939ec
+✓ Canonical reference refs/heads/master updated to target commit 737a10cfa29111afeb0d43cf3545cee386b939ec
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
f2de534..737a10c master -> master
```
diff --git a/crates/radicle-cli/examples/rad-merge-via-push.md b/crates/radicle-cli/examples/rad-merge-via-push.md
index 28ca61ea..b365f107 100644
--- a/crates/radicle-cli/examples/rad-merge-via-push.md
+++ b/crates/radicle-cli/examples/rad-merge-via-push.md
@@ -70,7 +70,7 @@ When we push to `rad/master`, we automatically merge the patches:
$ git push rad master
✓ Patch 356f73863a8920455ff6e77cd9c805d68910551b merged
✓ Patch 696ec5508494692899337afe6713fe1796d0315c merged
-✓ Canonical reference refs/heads/master updated to d6399c71702b40bae00825b3c444478d06b4e91c
+✓ Canonical reference refs/heads/master updated to target commit d6399c71702b40bae00825b3c444478d06b4e91c
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
f2de534..d6399c7 master -> master
```
@@ -148,7 +148,7 @@ the first patch, even though they were pushed together.
$ git reset --hard HEAD^
$ git push -f rad
! Patch 356f73863a8920455ff6e77cd9c805d68910551b reverted at revision 356f738
-✓ Canonical reference refs/heads/master updated to 20aa5dde6210796c3a2f04079b42316a31d02689
+✓ Canonical reference refs/heads/master updated to target commit 20aa5dde6210796c3a2f04079b42316a31d02689
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+ d6399c7...20aa5dd master -> master (forced update)
```
diff --git a/crates/radicle-cli/examples/rad-patch-merge-draft.md b/crates/radicle-cli/examples/rad-patch-merge-draft.md
index 922bab23..d36e6714 100644
--- a/crates/radicle-cli/examples/rad-patch-merge-draft.md
+++ b/crates/radicle-cli/examples/rad-patch-merge-draft.md
@@ -14,7 +14,7 @@ $ git checkout master -q
$ git merge feature/1
$ git push rad master
✓ Patch 8dfb4dcafc4346158c8160410dd3f2b0616ad4fe merged
-✓ Canonical reference refs/heads/master updated to 20aa5dde6210796c3a2f04079b42316a31d02689
+✓ Canonical reference refs/heads/master updated to target commit 20aa5dde6210796c3a2f04079b42316a31d02689
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
f2de534..20aa5dd master -> master
```
diff --git a/crates/radicle-cli/examples/rad-patch-open-explore.md b/crates/radicle-cli/examples/rad-patch-open-explore.md
index f1c98013..f26e9b3d 100644
--- a/crates/radicle-cli/examples/rad-patch-open-explore.md
+++ b/crates/radicle-cli/examples/rad-patch-open-explore.md
@@ -38,7 +38,7 @@ $ git checkout master -q
$ git merge changes -q
$ git push rad master
✓ Patch acab0ec777a97d013f30be5d5d1aec32562ecb02 merged
-✓ Canonical reference refs/heads/master updated to b2b6432af93f8fe188e32d400263021b602cfec8
+✓ Canonical reference refs/heads/master updated to target commit b2b6432af93f8fe188e32d400263021b602cfec8
✓ Synced with 1 seed(s)
https://app.radicle.xyz/nodes/[..]/rad:z3yXbb1sR6UG6ixxV2YF9jUP7ABra/tree/b2b6432af93f8fe188e32d400263021b602cfec8
diff --git a/crates/radicle-cli/examples/rad-patch-revert-merge.md b/crates/radicle-cli/examples/rad-patch-revert-merge.md
index a17ad836..85499566 100644
--- a/crates/radicle-cli/examples/rad-patch-revert-merge.md
+++ b/crates/radicle-cli/examples/rad-patch-revert-merge.md
@@ -12,7 +12,7 @@ Switched to branch 'master'
$ git merge feature/1
$ git push rad master
✓ Patch 696ec5508494692899337afe6713fe1796d0315c merged
-✓ Canonical reference refs/heads/master updated to 20aa5dde6210796c3a2f04079b42316a31d02689
+✓ Canonical reference refs/heads/master updated to target commit 20aa5dde6210796c3a2f04079b42316a31d02689
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
f2de534..20aa5dd master -> master
```
@@ -50,7 +50,7 @@ When pushing, notice that we're told our patch is reverted.
``` (stderr) RAD_SOCKET=/dev/null
$ git push rad master --force
! Patch 696ec5508494692899337afe6713fe1796d0315c reverted at revision 696ec55
-✓ Canonical reference refs/heads/master updated to f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
+✓ Canonical reference refs/heads/master updated to target commit f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+ 20aa5dd...f2de534 master -> master (forced update)
```
diff --git a/crates/radicle-cli/examples/workflow/5-patching-maintainer.md b/crates/radicle-cli/examples/workflow/5-patching-maintainer.md
index b5eb6db6..61c771da 100644
--- a/crates/radicle-cli/examples/workflow/5-patching-maintainer.md
+++ b/crates/radicle-cli/examples/workflow/5-patching-maintainer.md
@@ -92,7 +92,7 @@ Fast-forward
``` (stderr)
$ git push rad master
✓ Patch e4934b6d9dbe01ce3c7fbb5b77a80d5f1dacdc46 merged at revision 9d62420
-✓ Canonical reference refs/heads/master updated to f567f695d25b4e8fb63b5f5ad2a584529826e908
+✓ Canonical reference refs/heads/master updated to target commit f567f695d25b4e8fb63b5f5ad2a584529826e908
✓ Synced with 1 seed(s)
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
f2de534..f567f69 master -> master
diff --git a/crates/radicle-cli/tests/commands.rs b/crates/radicle-cli/tests/commands.rs
index be47ec65..2eef6a9f 100644
--- a/crates/radicle-cli/tests/commands.rs
+++ b/crates/radicle-cli/tests/commands.rs
@@ -2571,7 +2571,7 @@ fn git_tag() {
}
#[test]
-fn git_push_canonical_tags() {
+fn git_push_canonical_lightweight_tags() {
let mut environment = Environment::new();
let alice = environment.node("alice");
let bob = environment.node("bob");
@@ -2595,7 +2595,49 @@ fn git_push_canonical_tags() {
bob.clone(rid, environment.work(&bob)).unwrap();
formula(
&environment.tempdir(),
- "examples/git/git-push-canonical-tags.md",
+ "examples/git/git-push-canonical-lightweight-tags.md",
+ )
+ .unwrap()
+ .home(
+ "alice",
+ environment.work(&alice),
+ [("RAD_HOME", alice.home.path().display())],
+ )
+ .home(
+ "bob",
+ environment.work(&bob),
+ [("RAD_HOME", bob.home.path().display())],
+ )
+ .run()
+ .unwrap();
+}
+
+#[test]
+fn git_push_canonical_annotated_tags() {
+ let mut environment = Environment::new();
+ let alice = environment.node("alice");
+ let bob = environment.node("bob");
+
+ let rid = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
+
+ fixtures::repository(environment.work(&alice));
+
+ test(
+ "examples/rad-init.md",
+ environment.work(&alice),
+ Some(&alice.home),
+ [],
+ )
+ .unwrap();
+
+ let alice = alice.spawn();
+ let mut bob = bob.spawn();
+
+ bob.connect(&alice).converge([&alice]);
+ bob.clone(rid, environment.work(&bob)).unwrap();
+ formula(
+ &environment.tempdir(),
+ "examples/git/git-push-canonical-annotated-tags.md",
)
.unwrap()
.home(
diff --git a/crates/radicle-node/src/runtime.rs b/crates/radicle-node/src/runtime.rs
index 86954152..8f0e5a40 100644
--- a/crates/radicle-node/src/runtime.rs
+++ b/crates/radicle-node/src/runtime.rs
@@ -73,7 +73,7 @@ pub enum Error {
Address(#[from] address::Error),
/// A service error.
#[error("service error: {0}")]
- Service(#[from] service::Error),
+ Service(Box<service::Error>),
/// An I/O error.
#[error("i/o error: {0}")]
Io(#[from] io::Error),
@@ -92,6 +92,12 @@ pub enum Error {
GitVersion(#[from] git::VersionError),
}
+impl From<service::Error> for Error {
+ fn from(e: service::Error) -> Self {
+ Self::Service(Box::new(e))
+ }
+}
+
/// Wraps a [`UnixListener`] but tracks its origin.
pub enum ControlSocket {
/// The listener was created by binding to it.
diff --git a/crates/radicle-node/src/worker/fetch.rs b/crates/radicle-node/src/worker/fetch.rs
index ab9cf96d..40eebfd8 100644
--- a/crates/radicle-node/src/worker/fetch.rs
+++ b/crates/radicle-node/src/worker/fetch.rs
@@ -122,9 +122,9 @@ impl Handle {
log::trace!(target: "worker", "Set HEAD to {}", head.new);
}
}
- Err(RepositoryError::Quorum(radicle::git::canonical::QuorumError::Git(e))) => {
- return Err(e.into())
- }
+ Err(RepositoryError::Quorum(
+ radicle::git::canonical::error::QuorumError::Git(e),
+ )) => return Err(e.into()),
Err(RepositoryError::Quorum(e)) => {
log::warn!(target: "worker", "Fetch could not set HEAD: {e}")
}
@@ -406,7 +406,7 @@ fn set_canonical_refs(repo: &Repository, applied: &Applied) -> Result<(), error:
);
continue;
}
- Ok((refname, oid)) => {
+ Ok((refname, _, oid)) => {
if let Err(e) = repo.backend.reference(
refname.clone().as_str(),
*oid,
diff --git a/crates/radicle-protocol/src/service.rs b/crates/radicle-protocol/src/service.rs
index 8901a011..75c0f5d0 100644
--- a/crates/radicle-protocol/src/service.rs
+++ b/crates/radicle-protocol/src/service.rs
@@ -206,7 +206,13 @@ pub enum Error {
#[error(transparent)]
Repository(#[from] radicle::storage::RepositoryError),
#[error("namespaces error: {0}")]
- Namespaces(#[from] NamespacesError),
+ Namespaces(Box<NamespacesError>),
+}
+
+impl From<NamespacesError> for Error {
+ fn from(e: NamespacesError) -> Self {
+ Self::Namespaces(Box::new(e))
+ }
}
#[derive(thiserror::Error, Debug)]
@@ -304,7 +310,13 @@ enum TryFetchError<'a> {
#[error("peer fetch capacity reached; cannot initiate fetch")]
SessionCapacityReached,
#[error(transparent)]
- Namespaces(#[from] NamespacesError),
+ Namespaces(Box<NamespacesError>),
+}
+
+impl From<NamespacesError> for TryFetchError<'_> {
+ fn from(e: NamespacesError) -> Self {
+ Self::Namespaces(Box::new(e))
+ }
}
/// Fetch state for an ongoing fetch.
diff --git a/crates/radicle-remote-helper/src/push.rs b/crates/radicle-remote-helper/src/push.rs
index dfef5696..f085926a 100644
--- a/crates/radicle-remote-helper/src/push.rs
+++ b/crates/radicle-remote-helper/src/push.rs
@@ -113,13 +113,17 @@ pub enum Error {
Repository(#[from] radicle::storage::RepositoryError),
/// Quorum error.
#[error(transparent)]
- Quorum(#[from] radicle::git::canonical::QuorumError),
+ Quorum(#[from] radicle::git::canonical::error::QuorumError),
#[error(transparent)]
CanonicalRefs(#[from] radicle::identity::doc::CanonicalRefsError),
#[error(transparent)]
PushAction(#[from] error::PushAction),
#[error(transparent)]
Canonical(#[from] error::CanonicalUnrecoverable),
+ #[error(transparent)]
+ CanonicalInit(#[from] radicle::git::canonical::error::CanonicalError),
+ #[error("could not determine object type for {oid}")]
+ UnknownObjectType { oid: git::Oid },
}
/// Push command.
@@ -280,7 +284,8 @@ pub fn run(
let identity = stored.identity()?;
let project = identity.project()?;
let canonical_ref = git::refs::branch(project.default_branch());
- let mut set_canonical_refs: Vec<(git::Qualified, git::Oid)> = Vec::with_capacity(specs.len());
+ let mut set_canonical_refs: Vec<(git::Qualified, git::raw::ObjectType, git::Oid)> =
+ Vec::with_capacity(specs.len());
let working = git::raw::Repository::open(working)?;
// For each refspec, push a ref or delete a ref.
@@ -348,7 +353,13 @@ pub fn run(
// Note that we *do* allow rolling back to a previous commit on the
// canonical branch.
if let Some(canonical) = rules.canonical(dst.clone(), stored)? {
- let canonical = canonical::Canonical::new(me, *src, canonical);
+ let kind = working
+ .find_object(**src, None)?
+ .kind()
+ .and_then(git::canonical::CanonicalObjectType::new)
+ .ok_or(Error::UnknownObjectType { oid: *src })?;
+
+ let canonical = canonical::Canonical::new(me, *src, kind, canonical);
match canonical.quorum(&working) {
Ok(quorum) => set_canonical_refs.push(quorum),
Err(e) => canonical::io::handle_error(e, &dst, hints)?,
@@ -375,10 +386,10 @@ pub fn run(
if !ok.is_empty() {
let _ = stored.sign_refs(&signer)?;
- for (refname, oid) in &set_canonical_refs {
+ for (refname, kind, oid) in &set_canonical_refs {
let print_update = || {
eprintln!(
- "{} Canonical reference {} updated to {}",
+ "{} Canonical reference {} updated to target {kind} {}",
term::format::positive("✓"),
term::format::secondary(refname),
term::format::secondary(oid),
diff --git a/crates/radicle-remote-helper/src/push/canonical.rs b/crates/radicle-remote-helper/src/push/canonical.rs
index 0a509011..524402e4 100644
--- a/crates/radicle-remote-helper/src/push/canonical.rs
+++ b/crates/radicle-remote-helper/src/push/canonical.rs
@@ -6,7 +6,8 @@ use super::error;
pub(crate) struct Vote {
did: Did,
- commit: git::Oid,
+ oid: git::Oid,
+ kind: git::canonical::CanonicalObjectType,
}
/// Validates a vote to update a canonical reference during push.
@@ -16,11 +17,17 @@ pub(crate) struct Canonical<'a, 'b> {
}
impl<'a, 'b> Canonical<'a, 'b> {
- pub fn new(me: Did, head: git::Oid, canonical: git::canonical::Canonical<'a, 'b>) -> Self {
+ pub fn new(
+ me: Did,
+ head: git::Oid,
+ kind: git::canonical::CanonicalObjectType,
+ canonical: git::canonical::Canonical<'a, 'b>,
+ ) -> Self {
Self {
vote: Vote {
did: me,
- commit: head,
+ oid: head,
+ kind,
},
canonical,
}
@@ -46,31 +53,29 @@ impl<'a, 'b> Canonical<'a, 'b> {
pub fn quorum(
mut self,
working: &Repository,
- ) -> Result<(git::Qualified<'a>, git::Oid), error::Canonical> {
+ ) -> Result<(git::Qualified<'a>, git::raw::ObjectType, git::Oid), error::Canonical> {
let converges = self
.canonical
- .converges(working, (&self.vote.did, &self.vote.commit))?;
+ .converges(working, (&self.vote.did, &self.vote.oid))?;
if converges || self.canonical.has_no_tips() || self.canonical.is_only(&self.vote.did) {
- self.canonical.modify_vote(self.vote.did, self.vote.commit);
+ self.canonical
+ .modify_vote(self.vote.did, self.vote.oid, self.vote.kind);
}
match self.canonical.quorum(working) {
- Ok((cref, quorum_head)) => {
+ Ok((cref, quorum_type, quorum_head)) => {
// Canonical head is an ancestor of head.
- let is_ff = self.vote.commit == quorum_head
+ let is_ff = self.vote.oid == quorum_head
|| working
- .graph_descendant_of(*self.vote.commit, *quorum_head)
+ .graph_descendant_of(*self.vote.oid, *quorum_head)
.map_err(|err| {
- error::Canonical::graph_descendant(self.vote.commit, quorum_head, err)
+ error::Canonical::graph_descendant(self.vote.oid, quorum_head, err)
})?;
if !is_ff && !converges {
- Err(error::Canonical::heads_diverge(
- self.vote.commit,
- quorum_head,
- ))
+ Err(error::Canonical::heads_diverge(self.vote.oid, quorum_head))
} else {
- Ok((cref, quorum_head))
+ Ok((cref, quorum_type, quorum_head))
}
}
Err(err) => Err(err.into()),
@@ -80,7 +85,7 @@ impl<'a, 'b> Canonical<'a, 'b> {
pub(crate) mod io {
use radicle::git;
- use radicle::git::canonical;
+ use radicle::git::canonical::error::QuorumError;
use crate::push::error;
use crate::{hint, warn};
@@ -110,19 +115,29 @@ pub(crate) mod io {
Err(e.into())
}
error::Canonical::Quorum(e) => match e {
- e @ canonical::QuorumError::Diverging { .. } => {
+ e @ QuorumError::DivergingCommits { .. } => {
warn(e.to_string());
warn("it is recommended to find a commit to agree upon");
Ok(())
}
- e @ canonical::QuorumError::NoCandidates { .. } => {
+ e @ QuorumError::DivergingTags { .. } => {
warn(e.to_string());
- warn("it is recommended to find a commit to agree upon");
+ warn("it is recommended to find a tag to agree upon");
+ Ok(())
+ }
+ e @ QuorumError::DifferentTypes { .. } => {
+ warn(e.to_string());
+ warn("it is recommended to find an object type (either commit or tag) to agree upon");
Ok(())
}
- canonical::QuorumError::Git(err) => {
- Err(error::CanonicalUnrecoverable::Git { source: err })
+ e @ QuorumError::NoCandidates { .. } => {
+ warn(e.to_string());
+ warn(
+ "it is recommended to find an object (either commit or tag) to agree upon",
+ );
+ Ok(())
}
+ QuorumError::Git(err) => Err(error::CanonicalUnrecoverable::Git { source: err }),
},
}
}
diff --git a/crates/radicle-remote-helper/src/push/error.rs b/crates/radicle-remote-helper/src/push/error.rs
index add16353..6ce8d145 100644
--- a/crates/radicle-remote-helper/src/push/error.rs
+++ b/crates/radicle-remote-helper/src/push/error.rs
@@ -7,7 +7,7 @@ pub enum CanonicalUnrecoverable {
#[error(transparent)]
GraphDescendant(#[from] GraphDescendant),
#[error(transparent)]
- Converges(#[from] canonical::ConvergesError),
+ Converges(#[from] canonical::error::ConvergesError),
#[error(transparent)]
HeadsDiverge(#[from] HeadsDiverge),
#[error("failure while computing canonical reference: {source}")]
@@ -19,11 +19,11 @@ pub enum Canonical {
#[error(transparent)]
GraphDescendant(GraphDescendant),
#[error(transparent)]
- Converges(#[from] canonical::ConvergesError),
+ Converges(#[from] canonical::error::ConvergesError),
#[error(transparent)]
HeadsDiverge(HeadsDiverge),
#[error(transparent)]
- Quorum(#[from] canonical::QuorumError),
+ Quorum(#[from] canonical::error::QuorumError),
}
impl Canonical {
diff --git a/crates/radicle/src/git/canonical.rs b/crates/radicle/src/git/canonical.rs
index 11a4ae3b..3786e856 100644
--- a/crates/radicle/src/git/canonical.rs
+++ b/crates/radicle/src/git/canonical.rs
@@ -1,14 +1,19 @@
+pub mod error;
+use error::*;
+
pub mod rules;
+
+use nonempty::NonEmpty;
pub use rules::{MatchedRule, RawRule, Rules, ValidRule};
use std::collections::BTreeMap;
-use std::path::PathBuf;
+use std::fmt;
+use raw::ObjectType;
use raw::Repository;
-use thiserror::Error;
use crate::prelude::Did;
-use crate::storage::ReadRepository;
+use crate::storage::git;
use super::raw;
use super::{Oid, Qualified};
@@ -28,115 +33,70 @@ use super::{Oid, Qualified};
pub struct Canonical<'a, 'b> {
refname: Qualified<'a>,
rule: &'b ValidRule,
- tips: BTreeMap<Did, Oid>,
-}
-
-/// Error that can occur when calculation the [`Canonical::quorum`].
-#[derive(Debug, Error)]
-pub enum QuorumError {
- /// Could not determine a quorum [`Oid`], due to diverging tips.
- #[error("could not determine commit for canonical reference '{refname}', found diverging commits {longest} and {head}, with base commit {base} and threshold {threshold}")]
- Diverging {
- refname: String,
- threshold: usize,
- base: Oid,
- longest: Oid,
- head: Oid,
- },
- /// Could not determine a base candidate from the given set of delegates.
- #[error("could not determine commit for canonical reference '{refname}', no commit with at least {threshold} vote(s) found (threshold not met)")]
- NoCandidates { refname: String, threshold: usize },
- /// An error occurred from [`git2`].
- #[error(transparent)]
- Git(#[from] git2::Error),
+ tips: BTreeMap<Did, (Oid, CanonicalObjectType)>,
}
-#[derive(Debug, Error)]
-#[error("failed to check if {head} is an ancestor of {canonical} due to: {source}")]
-pub struct GraphDescendant {
- head: Oid,
- canonical: Oid,
- source: raw::Error,
+/// Supported Git object types for canonical computation.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum CanonicalObjectType {
+ /// The Git object is a commit.
+ Commit,
+ /// The Git object is a tag.
+ Tag,
}
-#[derive(Debug, Error)]
-#[error("the commit {commit} for {did} is missing from the repository {repo:?}")]
-pub struct MissingCommit {
- repo: PathBuf,
- did: Did,
- commit: Oid,
- source: raw::Error,
-}
-
-#[derive(Debug, Error)]
-#[error("could not determine whether the commit {commit} for {did} is part of the repository {repo:?} due to: {source}")]
-pub struct InvalidCommit {
- repo: PathBuf,
- did: Did,
- commit: Oid,
- source: raw::Error,
-}
-
-#[derive(Debug, Error)]
-pub enum ConvergesError {
- #[error(transparent)]
- GraphDescendant(#[from] GraphDescendant),
- #[error(transparent)]
- MissingCommit(#[from] MissingCommit),
- #[error(transparent)]
- InvalidCommit(#[from] InvalidCommit),
-}
-
-impl ConvergesError {
- pub fn graph_descendant(head: Oid, canonical: Oid, source: raw::Error) -> Self {
- Self::GraphDescendant(GraphDescendant {
- head,
- canonical,
- source,
- })
- }
-
- pub fn missing_commit(repo: PathBuf, did: Did, commit: Oid, err: raw::Error) -> Self {
- Self::MissingCommit(MissingCommit {
- repo,
- did,
- commit,
- source: err,
- })
+impl fmt::Display for CanonicalObjectType {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ CanonicalObjectType::Commit => f.write_str("commit"),
+ CanonicalObjectType::Tag => f.write_str("tag"),
+ }
}
+}
- pub fn invalid_commit(repo: PathBuf, did: Did, commit: Oid, err: raw::Error) -> Self {
- Self::InvalidCommit(InvalidCommit {
- repo,
- did,
- commit,
- source: err,
- })
+impl CanonicalObjectType {
+ /// Construct the [`CanonicalObjectType`] from a [`git2::ObjectType`].
+ pub fn new(kind: git::raw::ObjectType) -> Option<Self> {
+ match kind {
+ ObjectType::Commit => Some(Self::Commit),
+ ObjectType::Tag => Some(Self::Tag),
+ _ => None,
+ }
}
}
impl<'a, 'b> Canonical<'a, 'b> {
/// Construct the set of canonical tips given for the given `rule` and
/// the reference `refname`.
- pub fn new<S>(repo: &S, refname: Qualified<'a>, rule: &'b ValidRule) -> Result<Self, raw::Error>
- where
- S: ReadRepository,
- {
+ pub fn new(
+ repo: &Repository,
+ refname: Qualified<'a>,
+ rule: &'b ValidRule,
+ ) -> Result<Self, CanonicalError> {
let mut tips = BTreeMap::new();
for delegate in rule.allowed().iter() {
- match repo.reference_oid(delegate, &refname) {
- Ok(tip) => {
- tips.insert(*delegate, tip);
- }
+ let name = &refname.with_namespace(delegate.as_key().into());
+
+ let reference = match repo.find_reference(name) {
+ Ok(reference) => reference,
Err(e) if super::ext::is_not_found_err(&e) => {
log::warn!(
target: "radicle",
- "Missing `refs/namespaces/{}/{refname}` while calculating the canonical reference",
- delegate.as_key()
+ "Missing `{name}` while calculating the canonical reference",
);
+ continue;
}
- Err(e) => return Err(e),
- }
+ Err(e) => return Err(CanonicalError::find_reference(name, e)),
+ };
+
+ let Some(oid) = reference.target() else {
+ log::warn!(target: "radicle", "Missing target for reference `{name}`");
+ continue;
+ };
+
+ let kind = Self::find_object_for(delegate, oid.into(), repo)?;
+
+ tips.insert(*delegate, (oid.into(), kind));
}
Ok(Canonical {
refname,
@@ -145,9 +105,31 @@ impl<'a, 'b> Canonical<'a, 'b> {
})
}
- /// Return the set of [`Did`]s and their [`Oid`] tip.
- pub fn tips(&self) -> impl Iterator<Item = (&Did, &Oid)> {
- self.tips.iter()
+ pub fn find_object_for(
+ did: &Did,
+ oid: Oid,
+ repo: &raw::Repository,
+ ) -> Result<CanonicalObjectType, CanonicalError> {
+ match repo.find_object(*oid, None) {
+ Ok(object) => object
+ .kind()
+ .and_then(CanonicalObjectType::new)
+ .ok_or_else(|| {
+ CanonicalError::invalid_object_type(
+ repo.path().to_path_buf(),
+ *did,
+ oid,
+ object.kind(),
+ )
+ }),
+ Err(err) if super::ext::is_not_found_err(&err) => Err(CanonicalError::missing_object(
+ repo.path().to_path_buf(),
+ *did,
+ oid,
+ err,
+ )),
+ Err(err) => Err(CanonicalError::find_object(oid, err)),
+ }
}
/// Returns `true` if there were no tips found for any of the DIDs for
@@ -165,8 +147,8 @@ impl<'a, 'b> Canonical<'a, 'b> {
/// In some cases, we allow the vote to be modified. For example, when the
/// `did` is pushing a new commit, we may want to see if the new commit will
/// reach a quorum.
- pub fn modify_vote(&mut self, did: Did, new: Oid) {
- self.tips.insert(did, new);
+ pub fn modify_vote(&mut self, did: Did, oid: Oid, kind: CanonicalObjectType) {
+ self.tips.insert(did, (oid, kind));
}
/// Check that the provided `did` is part of the set of allowed
@@ -191,28 +173,116 @@ impl<'a, 'b> Canonical<'a, 'b> {
repo: &Repository,
(candidate, commit): (&Did, &Oid),
) -> Result<bool, ConvergesError> {
+ /// Ensures [`Oid`]s are of the same object type
+ enum Objects {
+ Commits(NonEmpty<Oid>),
+ Tags(NonEmpty<Oid>),
+ }
+
+ impl Objects {
+ fn new(oid: Oid, kind: CanonicalObjectType) -> Self {
+ match kind {
+ CanonicalObjectType::Commit => Self::Commits(NonEmpty::new(oid)),
+ CanonicalObjectType::Tag => Self::Tags(NonEmpty::new(oid)),
+ }
+ }
+
+ fn insert(
+ mut self,
+ oid: Oid,
+ kind: CanonicalObjectType,
+ ) -> Result<Self, CanonicalObjectType> {
+ match self {
+ Objects::Commits(ref mut commits) => match kind {
+ CanonicalObjectType::Commit => {
+ commits.push(oid);
+ Ok(self)
+ }
+ CanonicalObjectType::Tag => Err(CanonicalObjectType::Tag),
+ },
+ Objects::Tags(ref mut tags) => match kind {
+ CanonicalObjectType::Commit => {
+ tags.push(oid);
+ Ok(self)
+ }
+ CanonicalObjectType::Tag => Err(CanonicalObjectType::Commit),
+ },
+ }
+ }
+ }
+
let heads = {
- let mut heads = self
+ let heads = self
.tips
.iter()
.filter_map(|(did, tip)| (did != candidate).then_some((did, tip)));
- heads.try_fold(
- Vec::with_capacity(heads.size_hint().0),
- |mut heads, (did, head)| {
- heads.push(Self::ensure_commit(*did, *head, repo)?);
- Ok::<_, ConvergesError>(heads)
- },
- )?
+
+ let mut objects = None;
+
+ for (did, (oid, _)) in heads {
+ let kind = find_object_for(did, *oid, repo)?;
+ let oid = *oid;
+ match objects {
+ None => objects = Some(Objects::new(oid, kind)),
+ Some(objs) => {
+ objects = Some(objs.insert(oid, kind).map_err(|expected| {
+ ConvergesError::mismatched_object(
+ repo.path().to_path_buf(),
+ oid,
+ kind,
+ expected,
+ )
+ })?)
+ }
+ }
+ }
+
+ objects
};
- for head in heads {
- let (ahead, behind) = repo
- .graph_ahead_behind(**commit, *head)
- .map_err(|err| ConvergesError::graph_descendant(*commit, head, err))?;
- if ahead * behind == 0 {
- return Ok(true);
+
+ match heads {
+ None => Ok(true),
+ Some(Objects::Tags(_)) => Ok(true),
+ Some(Objects::Commits(heads)) => {
+ for head in heads {
+ let (ahead, behind) = repo
+ .graph_ahead_behind(**commit, *head)
+ .map_err(|err| ConvergesError::graph_descendant(*commit, head, err))?;
+ if ahead * behind == 0 {
+ return Ok(true);
+ }
+ }
+ Ok(false)
}
}
- Ok(false)
+ }
+
+ fn quorum_tag(&self) -> Result<Oid, QuorumError> {
+ let voting =
+ TagVoting::from_targets(self.tips.values().filter_map(|(commit, kind)| {
+ (*kind == CanonicalObjectType::Tag).then_some(*commit)
+ }));
+ let mut votes = voting.votes();
+
+ // Keep tags which pass the threshold.
+ votes.votes_past_threshold(self.threshold());
+
+ if votes.number_of_candidates() > 1 {
+ return Err(QuorumError::DivergingTags {
+ refname: self.refname.to_string(),
+ threshold: self.threshold(),
+ candidates: votes.candidates().cloned().collect(),
+ });
+ }
+
+ let tag = votes
+ .pop_first_candidate()
+ .ok_or(QuorumError::NoCandidates {
+ refname: self.refname.to_string(),
+ threshold: self.threshold(),
+ })?;
+
+ Ok((*tag).into())
}
/// Computes the quorum or "canonical" tip based on the tips, of `Canonical`,
@@ -222,41 +292,36 @@ impl<'a, 'b> Canonical<'a, 'b> {
///
/// Also returns an error if `heads` is empty or `threshold` cannot be
/// satisified with the number of heads given.
- pub fn quorum(self, repo: &raw::Repository) -> Result<(Qualified<'a>, Oid), QuorumError> {
- let mut candidates = BTreeMap::<_, usize>::new();
-
- // Build a list of candidate commits and count how many "votes" each of them has.
- // Commits get a point for each direct vote, as well as for being part of the ancestry
- // of a commit given to this function. Only commits given to the function are considered.
- for (i, head) in self.tips.values().enumerate() {
- // Add a direct vote for this head.
- *candidates.entry(*head).or_default() += 1;
-
- // Compare this head to all other heads ahead of it in the list.
- for other in self.tips.values().skip(i + 1) {
- // N.b. if heads are equal then skip it, otherwise it will end up as
- // a double vote.
- if *head == *other {
- continue;
- }
- let base = Oid::from(repo.merge_base(**head, **other)?);
-
- if base == *other || base == *head {
- *candidates.entry(base).or_default() += 1;
- }
+ fn quorum_commit(&self, repo: &raw::Repository) -> Result<Oid, QuorumError> {
+ let mut voting =
+ CommitVoting::from_targets(self.tips.values().filter_map(|(commit, kind)| {
+ (*kind == CanonicalObjectType::Commit).then_some(*commit)
+ }));
+ while let Some(targets) = voting.next_candidate() {
+ for (candidate, other) in targets {
+ let base = Oid::from(repo.merge_base(*candidate, *other)?);
+ voting.found_merge_base(MergeBase {
+ candidate,
+ other,
+ base,
+ });
}
}
+ let mut votes = voting.votes();
+
// Keep commits which pass the threshold.
- candidates.retain(|_, votes| *votes >= self.threshold());
+ votes.votes_past_threshold(self.threshold());
- let (mut longest, _) = candidates.pop_first().ok_or(QuorumError::NoCandidates {
- refname: self.refname.to_string(),
- threshold: self.threshold(),
- })?;
+ let mut longest = votes
+ .pop_first_candidate()
+ .ok_or(QuorumError::NoCandidates {
+ refname: self.refname.to_string(),
+ threshold: self.threshold(),
+ })?;
// Now that all scores are calculated, figure out what is the longest branch
// that passes the threshold. In case of divergence, return an error.
- for head in candidates.keys() {
+ for head in votes.candidates() {
let base = repo.merge_base(**head, *longest)?;
if base == *longest {
@@ -286,7 +351,7 @@ impl<'a, 'b> Canonical<'a, 'b> {
// o (base)
// |
//
- return Err(QuorumError::Diverging {
+ return Err(QuorumError::DivergingCommits {
refname: self.refname.to_string(),
threshold: self.threshold(),
base: base.into(),
@@ -295,27 +360,206 @@ impl<'a, 'b> Canonical<'a, 'b> {
});
}
}
- Ok((self.refname, (*longest).into()))
+
+ Ok((*longest).into())
+ }
+
+ /// Computes the quorum or "canonical" tip based on the tips, of `Canonical`,
+ /// and the threshold. This can be described as the latest commit that is
+ /// included in at least `threshold` histories. In case there are multiple tips
+ /// passing the threshold, and they are divergent, an error is returned.
+ ///
+ /// Also returns an error if `heads` is empty or `threshold` cannot be
+ /// satisified with the number of heads given.
+ pub fn quorum(
+ self,
+ repo: &raw::Repository,
+ ) -> Result<(Qualified<'a>, ObjectType, Oid), QuorumError> {
+ let (oid, kind) = match (self.quorum_commit(repo), self.quorum_tag()) {
+ (Ok(commit), Err(_)) => Ok((commit, ObjectType::Commit)),
+ (Err(_), Ok(tag)) => Ok((tag, ObjectType::Tag)),
+ (Ok(_), Ok(_)) => Err(QuorumError::DifferentTypes {
+ refname: self.refname.clone().to_string(),
+ }),
+ (Err(commit), Err(QuorumError::NoCandidates { .. })) => Err(commit),
+ (Err(QuorumError::NoCandidates { .. }), Err(tag)) => Err(tag),
+ (Err(err), _) => Err(err),
+ }?;
+
+ Ok((self.refname, kind, oid))
}
fn threshold(&self) -> usize {
(*self.rule.threshold()).into()
}
+}
- fn ensure_commit(from: Did, commit: Oid, working: &Repository) -> Result<Oid, ConvergesError> {
- match working.find_commit(*commit).map(|_| commit) {
- Ok(oid) => Ok(oid),
- Err(err) if err.code() == raw::ErrorCode::NotFound => Err(
- ConvergesError::missing_commit(working.path().to_path_buf(), from, commit, err),
- ),
- Err(err) => Err(ConvergesError::invalid_commit(
- working.path().to_path_buf(),
- from,
- commit,
- err,
- )),
+/// Keep track of [`Votes`] for quorums involving tag objects.
+struct TagVoting {
+ votes: Votes,
+}
+
+impl TagVoting {
+ fn from_targets(targets: impl Iterator<Item = Oid>) -> Self {
+ let votes = targets.fold(Votes::default(), |mut votes, oid| {
+ votes.vote(oid);
+ votes
+ });
+ Self { votes }
+ }
+
+ fn votes(self) -> Votes {
+ self.votes
+ }
+}
+
+/// Keep track of [`Votes`] for quorums involving commit objects.
+///
+/// Build a list of candidate commits and count how many "votes" each of them
+/// has. Commits get a point for each direct vote, as well as for being part of
+/// the ancestry of a commit given to this function.
+#[derive(Debug)]
+struct CommitVoting {
+ candidates: Vec<(Oid, Vec<Oid>)>,
+ votes: Votes,
+}
+
+impl CommitVoting {
+ /// Build the initial set voting.
+ fn from_targets(targets: impl Iterator<Item = Oid> + Clone) -> Self {
+ let ts = targets.clone();
+ let (candidates, votes) = targets.enumerate().fold(
+ (Vec::new(), Votes::default()),
+ |(mut candidates, mut votes), (i, oid)| {
+ candidates.push((oid, ts.clone().skip(i + 1).collect()));
+ votes.vote(oid);
+ (candidates, votes)
+ },
+ );
+ Self { candidates, votes }
+ }
+
+ /// Get the next candidate to be considered for ancestry votes.
+ ///
+ /// The first of each pair will be the candidate commit, which should be
+ /// compared to the other commit to see what their common merge base is. The
+ /// merge base is then recorded using [`MergeBase`] and is recorded using
+ /// [`CommitVoting::found_merge_base`].
+ fn next_candidate(&mut self) -> Option<impl Iterator<Item = (Oid, Oid)>> {
+ self.candidates
+ .pop()
+ .map(|(oid, others)| others.into_iter().map(move |other| (oid, other)))
+ }
+
+ /// Record a merge base, and add to the vote if necessary.
+ fn found_merge_base(
+ &mut self,
+ MergeBase {
+ candidate,
+ other,
+ base,
+ }: MergeBase,
+ ) {
+ // Avoid double counting the same commits
+ let is_same = candidate == other;
+ if !is_same && (base == candidate || base == other) {
+ self.votes.vote(base);
}
}
+
+ /// Finish the voting process and get the [`Votes`] from the
+ /// [`CommitVoting`].
+ fn votes(self) -> Votes {
+ self.votes
+ }
+}
+
+/// Record a merge base between `candidate` and `other`.
+struct MergeBase {
+ /// The candidate commit for the merge base.
+ candidate: Oid,
+ /// The commit that is being compared against for the merge base.
+ other: Oid,
+ /// The computed merge base commit.
+ base: Oid,
+}
+
+/// Count the number of votes per [`Oid`].
+///
+/// Note that the count cannot exceed 255, since that is the maximum number the
+/// `threshold` value can be.
+#[derive(Debug, Default, PartialEq, Eq)]
+struct Votes {
+ inner: BTreeMap<Oid, u8>,
+}
+
+impl Votes {
+ /// Increase the vote count for `oid`.
+ ///
+ /// If `oid` does not exist in the set of [`Votes`] yet, then no vote will
+ /// be added.
+ #[inline]
+ fn vote(&mut self, oid: Oid) {
+ self.safe_inc(oid, 1);
+ }
+
+ /// Filter the candidates by the ones that have a number of votes that pass
+ /// the `threshold`.
+ #[inline]
+ fn votes_past_threshold(&mut self, threshold: usize) {
+ self.inner.retain(|_, votes| *votes as usize >= threshold);
+ }
+
+ /// Get the number of candidates this set of votes has.
+ #[inline]
+ fn number_of_candidates(&self) -> usize {
+ self.inner.len()
+ }
+
+ /// Get the set candidates.
+ #[inline]
+ fn candidates(&self) -> impl Iterator<Item = &Oid> {
+ self.inner.keys()
+ }
+
+ /// Pop off the first candidate from the set of votes.
+ #[inline]
+ fn pop_first_candidate(&mut self) -> Option<Oid> {
+ self.inner.pop_first().map(|(oid, _)| oid)
+ }
+
+ #[inline]
+ fn safe_inc(&mut self, oid: Oid, n: u8) {
+ let votes = self.inner.entry(oid).or_default();
+ *votes = votes.saturating_add(n);
+ }
+}
+
+fn find_object_for(
+ did: &Did,
+ oid: Oid,
+ repo: &raw::Repository,
+) -> Result<CanonicalObjectType, FindObjectError> {
+ match repo.find_object(*oid, None) {
+ Ok(object) => object
+ .kind()
+ .and_then(CanonicalObjectType::new)
+ .ok_or_else(|| {
+ FindObjectError::invalid_object_type(
+ repo.path().to_path_buf(),
+ *did,
+ oid,
+ object.kind(),
+ )
+ }),
+ Err(err) if super::ext::is_not_found_err(&err) => Err(FindObjectError::missing_object(
+ repo.path().to_path_buf(),
+ *did,
+ oid,
+ err,
+ )),
+ Err(err) => Err(FindObjectError::find_object(oid, err)),
+ }
}
#[cfg(test)]
@@ -334,13 +578,19 @@ mod tests {
threshold: usize,
repo: &git::raw::Repository,
) -> Result<Oid, QuorumError> {
- let tips: BTreeMap<Did, Oid> = heads
+ let tips: BTreeMap<Did, (Oid, CanonicalObjectType)> = heads
.iter()
.enumerate()
.map(|(i, head)| {
let signer = Device::mock_from_seed([(i + 1) as u8; 32]);
let did = Did::from(signer.public_key());
- (did, (*head).into())
+ let kind = repo
+ .find_object(*head, None)
+ .unwrap()
+ .kind()
+ .and_then(CanonicalObjectType::new)
+ .unwrap();
+ (did, ((*head).into(), kind))
})
.collect();
@@ -360,7 +610,7 @@ mod tests {
rule: &rule,
}
.quorum(repo)
- .map(|(_, oid)| oid)
+ .map(|(_, _, oid)| oid)
}
#[test]
@@ -397,6 +647,62 @@ mod tests {
}
}
+ #[test]
+ fn test_quorum_groups() {
+ let tmp = tempfile::tempdir().unwrap();
+ let (repo, c0) = fixtures::repository(tmp.path());
+ let c0: git::Oid = c0.into();
+ let c1 = fixtures::commit("C1", &[*c0], &repo);
+ let c2 = fixtures::commit("C2", &[*c0], &repo);
+
+ eprintln!("C0: {c0}");
+ eprintln!("C1: {c1}");
+ eprintln!("C2: {c2}");
+
+ assert_matches!(
+ quorum(&[*c1, *c2, *c1, *c2], 2, &repo),
+ Err(QuorumError::DivergingCommits { .. })
+ );
+
+ assert_matches!(
+ quorum(&[*c1, *c2], 1, &repo),
+ Err(QuorumError::DivergingCommits { .. })
+ );
+ }
+
+ #[test]
+ fn test_quorum_tag() {
+ let tmp = tempfile::tempdir().unwrap();
+ let (repo, c0) = fixtures::repository(tmp.path());
+ let c0: git::Oid = c0.into();
+ let c1 = fixtures::commit("C1", &[*c0], &repo);
+ let t1 = fixtures::tag("v1", "T1", *c1, &repo);
+ let t2 = fixtures::tag("v2", "T2", *c1, &repo);
+
+ eprintln!("C0: {c0}");
+ eprintln!("C1: {c1}");
+ eprintln!("T1: {t1}");
+ eprintln!("T2: {t2}");
+
+ assert_eq!(quorum(&[*t1], 1, &repo).unwrap(), t1);
+ assert_eq!(quorum(&[*t1, *t1], 2, &repo).unwrap(), t1);
+
+ assert_matches!(
+ quorum(&[*t1, *t2], 2, &repo),
+ Err(QuorumError::NoCandidates { .. })
+ );
+
+ assert_matches!(
+ quorum(&[*t1, *c1], 1, &repo),
+ Err(QuorumError::DifferentTypes { .. })
+ );
+
+ assert_matches!(
+ quorum(&[*t1, *t2], 1, &repo),
+ Err(QuorumError::DivergingTags { .. })
+ );
+ }
+
#[test]
fn test_quorum() {
let tmp = tempfile::tempdir().unwrap();
@@ -447,15 +753,15 @@ mod tests {
// C0
assert_matches!(
quorum(&[*c1, *c2, *b2], 1, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
assert_matches!(
quorum(&[*c2, *b2], 1, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
assert_matches!(
quorum(&[*b2, *c2], 1, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
assert_matches!(
quorum(&[*c2, *b2], 2, &repo),
@@ -471,7 +777,7 @@ mod tests {
assert_eq!(quorum(&[*b2, *c2, *c2], 2, &repo).unwrap(), c2);
assert_matches!(
quorum(&[*b2, *b2, *c2, *c2], 2, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
// B2 C2 C3
@@ -500,7 +806,7 @@ mod tests {
// C0
assert_matches!(
quorum(&[*c2, *b2, *a1], 1, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
assert_matches!(
quorum(&[*c2, *b2, *a1], 2, &repo),
@@ -520,23 +826,23 @@ mod tests {
assert_eq!(quorum(&[*c0, *c1, *c2, *b2, *a1], 4, &repo).unwrap(), c0,);
assert_matches!(
quorum(&[*a1, *a1, *c2, *c2, *c1], 2, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
assert_matches!(
quorum(&[*a1, *a1, *c2, *c2, *c1], 1, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
assert_matches!(
quorum(&[*a1, *a1, *c2], 1, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
assert_matches!(
quorum(&[*b2, *b2, *c2, *c2], 1, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
assert_matches!(
quorum(&[*b2, *b2, *c2, *c2, *a1], 1, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
// M2 M1
@@ -549,11 +855,11 @@ mod tests {
assert_eq!(quorum(&[*m1], 1, &repo).unwrap(), m1);
assert_matches!(
quorum(&[*m1, *m2], 1, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
assert_matches!(
quorum(&[*m2, *m1], 1, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
assert_matches!(
quorum(&[*m1, *m2], 2, &repo),
@@ -561,11 +867,11 @@ mod tests {
);
assert_matches!(
quorum(&[*m1, *m2, *c2], 1, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
assert_matches!(
quorum(&[*m1, *a1], 1, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
assert_matches!(
quorum(&[*m1, *a1], 2, &repo),
@@ -608,7 +914,7 @@ mod tests {
// C0
assert_matches!(
quorum(&[*m1, *m2], 1, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
assert_matches!(
quorum(&[*m1, *m2], 2, &repo),
@@ -624,7 +930,7 @@ mod tests {
// C0
assert_matches!(
quorum(&[*m1, *m3], 1, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
assert_matches!(
quorum(&[*m1, *m3], 2, &repo),
@@ -632,7 +938,7 @@ mod tests {
);
assert_matches!(
quorum(&[*m3, *m1], 1, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
assert_matches!(
quorum(&[*m3, *m1], 2, &repo),
@@ -640,7 +946,7 @@ mod tests {
);
assert_matches!(
quorum(&[*m3, *m2], 1, &repo),
- Err(QuorumError::Diverging { .. })
+ Err(QuorumError::DivergingCommits { .. })
);
assert_matches!(
quorum(&[*m3, *m2], 2, &repo),
diff --git a/crates/radicle/src/git/canonical/error.rs b/crates/radicle/src/git/canonical/error.rs
new file mode 100644
index 00000000..cd97819f
--- /dev/null
+++ b/crates/radicle/src/git/canonical/error.rs
@@ -0,0 +1,204 @@
+use std::path::PathBuf;
+
+use thiserror::Error;
+
+use crate::{git::raw, git::Oid, prelude::Did};
+
+use super::CanonicalObjectType;
+
+/// Error that can occur when calculation the [`Canonical::quorum`].
+#[derive(Debug, Error)]
+pub enum QuorumError {
+ /// Could not determine a quorum [`Oid`], due to diverging tips.
+ #[error("could not determine target commit for canonical reference '{refname}', found diverging commits {longest} and {head}, with base commit {base} and threshold {threshold}")]
+ DivergingCommits {
+ refname: String,
+ threshold: usize,
+ base: Oid,
+ longest: Oid,
+ head: Oid,
+ },
+ #[error("could not determine target tag for canonical reference '{refname}', found multiple candidates with threshold {threshold}")]
+ DivergingTags {
+ refname: String,
+ threshold: usize,
+ candidates: Vec<Oid>,
+ },
+ #[error("could not determine target for canonical reference '{refname}', found objects of different types")]
+ DifferentTypes { refname: String },
+ /// Could not determine a base candidate from the given set of delegates.
+ #[error("could not determine target for canonical reference '{refname}', no object with at least {threshold} vote(s) found (threshold not met)")]
+ NoCandidates { refname: String, threshold: usize },
+ /// An error occurred from [`git2`].
+ #[error(transparent)]
+ Git(#[from] git2::Error),
+}
+
+#[derive(Debug, Error)]
+#[error("failed to check if {head} is an ancestor of {canonical} due to: {source}")]
+pub struct GraphDescendant {
+ head: Oid,
+ canonical: Oid,
+ source: raw::Error,
+}
+
+#[derive(Debug, Error)]
+#[error("the commit {commit} for {did} is missing from the repository {repo:?}")]
+pub struct MissingObject {
+ repo: PathBuf,
+ did: Did,
+ commit: Oid,
+ source: raw::Error,
+}
+
+#[derive(Debug, Error)]
+#[error("could not determine whether the commit {commit} for {did} is part of the repository {repo:?} due to: {source}")]
+pub struct InvalidObject {
+ repo: PathBuf,
+ did: Did,
+ commit: Oid,
+ source: raw::Error,
+}
+
+#[derive(Debug, Error)]
+#[error("the object {oid} for {did} in the repository {repo:?} is of unexpected type {kind:?}")]
+pub struct InvalidObjectType {
+ repo: PathBuf,
+ did: Did,
+ oid: Oid,
+ kind: Option<git2::ObjectType>,
+}
+
+#[derive(Debug, Error)]
+#[error("the object {oid} in the repository {repo:?} is of unexpected type {found} and was expected to be {expected}")]
+pub struct MismatchedObject {
+ repo: PathBuf,
+ oid: Oid,
+ found: CanonicalObjectType,
+ expected: CanonicalObjectType,
+}
+
+#[derive(Debug, Error)]
+pub enum CanonicalError {
+ #[error(transparent)]
+ InvalidObjectType(#[from] InvalidObjectType),
+ #[error(transparent)]
+ MissingObject(#[from] MissingObject),
+ #[error("failed to find object {oid} due to: {source}")]
+ FindObject { oid: Oid, source: git2::Error },
+ #[error("failed to find reference {name} due to: {source}")]
+ FindReference { name: String, source: git2::Error },
+}
+
+impl CanonicalError {
+ pub(super) fn invalid_object_type(
+ repo: PathBuf,
+ did: Did,
+ oid: Oid,
+ kind: Option<git2::ObjectType>,
+ ) -> Self {
+ InvalidObjectType {
+ repo,
+ did,
+ oid,
+ kind,
+ }
+ .into()
+ }
+
+ pub(super) fn missing_object(repo: PathBuf, did: Did, oid: Oid, err: git2::Error) -> Self {
+ MissingObject {
+ repo,
+ did,
+ commit: oid,
+ source: err,
+ }
+ .into()
+ }
+
+ pub(super) fn find_object(oid: Oid, err: git2::Error) -> Self {
+ Self::FindObject { oid, source: err }
+ }
+
+ pub(crate) fn find_reference(name: &str, e: git2::Error) -> CanonicalError {
+ Self::FindReference {
+ name: name.to_string(),
+ source: e,
+ }
+ }
+}
+
+#[derive(Debug, Error)]
+pub enum FindObjectError {
+ #[error(transparent)]
+ InvalidObjectType(#[from] InvalidObjectType),
+ #[error(transparent)]
+ MissingObject(#[from] MissingObject),
+ #[error("failed to find object {oid} due to: {source}")]
+ FindObject { oid: Oid, source: git2::Error },
+}
+
+impl FindObjectError {
+ pub(super) fn find_object(oid: Oid, err: git2::Error) -> Self {
+ Self::FindObject { oid, source: err }
+ }
+
+ pub(super) fn missing_object(repo: PathBuf, did: Did, oid: Oid, err: git2::Error) -> Self {
+ MissingObject {
+ repo,
+ did,
+ commit: oid,
+ source: err,
+ }
+ .into()
+ }
+
+ pub(super) fn invalid_object_type(
+ repo: PathBuf,
+ did: Did,
+ oid: Oid,
+ kind: Option<git2::ObjectType>,
+ ) -> Self {
+ InvalidObjectType {
+ repo,
+ did,
+ oid,
+ kind,
+ }
+ .into()
+ }
+}
+
+#[derive(Debug, Error)]
+pub enum ConvergesError {
+ #[error(transparent)]
+ GraphDescendant(#[from] GraphDescendant),
+ #[error(transparent)]
+ MismatchedObject(#[from] MismatchedObject),
+ #[error(transparent)]
+ FindObjectError(#[from] FindObjectError),
+}
+
+impl ConvergesError {
+ pub(super) fn graph_descendant(head: Oid, canonical: Oid, source: raw::Error) -> Self {
+ Self::GraphDescendant(GraphDescendant {
+ head,
+ canonical,
+ source,
+ })
+ }
+
+ pub(super) fn mismatched_object(
+ repo: PathBuf,
+ oid: Oid,
+ found: CanonicalObjectType,
+ expected: CanonicalObjectType,
+ ) -> Self {
+ Self::MismatchedObject(MismatchedObject {
+ repo,
+ oid,
+ found,
+ expected,
+ })
+ }
+}
diff --git a/crates/radicle/src/git/canonical/rules.rs b/crates/radicle/src/git/canonical/rules.rs
index da9bd7fd..59233456 100644
--- a/crates/radicle/src/git/canonical/rules.rs
+++ b/crates/radicle/src/git/canonical/rules.rs
@@ -20,6 +20,7 @@ use serde_json as json;
use thiserror::Error;
use crate::git;
+use crate::git::canonical;
use crate::git::canonical::Canonical;
use crate::git::fmt::{refname, RefString};
use crate::git::refspec::QualifiedPattern;
@@ -634,9 +635,9 @@ impl Rules {
&'a self,
refname: Qualified<'b>,
repo: &Repository,
- ) -> Result<Option<Canonical<'b, 'a>>, git::raw::Error> {
+ ) -> Result<Option<Canonical<'b, 'a>>, canonical::error::CanonicalError> {
if let Some((_, rule)) = self.matches(&refname).next() {
- Ok(Some(Canonical::new(repo, refname, rule)?))
+ Ok(Some(Canonical::new(&repo.backend, refname, rule)?))
} else {
Ok(None)
}
@@ -1206,7 +1207,7 @@ mod tests {
canonical
.quorum(&repo)
.unwrap_or_else(|e| panic!("quorum error for {refname}: {e}")),
- (refname, oid),
+ (refname, git::raw::ObjectType::Tag, oid),
)
}
}
diff --git a/crates/radicle/src/storage.rs b/crates/radicle/src/storage.rs
index 9f1d1768..7f487df9 100644
--- a/crates/radicle/src/storage.rs
+++ b/crates/radicle/src/storage.rs
@@ -117,7 +117,7 @@ pub enum RepositoryError {
#[error(transparent)]
GitExt(#[from] git_ext::Error),
#[error(transparent)]
- Quorum(#[from] canonical::QuorumError),
+ Quorum(#[from] canonical::error::QuorumError),
#[error(transparent)]
Refs(#[from] refs::Error),
#[error("missing canonical reference rule for default branch")]
@@ -126,6 +126,8 @@ pub enum RepositoryError {
DefaultBranchRule(#[from] doc::DefaultBranchRuleError),
#[error("failed to get canonical reference rules: {0}")]
CanonicalRefs(#[from] doc::CanonicalRefsError),
+ #[error(transparent)]
+ Canonical(#[from] canonical::error::CanonicalError),
}
impl RepositoryError {
diff --git a/crates/radicle/src/storage/git.rs b/crates/radicle/src/storage/git.rs
index 46a022df..be025611 100644
--- a/crates/radicle/src/storage/git.rs
+++ b/crates/radicle/src/storage/git.rs
@@ -720,14 +720,17 @@ impl ReadRepository for Repository {
for r in self.backend.references_glob(pattern)? {
let r = r?;
- let c = r.peel_to_commit()?;
+
+ let Some(oid) = r.resolve()?.target() else {
+ continue;
+ };
if let Some(name) = r
.name()
.and_then(|n| git::RefStr::try_from_str(n).ok())
.and_then(git::Qualified::from_refstr)
{
- refs.push((name.to_owned(), c.id().into()));
+ refs.push((name.to_owned(), oid.into()));
}
}
Ok(refs)
@@ -761,6 +764,7 @@ impl ReadRepository for Repository {
.canonical(refname, self)?
.ok_or(RepositoryError::MissingBranchRule)?
.quorum(self.raw())?)
+ .map(|(refname, _, oid)| (refname, oid))
}
fn identity_head(&self) -> Result<Oid, RepositoryError> {
diff --git a/crates/radicle/src/test/fixtures.rs b/crates/radicle/src/test/fixtures.rs
index 6b046d1b..cd9f92ab 100644
--- a/crates/radicle/src/test/fixtures.rs
+++ b/crates/radicle/src/test/fixtures.rs
@@ -148,6 +148,22 @@ pub fn commit(msg: &str, parents: &[git2::Oid], repo: &git2::Repository) -> git:
.into()
}
+/// Create an (annotated) tag of the given commit.
+pub fn tag(name: &str, message: &str, commit: git2::Oid, repo: &git2::Repository) -> git::Oid {
+ let target = repo
+ .find_object(commit, Some(git2::ObjectType::Commit))
+ .unwrap();
+ let tagger = git2::Signature::new(
+ "anonymous",
+ "anonymous@radicle.xyz",
+ &git2::Time::new(RADICLE_EPOCH, 0),
+ )
+ .unwrap();
+ repo.tag(name, &target, &tagger, message, false)
+ .unwrap()
+ .into()
+}
+
/// Populate a repository with commits, branches and blobs.
pub fn populate(repo: &git2::Repository, scale: usize) -> Vec<git::Qualified> {
assert!(
Exit code: 0
shell: 'cargo --version rustc --version cargo fmt --check cargo clippy --all-targets --workspace -- --deny warnings cargo build --all-targets --workspace cargo doc --workspace --no-deps cargo test --workspace --no-fail-fast '
Commands:
$ podman run --name 9ffd5085-5f5f-4839-84f2-2fbd061cca0a -v /opt/radcis/ci.rad.levitte.org/cci/state/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/s:/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/s:ro -v /opt/radcis/ci.rad.levitte.org/cci/state/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w:/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w -w /9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w -v /opt/radcis/ci.rad.levitte.org/.radicle:/${id}/.radicle:ro -e RAD_HOME=/${id}/.radicle rust:bookworm bash /9ffd5085-5f5f-4839-84f2-2fbd061cca0a/s/script.sh
+ cargo --version
info: syncing channel updates for '1.88-x86_64-unknown-linux-gnu'
info: latest update on 2025-06-26, rust version 1.88.0 (6b00bc388 2025-06-23)
info: downloading component 'cargo'
info: downloading component 'clippy'
info: downloading component 'rust-docs'
info: downloading component 'rust-src'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'rustfmt'
info: installing component 'cargo'
info: installing component 'clippy'
info: installing component 'rust-docs'
info: installing component 'rust-src'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'rustfmt'
cargo 1.88.0 (873a06493 2025-05-10)
+ rustc --version
rustc 1.88.0 (6b00bc388 2025-06-23)
+ cargo fmt --check
+ cargo clippy --all-targets --workspace -- --deny warnings
Updating crates.io index
Downloading crates ...
Downloaded chacha20 v0.9.1
Downloaded byteorder v1.5.0
Downloaded aes-gcm v0.10.3
Downloaded qcheck-macros v1.0.0
Downloaded git-ref-format v0.3.1
Downloaded radicle-git-ext v0.8.1
Downloaded streaming-iterator v0.1.9
Downloaded salsa20 v0.10.2
Downloaded ssh-key v0.6.6
Downloaded signal-hook v0.3.18
Downloaded similar v2.5.0
Downloaded version_check v0.9.4
Downloaded schemars v1.0.4
Downloaded sqlite v0.32.0
Downloaded signals_receipts v0.2.0
Downloaded tinystr v0.7.6
Downloaded ref-cast v1.0.24
Downloaded cypheraddr v0.4.0
Downloaded form_urlencoded v1.2.1
Downloaded amplify v4.6.0
Downloaded generic-array v0.14.7
Downloaded serde-untagged v0.1.7
Downloaded rsa v0.9.6
Downloaded stable_deref_trait v1.2.0
Downloaded spin v0.9.8
Downloaded jobserver v0.1.31
Downloaded keccak v0.1.5
Downloaded thiserror-impl v1.0.69
Downloaded crossbeam-utils v0.8.19
Downloaded rustix v1.0.7
Downloaded tree-sitter-highlight v0.24.4
Downloaded tree-sitter-c v0.23.2
Downloaded typeid v1.0.3
Downloaded localtime v1.3.1
Downloaded lexopt v0.3.0
Downloaded uuid-simd v0.8.0
Downloaded sha2 v0.10.8
Downloaded home v0.5.9
Downloaded gix-packetline v0.18.4
Downloaded derive_more-impl v2.0.1
Downloaded iana-time-zone v0.1.60
Downloaded hmac v0.12.1
Downloaded normalize-line-endings v0.3.0
Downloaded num v0.4.3
Downloaded universal-hash v0.5.1
Downloaded unicode-width v0.1.11
Downloaded outref v0.5.2
Downloaded gix-date v0.9.4
Downloaded inout v0.1.3
Downloaded gix-lock v15.0.1
Downloaded itoa v1.0.11
Downloaded writeable v0.5.5
Downloaded unicode-display-width v0.3.0
Downloaded parking_lot_core v0.9.9
Downloaded unicode-ident v1.0.12
Downloaded gix-revision v0.31.1
Downloaded icu_provider_macros v1.5.0
Downloaded gix-traverse v0.43.1
Downloaded num-bigint v0.4.6
Downloaded typenum v1.17.0
Downloaded zeroize v1.7.0
Downloaded icu_normalizer v1.5.0
Downloaded utf8_iter v1.0.4
Downloaded once_cell v1.21.3
Downloaded idna_adapter v1.2.0
Downloaded utf8parse v0.2.1
Downloaded mio v1.0.4
Downloaded gix-quote v0.4.15
Downloaded git-ref-format-macro v0.3.1
Downloaded gix-credentials v0.26.0
Downloaded gix-negotiate v0.17.0
Downloaded litemap v0.7.5
Downloaded gix-actor v0.33.2
Downloaded git-ref-format-core v0.3.1
Downloaded zerovec-derive v0.10.3
Downloaded gix-tempfile v15.0.0
Downloaded gix-commitgraph v0.25.1
Downloaded icu_provider v1.5.0
Downloaded gix-sec v0.10.12
Downloaded unicode-segmentation v1.11.0
Downloaded gix-config-value v0.14.12
Downloaded gix-shallow v0.1.0
Downloaded gix-odb v0.66.0
Downloaded gix-object v0.46.1
Downloaded uuid v1.16.0
Downloaded memmap2 v0.9.4
Downloaded tree-sitter v0.24.4
Downloaded miniz_oxide v0.8.8
Downloaded indexmap v2.2.6
Downloaded jiff v0.2.1
Downloaded p521 v0.13.3
Downloaded icu_normalizer_data v1.5.1
Downloaded num-bigint-dig v0.8.4
Downloaded syn v1.0.109
Downloaded jsonschema v0.30.0
Downloaded winnow v0.6.26
Downloaded idna v1.0.3
Downloaded tree-sitter-python v0.23.4
Downloaded libm v0.2.8
Downloaded zerovec v0.10.4
Downloaded icu_properties_data v1.5.1
Downloaded git2 v0.19.0
Downloaded zerocopy v0.7.35
Downloaded vcpkg v0.2.15
Downloaded tree-sitter-bash v0.23.3
Downloaded memchr v2.7.2
Downloaded tree-sitter-md v0.3.2
Downloaded tree-sitter-rust v0.23.2
Downloaded icu_properties v1.5.1
Downloaded gix-ref v0.49.1
Downloaded gix-command v0.4.1
Downloaded gix-refspec v0.27.0
Downloaded gix-revwalk v0.17.0
Downloaded sha3 v0.10.8
Downloaded zerofrom-derive v0.1.6
Downloaded num-complex v0.4.6
Downloaded tree-sitter-ruby v0.23.1
Downloaded litrs v0.4.1
Downloaded icu_locid v1.5.0
Downloaded gix-chunk v0.4.11
Downloaded tree-sitter-typescript v0.23.2
Downloaded unicode-normalization v0.1.23
Downloaded gix-protocol v0.47.0
Downloaded num-rational v0.4.2
Downloaded num-integer v0.1.46
Downloaded zerofrom v0.1.6
Downloaded num-traits v0.2.19
Downloaded libc v0.2.174
Downloaded num-iter v0.1.45
Downloaded gix-transport v0.44.0
Downloaded inquire v0.7.5
Downloaded utf16_iter v1.0.5
Downloaded num-cmp v0.1.0
Downloaded gix-path v0.10.15
Downloaded gix-trace v0.1.12
Downloaded icu_locid_transform v1.5.0
Downloaded tree-sitter-go v0.23.4
Downloaded tree-sitter-toml-ng v0.6.0
Downloaded maybe-async v0.2.10
Downloaded log v0.4.27
Downloaded gix-prompt v0.9.1
Downloaded icu_locid_transform_data v1.5.1
Downloaded url v2.5.4
Downloaded yansi v0.5.1
Downloaded parking_lot v0.12.3
Downloaded hashbrown v0.14.3
Downloaded yoke v0.7.5
Downloaded walkdir v2.5.0
Downloaded vsimd v0.8.0
Downloaded serde v1.0.219
Downloaded io-reactor v0.5.2
Downloaded gix-hash v0.15.1
Downloaded gix-fs v0.12.1
Downloaded gix-features v0.39.1
Downloaded xattr v1.3.1
Downloaded tree-sitter-json v0.24.8
Downloaded tree-sitter-html v0.23.2
Downloaded gix-hashtable v0.6.0
Downloaded gix-diff v0.49.0
Downloaded pbkdf2 v0.12.2
Downloaded mio v0.8.11
Downloaded icu_collections v1.5.0
Downloaded gix-pack v0.56.0
Downloaded linux-raw-sys v0.4.13
Downloaded yoke-derive v0.7.5
Downloaded write16 v1.0.0
Downloaded tree-sitter-language v0.1.2
Downloaded nonempty v0.9.0
Downloaded nonempty v0.5.0
Downloaded gix-url v0.28.2
Downloaded p384 v0.13.0
Downloaded gix-utils v0.1.14
Downloaded group v0.13.0
Downloaded gix-validate v0.9.4
Downloaded netservices v0.8.0
Downloaded lock_api v0.4.11
Downloaded sec1 v0.7.3
Downloaded noise-framework v0.4.0
Downloaded multibase v0.9.1
Downloaded thiserror v1.0.69
Downloaded scopeguard v1.2.0
Downloaded cyphernet v0.5.2
Downloaded bstr v1.9.1
Downloaded newline-converter v0.3.0
Downloaded crossterm v0.29.0
Downloaded serde_derive_internals v0.29.1
Downloaded libgit2-sys v0.17.0+1.8.1
Downloaded emojis v0.6.4
Downloaded opaque-debug v0.3.1
Downloaded schemars_derive v1.0.4
Downloaded ssh-cipher v0.2.0
Downloaded flate2 v1.1.1
Downloaded fancy-regex v0.14.0
Downloaded crossterm v0.25.0
Downloaded crossbeam-channel v0.5.15
Downloaded lazy_static v1.5.0
Downloaded ssh-encoding v0.2.0
Downloaded siphasher v1.0.1
Downloaded linux-raw-sys v0.9.4
Downloaded sem_safe v0.2.0
Downloaded p256 v0.13.2
Downloaded fraction v0.15.3
Downloaded tinyvec_macros v0.1.1
Downloaded tinyvec v1.6.0
Downloaded timeago v0.4.2
Downloaded thiserror-impl v2.0.12
Downloaded thiserror v2.0.12
Downloaded tar v0.4.40
Downloaded spki v0.7.3
Downloaded socks5-client v0.4.1
Downloaded socket2 v0.5.7
Downloaded snapbox v0.4.17
Downloaded smallvec v1.13.2
Downloaded scrypt v0.11.0
Downloaded rfc6979 v0.4.0
Downloaded fluent-uri v0.3.2
Downloaded filetime v0.2.23
Downloaded email_address v0.2.9
Downloaded diff v0.1.13
Downloaded derive_more v2.0.1
Downloaded data-encoding-macro-internal v0.1.12
Downloaded crypto-common v0.1.6
Downloaded crc32fast v1.4.0
Downloaded sqlite3-src v0.5.1
Downloaded tree-sitter-css v0.23.1
Downloaded tempfile v3.10.1
Downloaded synstructure v0.13.1
Downloaded subtle v2.5.0
Downloaded serde_json v1.0.140
Downloaded fastrand v2.1.0
Downloaded fast-glob v0.3.3
Downloaded errno v0.3.13
Downloaded cc v1.2.2
Downloaded equivalent v1.0.1
Downloaded document-features v0.2.11
Downloaded data-encoding-macro v0.1.14
Downloaded cpufeatures v0.2.12
Downloaded borrow-or-share v0.2.2
Downloaded fxhash v0.2.1
Downloaded snapbox-macros v0.3.8
Downloaded ryu v1.0.17
Downloaded erased-serde v0.4.6
Downloaded either v1.11.0
Downloaded blowfish v0.9.1
Downloaded shlex v1.3.0
Downloaded chrono v0.4.38
Downloaded bytes v1.10.1
Downloaded shell-words v1.1.0
Downloaded sha1_smol v1.0.0
Downloaded serde_derive v1.0.219
Downloaded displaydoc v0.2.5
Downloaded der v0.7.9
Downloaded chacha20poly1305 v0.10.1
Downloaded cbc v0.1.2
Downloaded bytesize v2.0.1
Downloaded faster-hex v0.9.0
Downloaded ct-codecs v1.1.1
Downloaded base64 v0.21.7
Downloaded rustix v0.38.34
Downloaded ghash v0.5.1
Downloaded elliptic-curve v0.13.8
Downloaded ecdsa v0.16.9
Downloaded crypto-bigint v0.5.5
Downloaded bytecount v0.6.8
Downloaded ec25519 v0.1.0
Downloaded digest v0.10.7
Downloaded convert_case v0.7.1
Downloaded cfg-if v1.0.0
Downloaded ctr v0.9.2
Downloaded signature v2.2.0
Downloaded signal-hook-registry v1.4.5
Downloaded ff v0.13.0
Downloaded escargot v0.5.10
Downloaded cyphergraphy v0.3.0
Downloaded bloomy v1.2.0
Downloaded regex-automata v0.4.9
Downloaded regex v1.11.1
Downloaded rand_core v0.6.4
Downloaded colorchoice v1.0.0
Downloaded bit-set v0.8.0
Downloaded libz-sys v1.1.16
Downloaded bcrypt-pbkdf v0.10.0
Downloaded base64 v0.22.1
Downloaded base-x v0.2.11
Downloaded ascii v1.1.0
Downloaded siphasher v0.3.11
Downloaded signal-hook-mio v0.2.4
Downloaded getrandom v0.2.15
Downloaded ed25519 v1.5.3
Downloaded data-encoding v2.5.0
Downloaded const-oid v0.9.6
Downloaded colored v2.1.0
Downloaded cipher v0.4.4
Downloaded bit-vec v0.8.0
Downloaded regex-syntax v0.8.5
Downloaded referencing v0.30.0
Downloaded rand v0.8.5
Downloaded bitflags v2.9.1
Downloaded anstyle v1.0.6
Downloaded amplify_num v0.5.2
Downloaded syn v2.0.89
Downloaded signature v1.6.4
Downloaded block-padding v0.3.3
Downloaded block-buffer v0.10.4
Downloaded base16ct v0.2.0
Downloaded arc-swap v1.7.1
Downloaded anstyle-query v1.0.2
Downloaded popol v3.0.0
Downloaded radicle-surf v0.22.0
Downloaded dyn-clone v1.0.17
Downloaded base64ct v1.6.0
Downloaded base32 v0.4.0
Downloaded autocfg v1.2.0
Downloaded anyhow v1.0.82
Downloaded anstyle-parse v0.2.3
Downloaded amplify_derive v4.0.0
Downloaded aho-corasick v1.1.3
Downloaded ahash v0.8.11
Downloaded prodash v29.0.2
Downloaded pretty_assertions v1.4.0
Downloaded anstream v0.6.13
Downloaded aead v0.5.2
Downloaded ref-cast-impl v1.0.24
Downloaded quote v1.0.36
Downloaded qcheck v1.0.0
Downloaded proc-macro2 v1.0.92
Downloaded proc-macro-error v1.0.4
Downloaded poly1305 v0.8.0
Downloaded pkg-config v0.3.30
Downloaded pkcs8 v0.10.2
Downloaded percent-encoding v2.3.1
Downloaded pem-rfc7468 v0.7.0
Downloaded aes v0.8.4
Downloaded sqlite3-sys v0.15.2
Downloaded same-file v1.0.6
Downloaded rand_chacha v0.3.1
Downloaded radicle-std-ext v0.1.0
Downloaded polyval v0.6.2
Downloaded pkcs1 v0.7.5
Downloaded phf v0.11.3
Downloaded base64 v0.13.1
Downloaded proc-macro-error-attr v1.0.4
Downloaded primeorder v0.13.6
Downloaded ppv-lite86 v0.2.17
Downloaded phf_shared v0.11.3
Downloaded bitflags v1.3.2
Downloaded amplify_syn v2.0.1
Downloaded adler2 v2.0.0
Compiling libc v0.2.174
Compiling proc-macro2 v1.0.92
Compiling unicode-ident v1.0.12
Checking cfg-if v1.0.0
Compiling shlex v1.3.0
Compiling version_check v0.9.4
Compiling serde v1.0.219
Checking memchr v2.7.2
Compiling quote v1.0.36
Compiling autocfg v1.2.0
Checking getrandom v0.2.15
Compiling syn v2.0.89
Compiling jobserver v0.1.31
Checking smallvec v1.13.2
Checking aho-corasick v1.1.3
Checking regex-syntax v0.8.5
Compiling cc v1.2.2
Compiling typenum v1.17.0
Checking regex-automata v0.4.9
Compiling generic-array v0.14.7
Checking rand_core v0.6.4
Checking log v0.4.27
Checking fastrand v2.1.0
Checking crypto-common v0.1.6
Compiling lock_api v0.4.11
Compiling parking_lot_core v0.9.9
Checking bitflags v2.9.1
Checking bstr v1.9.1
Compiling synstructure v0.13.1
Checking scopeguard v1.2.0
Checking subtle v2.5.0
Checking stable_deref_trait v1.2.0
Checking parking_lot v0.12.3
Checking once_cell v1.21.3
Checking tinyvec_macros v0.1.1
Compiling syn v1.0.109
Checking tinyvec v1.6.0
Checking zeroize v1.7.0
Checking cpufeatures v0.2.12
Checking litemap v0.7.5
Checking unicode-normalization v0.1.23
Checking writeable v0.5.5
Compiling icu_locid_transform_data v1.5.1
Compiling crc32fast v1.4.0
Compiling serde_derive v1.0.219
Compiling zerofrom-derive v0.1.6
Compiling yoke-derive v0.7.5
Compiling zerovec-derive v0.10.3
Checking zerofrom v0.1.6
Checking yoke v0.7.5
Compiling displaydoc v0.2.5
Checking zerovec v0.10.4
Compiling thiserror v2.0.12
Compiling thiserror-impl v2.0.12
Compiling icu_provider_macros v1.5.0
Checking tinystr v0.7.6
Checking icu_locid v1.5.0
Checking block-padding v0.3.3
Compiling icu_properties_data v1.5.1
Checking icu_provider v1.5.0
Checking inout v0.1.3
Checking block-buffer v0.10.4
Compiling icu_normalizer_data v1.5.1
Checking itoa v1.0.11
Compiling pkg-config v0.3.30
Checking hashbrown v0.14.3
Checking digest v0.10.7
Checking cipher v0.4.4
Checking icu_locid_transform v1.5.0
Checking icu_collections v1.5.0
Checking utf8_iter v1.0.4
Compiling thiserror v1.0.69
Checking utf16_iter v1.0.5
Checking write16 v1.0.0
Checking icu_properties v1.5.1
Compiling thiserror-impl v1.0.69
Checking percent-encoding v2.3.1
Compiling rustix v0.38.34
Checking linux-raw-sys v0.4.13
Checking sha2 v0.10.8
Checking form_urlencoded v1.2.1
Checking universal-hash v0.5.1
Checking opaque-debug v0.3.1
Compiling vcpkg v0.2.15
Checking icu_normalizer v1.5.0
Compiling amplify_syn v2.0.1
Checking idna_adapter v1.2.0
Checking idna v1.0.3
Compiling libz-sys v1.1.16
Checking url v2.5.4
Compiling data-encoding v2.5.0
Checking gix-trace v0.1.12
Compiling amplify_derive v4.0.0
Checking tempfile v3.10.1
Compiling data-encoding-macro-internal v0.1.12
Checking amplify_num v0.5.2
Checking signature v1.6.4
Checking ascii v1.1.0
Checking data-encoding-macro v0.1.14
Checking ed25519 v1.5.3
Checking faster-hex v0.9.0
Compiling libgit2-sys v0.17.0+1.8.1
Checking aead v0.5.2
Compiling num-traits v0.2.19
Compiling proc-macro-error-attr v1.0.4
Checking ct-codecs v1.1.1
Checking byteorder v1.5.0
Checking base-x v0.2.11
Checking ec25519 v0.1.0
Checking multibase v0.9.1
Checking poly1305 v0.8.0
Checking amplify v4.6.0
Checking chacha20 v0.9.1
Checking gix-utils v0.1.14
Compiling proc-macro-error v1.0.4
Checking adler2 v2.0.0
Checking cyphergraphy v0.3.0
Checking miniz_oxide v0.8.8
Checking gix-hash v0.15.1
Compiling crossbeam-utils v0.8.19
Checking same-file v1.0.6
Checking keccak v0.1.5
Checking walkdir v2.5.0
Checking sha3 v0.10.8
Checking flate2 v1.1.1
Compiling git-ref-format-core v0.3.1
Checking polyval v0.6.2
Checking hmac v0.12.1
Compiling sqlite3-src v0.5.1
Checking prodash v29.0.2
Checking equivalent v1.0.1
Compiling serde_json v1.0.140
Checking base32 v0.4.0
Checking ppv-lite86 v0.2.17
Checking base64ct v1.6.0
Checking sha1_smol v1.0.0
Checking pem-rfc7468 v0.7.0
Checking gix-features v0.39.1
Checking rand_chacha v0.3.1
Checking cypheraddr v0.4.0
Checking indexmap v2.2.6
Compiling git-ref-format-macro v0.3.1
Checking pbkdf2 v0.12.2
Checking ghash v0.5.1
Checking chacha20poly1305 v0.10.1
Checking aes v0.8.4
Checking ctr v0.9.2
Checking ryu v1.0.17
Checking aes-gcm v0.10.3
Checking git-ref-format v0.3.1
Checking crossbeam-channel v0.5.15
Checking noise-framework v0.4.0
Checking socks5-client v0.4.1
Checking rand v0.8.5
Checking ssh-encoding v0.2.0
Checking blowfish v0.9.1
Checking cbc v0.1.2
Compiling ref-cast v1.0.24
Checking radicle-std-ext v0.1.0
Checking home v0.5.9
Checking ssh-cipher v0.2.0
Checking gix-path v0.10.15
Checking bcrypt-pbkdf v0.10.0
Checking cyphernet v0.5.2
Compiling ref-cast-impl v1.0.24
Checking signature v2.2.0
Checking ssh-key v0.6.6
Checking qcheck v1.0.0
Checking radicle-ssh v0.9.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-ssh)
Checking lazy_static v1.5.0
Checking dyn-clone v1.0.17
Compiling typeid v1.0.3
Checking siphasher v1.0.1
Checking nonempty v0.9.0
Compiling serde_derive_internals v0.29.1
Checking radicle-dag v0.10.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-dag)
Checking erased-serde v0.4.6
Checking iana-time-zone v0.1.60
Compiling schemars_derive v1.0.4
Checking jiff v0.2.1
Checking schemars v1.0.4
Checking chrono v0.4.38
Checking gix-date v0.9.4
Checking serde-untagged v0.1.7
Checking colored v2.1.0
Checking bytesize v2.0.1
Checking localtime v1.3.1
Checking winnow v0.6.26
Checking tree-sitter-language v0.1.2
Checking fast-glob v0.3.3
Checking base64 v0.21.7
Checking gix-hashtable v0.6.0
Checking gix-validate v0.9.4
Checking memmap2 v0.9.4
Compiling anyhow v1.0.82
Checking gix-actor v0.33.2
Checking gix-object v0.46.1
Checking gix-chunk v0.4.11
Checking anstyle-query v1.0.2
Checking gix-commitgraph v0.25.1
Checking gix-fs v0.12.1
Checking errno v0.3.13
Checking gix-revwalk v0.17.0
Checking sem_safe v0.2.0
Checking signals_receipts v0.2.0
Checking gix-tempfile v15.0.0
Compiling signal-hook v0.3.18
Checking signal-hook-registry v1.4.5
Checking shell-words v1.1.0
Checking gix-command v0.4.1
Checking radicle-signals v0.11.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-signals)
Checking mio v1.0.4
Checking mio v0.8.11
Compiling tree-sitter v0.24.4
Compiling unicode-segmentation v1.11.0
Compiling convert_case v0.7.1
Checking signal-hook-mio v0.2.4
Checking gix-lock v15.0.1
Checking gix-config-value v0.14.12
Checking gix-url v0.28.2
Checking gix-quote v0.4.15
Checking regex v1.11.1
Checking gix-sec v0.10.12
Compiling rustix v1.0.7
Checking gix-prompt v0.9.1
Compiling derive_more-impl v2.0.1
Compiling xattr v1.3.1
Checking sqlite3-sys v0.15.2
Checking sqlite v0.32.0
Compiling filetime v0.2.23
Checking gix-traverse v0.43.1
Checking gix-revision v0.31.1
Checking gix-diff v0.49.0
Checking gix-packetline v0.18.4
Checking lexopt v0.3.0
Checking utf8parse v0.2.1
Checking bitflags v1.3.2
Checking linux-raw-sys v0.9.4
Compiling litrs v0.4.1
Checking crossterm v0.25.0
Checking anstyle-parse v0.2.3
Compiling document-features v0.2.11
Checking gix-transport v0.44.0
Checking derive_more v2.0.1
Checking gix-pack v0.56.0
Checking gix-refspec v0.27.0
Compiling tar v0.4.40
Checking gix-credentials v0.26.0
Checking newline-converter v0.3.0
Checking gix-ref v0.49.1
Checking gix-shallow v0.1.0
Checking gix-negotiate v0.17.0
Checking fxhash v0.2.1
Compiling maybe-async v0.2.10
Checking anstyle v1.0.6
Checking unicode-width v0.1.11
Checking streaming-iterator v0.1.9
Checking colorchoice v1.0.0
Checking arc-swap v1.7.1
Checking gix-odb v0.66.0
Checking anstream v0.6.13
Checking gix-protocol v0.47.0
Checking inquire v0.7.5
Compiling radicle-surf v0.22.0
Checking crossterm v0.29.0
Checking unicode-display-width v0.3.0
Compiling tree-sitter-css v0.23.1
Compiling tree-sitter-c v0.23.2
Compiling tree-sitter-typescript v0.23.2
Compiling tree-sitter-bash v0.23.3
Compiling tree-sitter-html v0.23.2
Compiling tree-sitter-go v0.23.4
Compiling tree-sitter-toml-ng v0.6.0
Compiling tree-sitter-md v0.3.2
Compiling tree-sitter-ruby v0.23.1
Compiling tree-sitter-json v0.24.8
Compiling tree-sitter-rust v0.23.2
Compiling tree-sitter-python v0.23.4
Checking either v1.11.0
Checking snapbox-macros v0.3.8
Checking salsa20 v0.10.2
Checking nonempty v0.5.0
Checking normalize-line-endings v0.3.0
Checking siphasher v0.3.11
Checking base64 v0.13.1
Checking similar v2.5.0
Compiling radicle-cli v0.14.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-cli)
Checking bloomy v1.2.0
Checking snapbox v0.4.17
Checking scrypt v0.11.0
Checking tree-sitter-highlight v0.24.4
Checking popol v3.0.0
Checking timeago v0.4.2
Checking bytes v1.10.1
Checking io-reactor v0.5.2
Checking socket2 v0.5.7
Checking diff v0.1.13
Compiling radicle-node v0.12.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-node)
Checking yansi v0.5.1
Checking netservices v0.8.0
Checking pretty_assertions v1.4.0
Checking num-integer v0.1.46
Compiling escargot v0.5.10
Checking radicle-systemd v0.9.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-systemd)
Compiling qcheck-macros v1.0.0
Checking num-bigint v0.4.6
Compiling ahash v0.8.11
Compiling radicle-remote-helper v0.11.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-remote-helper)
Checking num-iter v0.1.45
Checking num-complex v0.4.6
Checking borrow-or-share v0.2.2
Checking num-rational v0.4.2
Checking zerocopy v0.7.35
Checking bit-vec v0.8.0
Checking num v0.4.3
Checking fluent-uri v0.3.2
Checking bit-set v0.8.0
Checking phf_shared v0.11.3
Checking outref v0.5.2
Checking vsimd v0.8.0
Checking uuid v1.16.0
Checking referencing v0.30.0
Checking phf v0.11.3
Checking uuid-simd v0.8.0
Checking fancy-regex v0.14.0
Checking fraction v0.15.3
Checking email_address v0.2.9
Checking bytecount v0.6.8
Checking num-cmp v0.1.0
Checking base64 v0.22.1
Checking emojis v0.6.4
Checking jsonschema v0.30.0
Checking git2 v0.19.0
Checking radicle-git-ext v0.8.1
Checking radicle-term v0.13.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-term)
Checking radicle-crypto v0.12.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-crypto)
Checking radicle-cob v0.14.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-cob)
Checking radicle v0.16.1 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle)
Checking radicle-fetch v0.12.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-fetch)
Checking radicle-cli-test v0.11.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-cli-test)
Checking radicle-schemars v0.1.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-schemars)
Checking radicle-protocol v0.1.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-protocol)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 42.50s
+ cargo build --all-targets --workspace
Compiling cfg-if v1.0.0
Compiling libc v0.2.174
Compiling shlex v1.3.0
Compiling memchr v2.7.2
Compiling serde v1.0.219
Compiling smallvec v1.13.2
Compiling jobserver v0.1.31
Compiling getrandom v0.2.15
Compiling cc v1.2.2
Compiling aho-corasick v1.1.3
Compiling regex-syntax v0.8.5
Compiling typenum v1.17.0
Compiling rand_core v0.6.4
Compiling log v0.4.27
Compiling regex-automata v0.4.9
Compiling generic-array v0.14.7
Compiling crypto-common v0.1.6
Compiling fastrand v2.1.0
Compiling bitflags v2.9.1
Compiling scopeguard v1.2.0
Compiling zerofrom v0.1.6
Compiling lock_api v0.4.11
Compiling parking_lot_core v0.9.9
Compiling stable_deref_trait v1.2.0
Compiling subtle v2.5.0
Compiling yoke v0.7.5
Compiling parking_lot v0.12.3
Compiling once_cell v1.21.3
Compiling tinyvec_macros v0.1.1
Compiling zerovec v0.10.4
Compiling tinyvec v1.6.0
Compiling zeroize v1.7.0
Compiling cpufeatures v0.2.12
Compiling writeable v0.5.5
Compiling unicode-normalization v0.1.23
Compiling tinystr v0.7.6
Compiling litemap v0.7.5
Compiling block-padding v0.3.3
Compiling icu_locid v1.5.0
Compiling thiserror v2.0.12
Compiling inout v0.1.3
Compiling bstr v1.9.1
Compiling icu_locid_transform_data v1.5.1
Compiling icu_provider v1.5.0
Compiling block-buffer v0.10.4
Compiling adler2 v2.0.0
Compiling hashbrown v0.14.3
Compiling itoa v1.0.11
Compiling miniz_oxide v0.8.8
Compiling digest v0.10.7
Compiling icu_locid_transform v1.5.0
Compiling cipher v0.4.4
Compiling crc32fast v1.4.0
Compiling icu_properties_data v1.5.1
Compiling icu_collections v1.5.0
Compiling icu_normalizer_data v1.5.1
Compiling icu_properties v1.5.1
Compiling write16 v1.0.0
Compiling utf16_iter v1.0.5
Compiling utf8_iter v1.0.4
Compiling percent-encoding v2.3.1
Compiling linux-raw-sys v0.4.13
Compiling thiserror v1.0.69
Compiling rustix v0.38.34
Compiling sha2 v0.10.8
Compiling form_urlencoded v1.2.1
Compiling universal-hash v0.5.1
Compiling opaque-debug v0.3.1
Compiling libz-sys v1.1.16
Compiling gix-trace v0.1.12
Compiling icu_normalizer v1.5.0
Compiling amplify_num v0.5.2
Compiling data-encoding v2.5.0
Compiling idna_adapter v1.2.0
Compiling idna v1.0.3
Compiling tempfile v3.10.1
Compiling url v2.5.4
Compiling ascii v1.1.0
Compiling signature v1.6.4
Compiling ed25519 v1.5.3
Compiling amplify v4.6.0
Compiling data-encoding-macro v0.1.14
Compiling libgit2-sys v0.17.0+1.8.1
Compiling aead v0.5.2
Compiling faster-hex v0.9.0
Compiling ct-codecs v1.1.1
Compiling base-x v0.2.11
Compiling byteorder v1.5.0
Compiling multibase v0.9.1
Compiling ec25519 v0.1.0
Compiling poly1305 v0.8.0
Compiling chacha20 v0.9.1
Compiling gix-utils v0.1.14
Compiling cyphergraphy v0.3.0
Compiling num-traits v0.2.19
Compiling gix-hash v0.15.1
Compiling same-file v1.0.6
Compiling keccak v0.1.5
Compiling walkdir v2.5.0
Compiling sha3 v0.10.8
Compiling polyval v0.6.2
Compiling git-ref-format-core v0.3.1
Compiling flate2 v1.1.1
Compiling hmac v0.12.1
Compiling sqlite3-src v0.5.1
Compiling prodash v29.0.2
Compiling base32 v0.4.0
Compiling base64ct v1.6.0
Compiling ppv-lite86 v0.2.17
Compiling equivalent v1.0.1
Compiling sha1_smol v1.0.0
Compiling indexmap v2.2.6
Compiling gix-features v0.39.1
Compiling rand_chacha v0.3.1
Compiling pem-rfc7468 v0.7.0
Compiling cypheraddr v0.4.0
Compiling git-ref-format-macro v0.3.1
Compiling pbkdf2 v0.12.2
Compiling ghash v0.5.1
Compiling crossbeam-utils v0.8.19
Compiling chacha20poly1305 v0.10.1
Compiling aes v0.8.4
Compiling ctr v0.9.2
Compiling ryu v1.0.17
Compiling serde_json v1.0.140
Compiling aes-gcm v0.10.3
Compiling git-ref-format v0.3.1
Compiling crossbeam-channel v0.5.15
Compiling noise-framework v0.4.0
Compiling socks5-client v0.4.1
Compiling ssh-encoding v0.2.0
Compiling rand v0.8.5
Compiling blowfish v0.9.1
Compiling cbc v0.1.2
Compiling radicle-std-ext v0.1.0
Compiling home v0.5.9
Compiling gix-path v0.10.15
Compiling ssh-cipher v0.2.0
Compiling bcrypt-pbkdf v0.10.0
Compiling cyphernet v0.5.2
Compiling signature v2.2.0
Compiling ssh-key v0.6.6
Compiling ref-cast v1.0.24
Compiling qcheck v1.0.0
Compiling radicle-ssh v0.9.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-ssh)
Compiling dyn-clone v1.0.17
Compiling lazy_static v1.5.0
Compiling siphasher v1.0.1
Compiling typeid v1.0.3
Compiling radicle-dag v0.10.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-dag)
Compiling nonempty v0.9.0
Compiling erased-serde v0.4.6
Compiling iana-time-zone v0.1.60
Compiling jiff v0.2.1
Compiling serde-untagged v0.1.7
Compiling chrono v0.4.38
Compiling schemars v1.0.4
Compiling gix-date v0.9.4
Compiling colored v2.1.0
Compiling bytesize v2.0.1
Compiling localtime v1.3.1
Compiling winnow v0.6.26
Compiling fast-glob v0.3.3
Compiling base64 v0.21.7
Compiling tree-sitter-language v0.1.2
Compiling gix-actor v0.33.2
Compiling gix-hashtable v0.6.0
Compiling gix-validate v0.9.4
Compiling memmap2 v0.9.4
Compiling gix-object v0.46.1
Compiling gix-chunk v0.4.11
Compiling anstyle-query v1.0.2
Compiling gix-commitgraph v0.25.1
Compiling anyhow v1.0.82
Compiling sqlite3-sys v0.15.2
Compiling gix-revwalk v0.17.0
Compiling sqlite v0.32.0
Compiling gix-fs v0.12.1
Compiling sem_safe v0.2.0
Compiling errno v0.3.13
Compiling unicode-segmentation v1.11.0
Compiling signals_receipts v0.2.0
Compiling gix-tempfile v15.0.0
Compiling signal-hook-registry v1.4.5
Compiling shell-words v1.1.0
Compiling signal-hook v0.3.18
Compiling gix-command v0.4.1
Compiling radicle-signals v0.11.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-signals)
Compiling mio v1.0.4
Compiling mio v0.8.11
Compiling tree-sitter v0.24.4
Compiling signal-hook-mio v0.2.4
Compiling gix-lock v15.0.1
Compiling convert_case v0.7.1
Compiling gix-url v0.28.2
Compiling gix-config-value v0.14.12
Compiling gix-quote v0.4.15
Compiling regex v1.11.1
Compiling gix-sec v0.10.12
Compiling gix-prompt v0.9.1
Compiling derive_more-impl v2.0.1
Compiling xattr v1.3.1
Compiling gix-revision v0.31.1
Compiling gix-traverse v0.43.1
Compiling gix-diff v0.49.0
Compiling gix-packetline v0.18.4
Compiling filetime v0.2.23
Compiling lexopt v0.3.0
Compiling bitflags v1.3.2
Compiling linux-raw-sys v0.9.4
Compiling utf8parse v0.2.1
Compiling anstyle-parse v0.2.3
Compiling crossterm v0.25.0
Compiling rustix v1.0.7
Compiling derive_more v2.0.1
Compiling tar v0.4.40
Compiling gix-transport v0.44.0
Compiling gix-pack v0.56.0
Compiling gix-refspec v0.27.0
Compiling gix-credentials v0.26.0
Compiling gix-ref v0.49.1
Compiling gix-shallow v0.1.0
Compiling newline-converter v0.3.0
Compiling gix-negotiate v0.17.0
Compiling fxhash v0.2.1
Compiling unicode-width v0.1.11
Compiling colorchoice v1.0.0
Compiling anstyle v1.0.6
Compiling arc-swap v1.7.1
Compiling streaming-iterator v0.1.9
Compiling gix-odb v0.66.0
Compiling anstream v0.6.13
Compiling inquire v0.7.5
Compiling gix-protocol v0.47.0
Compiling radicle-surf v0.22.0
Compiling crossterm v0.29.0
Compiling unicode-display-width v0.3.0
Compiling tree-sitter-python v0.23.4
Compiling tree-sitter-typescript v0.23.2
Compiling tree-sitter-rust v0.23.2
Compiling tree-sitter-md v0.3.2
Compiling tree-sitter-bash v0.23.3
Compiling tree-sitter-json v0.24.8
Compiling tree-sitter-c v0.23.2
Compiling tree-sitter-ruby v0.23.1
Compiling tree-sitter-css v0.23.1
Compiling tree-sitter-toml-ng v0.6.0
Compiling tree-sitter-html v0.23.2
Compiling tree-sitter-go v0.23.4
Compiling either v1.11.0
Compiling snapbox-macros v0.3.8
Compiling salsa20 v0.10.2
Compiling nonempty v0.5.0
Compiling similar v2.5.0
Compiling radicle-cli v0.14.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-cli)
Compiling base64 v0.13.1
Compiling normalize-line-endings v0.3.0
Compiling siphasher v0.3.11
Compiling snapbox v0.4.17
Compiling bloomy v1.2.0
Compiling scrypt v0.11.0
Compiling tree-sitter-highlight v0.24.4
Compiling popol v3.0.0
Compiling timeago v0.4.2
Compiling bytes v1.10.1
Compiling io-reactor v0.5.2
Compiling socket2 v0.5.7
Compiling diff v0.1.13
Compiling yansi v0.5.1
Compiling radicle-node v0.12.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-node)
Compiling pretty_assertions v1.4.0
Compiling netservices v0.8.0
Compiling num-integer v0.1.46
Compiling radicle-systemd v0.9.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-systemd)
Compiling escargot v0.5.10
Compiling num-bigint v0.4.6
Compiling radicle-remote-helper v0.11.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-remote-helper)
Compiling num-iter v0.1.45
Compiling num-complex v0.4.6
Compiling num-rational v0.4.2
Compiling borrow-or-share v0.2.2
Compiling bit-vec v0.8.0
Compiling zerocopy v0.7.35
Compiling bit-set v0.8.0
Compiling fluent-uri v0.3.2
Compiling num v0.4.3
Compiling ahash v0.8.11
Compiling phf_shared v0.11.3
Compiling uuid v1.16.0
Compiling vsimd v0.8.0
Compiling outref v0.5.2
Compiling uuid-simd v0.8.0
Compiling referencing v0.30.0
Compiling phf v0.11.3
Compiling fraction v0.15.3
Compiling fancy-regex v0.14.0
Compiling email_address v0.2.9
Compiling base64 v0.22.1
Compiling num-cmp v0.1.0
Compiling bytecount v0.6.8
Compiling emojis v0.6.4
Compiling jsonschema v0.30.0
Compiling git2 v0.19.0
Compiling radicle-git-ext v0.8.1
Compiling radicle-term v0.13.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-term)
Compiling radicle-crypto v0.12.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-crypto)
Compiling radicle-cob v0.14.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-cob)
Compiling radicle v0.16.1 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle)
Compiling radicle-fetch v0.12.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-fetch)
Compiling radicle-protocol v0.1.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-protocol)
Compiling radicle-cli-test v0.11.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-cli-test)
Compiling radicle-schemars v0.1.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-schemars)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 03s
+ cargo doc --workspace --no-deps
Checking regex-automata v0.4.9
Compiling syn v1.0.109
Checking idna v1.0.3
Checking radicle-ssh v0.9.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-ssh)
Compiling num-traits v0.2.19
Checking url v2.5.4
Checking git2 v0.19.0
Checking radicle-dag v0.10.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-dag)
Checking bstr v1.9.1
Compiling amplify_syn v2.0.1
Compiling proc-macro-error v1.0.4
Checking gix-path v0.10.15
Checking git-ref-format-core v0.3.1
Checking gix-date v0.9.4
Checking gix-validate v0.9.4
Checking chrono v0.4.38
Checking gix-actor v0.33.2
Compiling amplify_derive v4.0.0
Compiling data-encoding-macro-internal v0.1.12
Compiling git-ref-format-macro v0.3.1
Checking gix-object v0.46.1
Checking git-ref-format v0.3.1
Checking radicle-git-ext v0.8.1
Checking data-encoding-macro v0.1.14
Checking multibase v0.9.1
Checking gix-commitgraph v0.25.1
Checking gix-command v0.4.1
Checking radicle-signals v0.11.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-signals)
Checking regex v1.11.1
Checking gix-url v0.28.2
Checking gix-revwalk v0.17.0
Checking tree-sitter v0.24.4
Checking gix-config-value v0.14.12
Checking gix-quote v0.4.15
Checking gix-traverse v0.43.1
Checking gix-revision v0.31.1
Checking gix-prompt v0.9.1
Checking gix-diff v0.49.0
Checking gix-packetline v0.18.4
Compiling radicle-cli v0.14.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-cli)
Checking radicle-surf v0.22.0
Checking gix-pack v0.56.0
Checking amplify v4.6.0
Checking gix-transport v0.44.0
Checking cyphergraphy v0.3.0
Checking gix-refspec v0.27.0
Checking gix-credentials v0.26.0
Checking cypheraddr v0.4.0
Checking noise-framework v0.4.0
Checking tree-sitter-highlight v0.24.4
Checking tree-sitter-toml-ng v0.6.0
Checking socks5-client v0.4.1
Checking gix-negotiate v0.17.0
Checking gix-ref v0.49.1
Checking cyphernet v0.5.2
Checking gix-shallow v0.1.0
Checking radicle-crypto v0.12.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-crypto)
Checking radicle-term v0.13.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-term)
Checking gix-odb v0.66.0
Checking radicle-cob v0.14.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-cob)
Checking io-reactor v0.5.2
Compiling radicle-node v0.12.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-node)
Checking radicle v0.16.1 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle)
Checking gix-protocol v0.47.0
Checking netservices v0.8.0
Checking radicle-systemd v0.9.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-systemd)
Documenting radicle v0.16.1 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle)
Documenting radicle-cob v0.14.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-cob)
Documenting radicle-crypto v0.12.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-crypto)
Documenting radicle-term v0.13.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-term)
Documenting radicle-signals v0.11.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-signals)
Documenting radicle-ssh v0.9.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-ssh)
Documenting radicle-dag v0.10.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-dag)
Documenting radicle-systemd v0.9.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-systemd)
warning: unresolved link to `Canonical::quorum`
--> crates/radicle/src/git/canonical/error.rs:9:49
|
9 | /// Error that can occur when calculation the [`Canonical::quorum`].
| ^^^^^^^^^^^^^^^^^ no item named `Canonical` in scope
|
= note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default
warning: `radicle` (lib doc) generated 1 warning
Checking radicle-fetch v0.12.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-fetch)
Documenting radicle-cli v0.14.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-cli)
Documenting radicle-cli-test v0.11.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-cli-test)
Checking radicle-protocol v0.1.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-protocol)
Documenting radicle-protocol v0.1.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-protocol)
Documenting radicle-fetch v0.12.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-fetch)
Documenting radicle-node v0.12.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-node)
Checking radicle-remote-helper v0.11.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-remote-helper)
Documenting radicle-remote-helper v0.11.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-remote-helper)
Documenting radicle-schemars v0.1.0 (/9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/crates/radicle-schemars)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 9.58s
Generated /9ffd5085-5f5f-4839-84f2-2fbd061cca0a/w/target/doc/radicle/index.html and 16 other files
+ cargo test --workspace --no-fail-fast
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.19s
Running unittests src/lib.rs (target/debug/deps/radicle-831033a3e8b0bba8)
running 207 tests
test canonical::formatter::test::ascii_control_characters ... ok
test canonical::formatter::test::ordered_nested_object ... ok
test canonical::formatter::test::securesystemslib_asserts ... ok
test cob::cache::migrations::_2::tests::test_patch_json_deserialization ... ok
test cob::common::test::test_color ... ok
test cob::common::test::test_emojis ... ok
test cob::cache::tests::test_migrate_to ... ok
test cob::cache::migrations::_2::tests::test_migration_2 ... ok
test cob::cache::tests::test_check_version ... ok
test cob::identity::test::prop_json_eq_str ... ok
test cob::identity::test::test_identity_update_rejected ... ok
test cob::identity::test::test_identity_redact_revision ... ok
test cob::identity::test::test_identity_reject_concurrent ... ok
test cob::identity::test::test_identity_remove_delegate_concurrent ... ok
test cob::identity::test::test_identity_updates ... ok
test cob::issue::cache::tests::test_counts ... ok
test cob::issue::cache::tests::test_get ... ok
test cob::issue::cache::tests::test_is_empty ... ok
test cob::issue::cache::tests::test_list ... ok
test cob::issue::cache::tests::test_list_by_status ... ok
test cob::issue::cache::tests::test_remove ... ok
test cob::identity::test::test_identity_updates_concurrent ... ok
test cob::identity::test::test_valid_identity ... ok
test cob::issue::test::test_embeds ... ok
test cob::issue::test::test_embeds_edit ... ok
test cob::issue::test::test_invalid_actions ... ok
test cob::identity::test::test_identity_updates_concurrent_outdated ... ok
test cob::issue::test::test_invalid_tx ... ok
test cob::issue::test::test_invalid_tx_reference ... ok
test cob::issue::test::test_invalid_cob ... ok
test cob::issue::test::test_concurrency ... ok
test cob::issue::test::test_issue_comment_redact ... ok
test cob::issue::test::test_issue_create_and_assign ... ok
test cob::issue::test::test_issue_comment ... ok
test cob::issue::test::test_issue_all ... ok
test cob::issue::test::test_issue_create_and_get ... ok
test cob::issue::test::test_issue_create_and_unassign ... ok
test cob::issue::test::test_issue_create_and_reassign ... ok
test cob::issue::test::test_issue_create_and_change_state ... ok
test cob::issue::test::test_issue_edit_description ... ok
test cob::issue::test::test_issue_multilines ... ok
test cob::issue::test::test_issue_edit ... ok
test cob::issue::test::test_issue_state_serde ... ok
test cob::issue::test::test_ordering ... ok
test cob::patch::actions::test::test_review_edit ... ok
test cob::issue::test::test_issue_label ... ok
test cob::patch::cache::tests::test_find_by_revision ... ok
test cob::issue::test::test_issue_reply ... ok
test cob::issue::test::test_issue_react ... ok
test cob::patch::cache::tests::test_is_empty ... ok
test cob::patch::cache::tests::test_list ... ok
test cob::patch::cache::tests::test_list_by_status ... ok
test cob::patch::encoding::review::test::test_review_deserialize_summary_migration_null_summary ... ok
test cob::patch::cache::tests::test_get ... ok
test cob::patch::encoding::review::test::test_review_deserialize_summary_migration_with_summary ... ok
test cob::patch::encoding::review::test::test_review_deserialize_summary_migration_without_summary ... ok
test cob::patch::encoding::review::test::test_review_deserialize_summary_v2 ... ok
test cob::patch::encoding::review::test::test_review_summary ... ok
test cob::patch::test::test_json ... ok
test cob::patch::test::test_json_serialization ... ok
test cob::patch::cache::tests::test_remove ... ok
test cob::patch::cache::tests::test_counts ... ok
test cob::patch::test::test_patch_discussion ... ok
test cob::patch::test::test_patch_merge ... ok
test cob::patch::test::test_patch_create_and_get ... ok
test cob::patch::test::test_patch_redact ... ok
test cob::patch::test::test_patch_review_comment ... ok
test cob::patch::test::test_patch_review ... ok
test cob::patch::test::test_patch_review_duplicate ... ok
test cob::patch::test::test_patch_review_edit ... ok
test cob::patch::test::test_patch_review_remove_summary ... ok
test cob::patch::test::test_reactions_json_serialization ... ok
test cob::patch::test::test_patch_review_edit_comment ... ok
test cob::patch::test::test_revision_reaction ... ok
test cob::patch::test::test_revision_review_merge_redacted ... ok
test cob::patch::test::test_patch_review_revision_redact ... ok
test cob::patch::test::test_revision_edit_redact ... ok
test cob::thread::tests::test_comment_edit_redacted ... ok
test cob::thread::tests::test_comment_redact_missing ... ok
test cob::thread::tests::test_duplicate_comments ... ok
test cob::thread::tests::test_edit_comment ... ok
test cob::thread::tests::test_redact_comment ... ok
test cob::thread::tests::test_comment_edit_missing ... ok
test cob::thread::tests::test_timeline ... ok
test git::canonical::rules::tests::test_deserialization ... ok
test git::canonical::rules::tests::test_deserialize_extensions ... ok
test git::canonical::rules::tests::test_order ... ok
test git::canonical::rules::tests::test_roundtrip ... ok
test git::canonical::rules::tests::test_canonical ... ok
test git::canonical::rules::tests::test_rule_validate_success ... ok
test git::canonical::rules::tests::test_special_branches ... ok
test git::canonical::rules::tests::test_rule_validate_failures ... ok
test git::canonical::tests::test_quorum_groups ... ok
test git::canonical::tests::test_quorum_merges ... ok
test cob::patch::test::test_patch_update ... ok
test git::canonical::tests::test_quorum_tag ... ok
test git::test::test_version_from_str ... ok
test git::test::test_version_ord ... ok
test identity::did::test::test_did_encode_decode ... ok
test identity::did::test::test_did_vectors ... ok
test git::canonical::tests::test_quorum ... ok
test identity::doc::id::test::prop_from_str ... ok
test identity::doc::test::test_canonical_doc ... ok
test git::canonical::tests::test_quorum_properties ... ok
test identity::doc::test::test_duplicate_dids ... ok
test identity::doc::test::test_future_version_error ... ok
test identity::doc::test::test_is_valid_version ... ok
test identity::doc::test::test_canonical_example ... ok
test identity::doc::test::test_not_found ... ok
test identity::doc::test::test_parse_version ... ok
test identity::doc::test::test_max_delegates ... ok
test identity::doc::test::test_visibility_json ... ok
test identity::doc::update::test::test_cannot_include_default_branch_rule ... ok
test identity::doc::update::test::test_default_branch_rule_exists_after_verification ... ok
test identity::project::test::test_project_name ... ok
test node::address::store::test::test_alias ... ok
test node::address::store::test::test_disconnected ... ok
test node::address::store::test::test_disconnected_ban ... ok
test identity::doc::update::test::test_can_update_crefs ... ok
test cob::thread::tests::prop_ordering ... ok
test node::address::store::test::test_get_none ... ok
test node::address::store::test::test_insert_and_get ... ok
test node::address::store::test::test_insert_and_remove ... ok
test node::address::store::test::test_entries ... ok
test node::address::store::test::test_insert_and_update ... ok
test node::address::store::test::test_insert_duplicate ... ok
test node::address::store::test::test_remove_nothing ... ok
test node::db::test::test_version ... ok
test node::features::test::test_operations ... ok
test node::notifications::store::test::test_branch_notifications ... ok
test node::address::store::test::test_node_aliases ... ok
test node::notifications::store::test::test_clear ... ok
test node::notifications::store::test::test_cob_notifications ... ok
test node::notifications::store::test::test_counts_by_repo ... ok
test node::notifications::store::test::test_duplicate_notifications ... ok
test node::notifications::store::test::test_notification_status ... ok
test node::policy::store::test::test_follow_and_unfollow_node ... ok
test node::policy::store::test::test_node_policies ... ok
test node::policy::store::test::test_node_policy ... ok
test node::policy::store::test::test_node_aliases ... ok
test node::policy::store::test::test_repo_policies ... ok
test node::policy::store::test::test_seed_and_unseed_repo ... ok
test node::policy::store::test::test_repo_policy ... ok
test node::policy::store::test::test_update_scope ... ok
test node::policy::store::test::test_update_alias ... ok
test node::refs::store::test::test_count ... ok
test node::refs::store::test::test_set_and_delete ... ok
test node::refs::store::test::test_set_and_get ... ok
test node::routing::test::test_count ... ok
test node::routing::test::test_entries ... ok
test node::address::store::test::test_empty ... ok
test node::routing::test::test_insert_and_get ... ok
test node::routing::test::test_insert_duplicate ... ok
test node::routing::test::test_insert_and_get_resources ... ok
test node::routing::test::test_insert_existing_updated_time ... ok
test node::routing::test::test_insert_and_remove ... ok
test node::routing::test::test_len ... ok
test node::routing::test::test_remove_many ... ok
test node::routing::test::test_remove_redundant ... ok
test node::routing::test::test_update_existing_multi ... ok
test node::routing::test::test_prune ... ok
test node::sync::announce::test::announcer_must_reach_preferred_seeds ... ok
test node::sync::announce::test::announcer_reached_min_replication_target ... ok
test node::sync::announce::test::announcer_reached_max_replication_target ... ok
test node::sync::announce::test::announcer_will_minimise_replication_factor ... ok
test node::sync::announce::test::announcer_timed_out ... ok
test node::sync::fetch::test::all_nodes_are_candidates ... ok
test node::sync::announce::test::cannot_construct_announcer ... ok
test node::sync::fetch::test::could_not_reach_target ... ok
test node::sync::fetch::test::preferred_seeds_target_returned_over_replicas ... ok
test node::sync::fetch::test::all_nodes_are_fetchable ... ok
test node::sync::fetch::test::reaches_target_of_max_replicas ... ok
test node::sync::fetch::test::ignores_duplicates_and_local_node ... ok
test node::sync::fetch::test::reaches_target_of_replicas ... ok
test node::sync::test::ensure_replicas_construction ... ok
test node::sync::test::replicas_constrain_to ... ok
test node::test::test_command_result ... ok
test node::test::test_user_agent ... ok
test node::timestamp::tests::test_timestamp_max ... ok
test node::sync::fetch::test::reaches_target_of_preferred_seeds ... ok
test node::test::test_alias ... ok
test profile::test::canonicalize_home ... ok
test profile::test::test_config ... ok
test rad::tests::test_fork ... ok
test rad::tests::test_init ... ok
test rad::tests::test_checkout ... ok
test serde_ext::test::test_localtime ... ok
test serde_ext::test::test_localtime_ext ... ok
test storage::git::tests::test_references_of ... ok
test profile::config::test::schema ... ok
test storage::git::transport::local::url::test::test_url_parse ... ok
test storage::git::transport::local::url::test::test_url_to_string ... ok
test storage::git::transport::remote::url::test::test_url_parse ... ok
test storage::refs::tests::prop_canonical_roundtrip ... ok
test storage::git::tests::test_sign_refs ... ok
test storage::tests::test_storage ... ok
test test::assert::test::assert_with_message ... ok
test test::assert::test::test_assert_no_move ... ok
test test::assert::test::test_assert_panic_0 - should panic ... ok
test test::assert::test::test_assert_panic_1 - should panic ... ok
test test::assert::test::test_assert_panic_2 - should panic ... ok
test test::assert::test::test_assert_succeed ... ok
test test::assert::test::test_panic_message ... ok
test version::test::test_version ... ok
test storage::git::tests::test_remote_refs ... ok
test storage::refs::tests::test_rid_verification ... ok
test identity::doc::test::prop_encode_decode ... ok
test result: ok. 207 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.08s
Running unittests src/lib.rs (target/debug/deps/radicle_cli-42364fc438aa216f)
running 14 tests
test commands::rad_patch::review::builder::tests::test_review_comments_basic ... ok
test commands::rad_patch::review::builder::tests::test_review_comments_before ... ok
test commands::rad_patch::review::builder::tests::test_review_comments_split_hunk ... ok
test git::pretty_diff::test::test_pretty ... ignored
test git::ddiff::tests::diff_encode_decode_ddiff_hunk ... ok
test git::unified_diff::test::test_diff_content_encode_decode_content ... ok
test terminal::format::test::test_bytes ... ok
test commands::rad_inspect::test::test_tree ... ok
test git::unified_diff::test::test_diff_encode_decode_diff ... ok
test terminal::format::test::test_strip_comments ... ok
test commands::rad_patch::review::builder::tests::test_review_comments_multiline ... ok
test terminal::patch::test::test_edit_display_message ... ok
test terminal::patch::test::test_create_display_message ... ok
test terminal::patch::test::test_update_display_message ... ok
test result: ok. 13 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.01s
Running unittests src/main.rs (target/debug/deps/rad-68fb29d7e34eb57f)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/commands.rs (target/debug/deps/commands-42439250e178282e)
running 101 tests
test framework_home ... ok
test git_push_and_fetch ... ok
test git_push_amend ... ok
test git_push_canonical_lightweight_tags ... ok
test git_push_canonical_annotated_tags ... ok
test git_push_diverge ... ok
test rad_auth ... ok
test rad_auth_errors ... ok
test rad_block ... ok
test rad_checkout ... ok
test git_push_rollback ... ok
test git_tag ... ok
test git_push_converge ... ok
test rad_clone ... ok
test rad_clean ... ok
test rad_clone_connect ... ok
test rad_clone_all ... ok
test rad_clone_unknown ... ok
test rad_clone_directory ... ok
test rad_cob_multiset ... ok
test rad_cob_log ... ok
test rad_cob_migrate ... ok
test rad_cob_show ... ok
test rad_config ... ok
test rad_cob_update_identity ... ok
test rad_clone_partial_fail ... ok
test rad_cob_update ... ok
test rad_diff ... ok
test rad_id_collaboration ... ignored, slow
test rad_id ... ok
test rad_id_conflict ... ok
test rad_id_missing_commits ... ok
test rad_id_private ... ok
test rad_id_multi_delegate ... ok
test rad_id_threshold_soft_fork ... ok
test rad_id_threshold ... ok
test rad_id_unknown_field ... ok
test rad_id_update_delete_field ... ok
test rad_init ... ignored, part of many other tests
test rad_init_detached_head ... ok
test rad_init_existing ... ok
test rad_init_no_git ... ok
test rad_init_no_seed ... ok
test rad_init_private ... ok
test rad_fork ... ok
test rad_fetch ... ok
test rad_init_private_no_seed ... ok
test rad_init_private_clone ... ok
test rad_inbox ... ok
test rad_init_private_clone_seed ... ok
test rad_init_sync_not_connected ... ok
test rad_init_private_seed ... ok
test rad_init_sync_preferred ... ok
test rad_init_with_existing_remote ... ok
test rad_inspect ... ok
test rad_issue ... ok
test rad_merge_after_update ... ok
test rad_merge_no_ff ... ok
test rad_merge_via_push ... ok
test rad_node_connect ... ok
test rad_node_connect_without_address ... ok
test rad_node ... ok
test rad_patch ... ok
test rad_patch_ahead_behind ... ok
test rad_patch_change_base ... ok
test rad_patch_checkout ... ok
test rad_patch_checkout_revision ... ok
test rad_patch_checkout_force ... ok
test rad_patch_detached_head ... ok
test rad_init_sync_and_clone ... ok
test rad_init_sync_timeout ... ok
test rad_patch_diff ... ok
test rad_patch_draft ... ok
test rad_patch_edit ... ok
test rad_patch_fetch_2 ... ok
test rad_patch_merge_draft ... ok
test rad_patch_fetch_1 ... ok
test rad_patch_revert_merge ... ok
test rad_patch_delete ... ok
test rad_patch_update ... ok
test rad_patch_open_explore ... ok
test rad_publish ... ok
test rad_patch_via_push ... ok
test rad_review_by_hunk ... ok
test rad_seed_and_follow ... ok
test rad_remote ... ok
test rad_self ... ok
test rad_seed_many ... ok
test rad_sync_without_node ... ok
test rad_push_and_pull_patches ... ok
test rad_unseed ... ok
test rad_warn_old_nodes ... ok
test rad_unseed_many ... ok
test rad_watch ... ok
test rad_sync ... ok
test test_clone_without_seeds ... ok
test test_cob_deletion ... ok
test test_cob_replication ... ok
test rad_workflow ... ok
test rad_patch_pull_update ... ok
test test_replication_via_seed ... ok
test result: ok. 99 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 72.21s
Running unittests src/lib.rs (target/debug/deps/radicle_cli_test-68b099343e7f6e57)
running 3 tests
test tests::test_parse ... ok
test tests::test_run ... ok
test tests::test_example_spaced_brackets ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_cob-9109a114263758f9)
running 8 tests
test object::tests::test_serde ... ok
test tests::parse_refstr ... ok
test tests::invalid_parse_refstr ... ok
test tests::roundtrip ... ok
test tests::update_cob ... ok
test tests::list_cobs ... ok
test type_name::test::valid_typenames ... ok
test tests::traverse_cobs ... ok
test result: ok. 8 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
Running unittests src/lib.rs (target/debug/deps/radicle_crypto-075f17ddb707f16c)
running 12 tests
test ssh::fmt::test::test_key ... ok
test ssh::fmt::test::test_fingerprint ... ok
test ssh::keystore::tests::test_init_no_passphrase ... ok
test ssh::test::prop_encode_decode_sk ... ok
test ssh::test::test_agent_encoding_remove ... ok
test ssh::test::test_agent_encoding_sign ... ok
test tests::prop_encode_decode ... ok
test tests::test_e25519_dh ... ok
test tests::test_encode_decode ... ok
test tests::prop_key_equality ... ok
test ssh::keystore::tests::test_signer ... ok
test ssh::keystore::tests::test_init_passphrase ... ok
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.81s
Running unittests src/lib.rs (target/debug/deps/radicle_dag-7d941d2839407890)
running 20 tests
test tests::test_cycle ... ok
test tests::test_contains ... ok
test tests::test_complex ... ok
test tests::test_dependencies ... ok
test tests::test_diamond ... ok
test tests::test_fold_multiple_roots ... ok
test tests::test_fold_diamond ... ok
test tests::test_fold_reject ... ok
test tests::test_fold_sorting_1 ... ok
test tests::test_fold_sorting_2 ... ok
test tests::test_get ... ok
test tests::test_len ... ok
test tests::test_merge_1 ... ok
test tests::test_merge_2 ... ok
test tests::test_prune_by_sorting ... ok
test tests::test_remove ... ok
test tests::test_prune_2 ... ok
test tests::test_siblings ... ok
test tests::test_is_empty ... ok
test tests::test_prune_1 ... ok
test result: ok. 20 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_fetch-14c3dc27f06cc94f)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_node-8d7a7d9b4068e7f9)
running 69 tests
test control::tests::test_control_socket ... ok
test control::tests::test_seed_unseed ... ok
test tests::e2e::missing_default_branch ... ok
test tests::e2e::missing_delegate_default_branch ... ok
test tests::e2e::test_catchup_on_refs_announcements ... ok
test tests::e2e::test_background_foreground_fetch ... ok
test tests::e2e::test_channel_reader_limit ... ok
test tests::e2e::test_clone ... ok
test tests::e2e::test_dont_fetch_owned_refs ... ok
test tests::e2e::test_fetch_followed_remotes ... ok
test tests::e2e::test_connection_crossing ... ok
test tests::e2e::test_fetch_preserve_owned_refs ... ok
test tests::e2e::test_fetch_unseeded ... ok
test tests::e2e::test_fetch_up_to_date ... ok
test tests::e2e::test_concurrent_fetches ... ok
test tests::e2e::test_inventory_sync_basic ... ok
test tests::e2e::test_large_fetch ... ok
test tests::e2e::test_migrated_clone ... ok
test tests::e2e::test_missing_remote ... ok
test tests::e2e::test_multiple_offline_inits ... ok
test tests::e2e::test_non_fastforward_sigrefs ... ok
test tests::e2e::test_outdated_delegate_sigrefs ... ok
test tests::e2e::test_outdated_sigrefs ... ok
test tests::e2e::test_replication ... ok
test tests::e2e::test_replication_invalid ... ok
test tests::e2e::test_inventory_sync_bridge ... ok
test tests::e2e::test_inventory_sync_ring ... ok
test tests::e2e::test_replication_ref_in_sigrefs ... ok
test tests::e2e::test_inventory_sync_star ... ok
test tests::test_announcement_rebroadcast ... ok
test tests::test_announcement_rebroadcast_duplicates ... ok
test tests::test_announcement_rebroadcast_timestamp_filtered ... ok
test tests::test_announcement_relay ... ok
test tests::test_connection_kept_alive ... ok
test tests::test_disconnecting_unresponsive_peer ... ok
test tests::test_fetch_missing_inventory_on_gossip ... ok
test tests::test_fetch_missing_inventory_on_schedule ... ok
test tests::test_inbound_connection ... ok
test tests::test_inventory_decode ... ok
test tests::test_init_and_seed ... ok
test tests::test_inventory_relay ... ok
test tests::test_inventory_relay_bad_timestamp ... ok
test tests::test_inventory_sync ... ok
test tests::test_maintain_connections ... ok
test tests::test_maintain_connections_failed_attempt ... ok
test tests::test_maintain_connections_transient ... ok
test tests::test_inventory_pruning ... ok
test tests::test_outbound_connection ... ok
test tests::test_persistent_peer_connect ... ok
test tests::test_persistent_peer_reconnect_success ... ok
test tests::test_persistent_peer_reconnect_attempt ... ok
test tests::test_ping_response ... ok
test tests::test_queued_fetch_from_command_same_rid ... ok
test tests::test_queued_fetch_from_ann_same_rid ... ok
test tests::test_redundant_connect ... ok
test tests::test_queued_fetch_max_capacity ... ok
test tests::test_refs_announcement_followed ... ok
test tests::test_refs_announcement_fetch_trusted_no_inventory ... ok
test tests::test_refs_announcement_no_subscribe ... ok
test tests::test_refs_announcement_offline ... ok
test tests::test_refs_announcement_relay_private ... ok
test tests::test_refs_synced_event ... ok
test tests::test_refs_announcement_relay_public ... ok
test tests::test_seeding ... ok
test wire::test::test_inventory_ann_with_extension ... ok
test wire::test::test_pong_message_with_extension ... ok
test tests::test_seed_repo_subscribe ... ok
test tests::prop_inventory_exchange_dense ... ok
test tests::test_announcement_message_amplification ... ok
test result: ok. 69 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 16.39s
Running unittests src/main.rs (target/debug/deps/radicle_node-498b1e33a9c4009f)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_protocol-6de0bbe831e5d1f5)
running 45 tests
test deserializer::test::test_unparsed ... ok
test deserializer::test::test_decode_next ... ok
test service::filter::test::test_parameters ... ok
test service::limiter::test::test_limitter_different_rates ... ok
test service::gossip::store::test::test_announced ... ok
test deserializer::test::prop_decode_next ... ok
test service::limiter::test::test_limitter_multi ... ok
test service::limiter::test::test_limitter_refill ... ok
test service::filter::test::test_sizes ... ok
test service::message::tests::test_ref_remote_limit ... ok
test wire::frame::test::test_stream_id ... ok
test service::message::tests::test_inventory_limit ... ok
test wire::message::tests::prop_addr ... ok
test service::message::tests::prop_refs_announcement_signing ... ok
test wire::message::tests::prop_zero_bytes_encode_decode ... ok
test wire::message::tests::test_inv_ann_max_size ... ok
test wire::message::tests::test_node_ann_max_size ... ok
test wire::message::tests::test_ping_encode_size_overflow - should panic ... ok
test wire::message::tests::test_pingpong_encode_max_size ... ok
test wire::message::tests::test_pong_encode_size_overflow - should panic ... ok
test wire::message::tests::prop_message_encode_decode ... ok
test wire::tests::prop_filter ... ok
test wire::tests::prop_id ... ok
test wire::tests::prop_oid ... ok
test wire::tests::prop_pubkey ... ok
test wire::tests::prop_refs ... ok
test wire::tests::prop_signature ... ok
test wire::tests::prop_signed_refs ... ok
test wire::tests::prop_string ... ok
test wire::tests::prop_tuple ... ok
test wire::tests::prop_u16 ... ok
test wire::tests::prop_u32 ... ok
test wire::tests::prop_u64 ... ok
test wire::tests::prop_u8 ... ok
test wire::tests::prop_vec ... ok
test wire::tests::test_alias ... ok
test wire::tests::test_bounded_vec_limit ... ok
test wire::tests::test_filter_invalid ... ok
test wire::tests::test_string ... ok
test wire::varint::test::prop_encode_decode ... ok
test wire::varint::test::test_encode_overflow - should panic ... ok
test wire::varint::test::test_encoding ... ok
test wire::message::tests::test_refs_ann_max_size ... ok
test wire::message::tests::prop_message_decoder ... ok
test service::message::tests::test_node_announcement_validate ... ok
test result: ok. 45 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.53s
Running unittests src/lib.rs (target/debug/deps/radicle_remote_helper-3cfced1cb0b8f08a)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/git-remote-rad.rs (target/debug/deps/git_remote_rad-bb980559ab37aadb)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/main.rs (target/debug/deps/radicle_schemars-aa65fe85124a6484)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_signals-3340fefd7bdb5980)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_ssh-60ab4cdea8861dd8)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_systemd-8b6d0e6736538ff1)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_term-13eb155cdd070d2b)
running 20 tests
test ansi::tests::wrapping ... ok
test cell::test::test_width ... ok
test ansi::tests::colors_disabled ... ok
test element::test::test_truncate ... ok
test ansi::tests::colors_enabled ... ok
test table::test::test_table ... ok
test element::test::test_width ... ok
test table::test::test_table_border_maximized ... ok
test table::test::test_table_border_truncated ... ok
test table::test::test_table_truncate ... ok
test table::test::test_table_border ... ok
test table::test::test_table_unicode ... ok
test table::test::test_truncate ... ok
test table::test::test_table_unicode_truncate ... ok
test textarea::test::test_wrapping_fenced_block ... ok
test textarea::test::test_wrapping_code_block ... ok
test textarea::test::test_wrapping ... ok
test vstack::test::test_vstack ... ok
test vstack::test::test_vstack_maximize ... ok
test textarea::test::test_wrapping_paragraphs ... ok
test result: ok. 20 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle
running 1 test
test crates/radicle/src/cob/patch/encoding/review.rs - cob::patch::encoding::review::Review (line 23) ... ignored
test result: ok. 0 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_cli
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_cli_test
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_cob
running 1 test
test crates/radicle-cob/src/backend/git/stable.rs - backend::git::stable::with_advanced_timestamp (line 56) ... ignored
test result: ok. 0 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_crypto
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_dag
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_fetch
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_node
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_protocol
running 6 tests
test crates/radicle-protocol/src/bounded.rs - bounded::BoundedVec<T,N>::truncate (line 50) ... ok
test crates/radicle-protocol/src/bounded.rs - bounded::BoundedVec<T,N>::max (line 96) ... ok
test crates/radicle-protocol/src/bounded.rs - bounded::BoundedVec<T,N>::collect_from (line 30) ... ok
test crates/radicle-protocol/src/bounded.rs - bounded::BoundedVec<T,N>::unbound (line 149) ... ok
test crates/radicle-protocol/src/bounded.rs - bounded::BoundedVec<T,N>::with_capacity (line 66) ... ok
test crates/radicle-protocol/src/bounded.rs - bounded::BoundedVec<T,N>::push (line 122) ... ok
test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.57s
Doc-tests radicle_remote_helper
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_signals
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_ssh
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_systemd
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_term
running 1 test
test crates/radicle-term/src/table.rs - table (line 4) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.46s
Exit code: 0
{
"response": "finished",
"result": "success"
}