rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 heartwoodc3cc29b1c46980c4d5c775aa6e362029c2453e38
{
"request": "trigger",
"version": 1,
"event_type": "patch",
"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"
]
},
"action": "Updated",
"patch": {
"id": "76f3de33768da7d996e02cab5e89d0de06083172",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"title": "Test New Repository Abstractions",
"state": {
"status": "open",
"conflicts": []
},
"before": "fca3c519c64da0c0a79ce3f81bd035ad8b8a3c9a",
"after": "c3cc29b1c46980c4d5c775aa6e362029c2453e38",
"commits": [
"c3cc29b1c46980c4d5c775aa6e362029c2453e38",
"8c263ba6a32c732a304af92ad7059d5629845ec0",
"9462ede07e96136fea17383c73f4dff894e33083",
"6270c50328fce6508bc82da73e110a9240fe4b69"
],
"target": "a65ac048cbda3473e7b372375d33e3a357965492",
"labels": [],
"assignees": [],
"revisions": [
{
"id": "76f3de33768da7d996e02cab5e89d0de06083172",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "This is the final patch in a series that refactors the Git repository\npatterns.\n\nIt adds tests for:\n1. The pure domain types.\n2. The integration of `git2::Repository` as an implementor of the traits.\n3. The integration of `git2::Repository` for the `user::Namespace` and\n `user::Namespaces` methods.",
"base": "e3d54ba6efd5aec2b066778b14ad7b04d6a408f2",
"oid": "2d18300a2ce6f9eac52edcce4e4363b867dcc070",
"timestamp": 1778830328
},
{
"id": "9150c42ec0b30d7901e9b3f713dda1e279053b8b",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "Rebase",
"base": "5c7ab12225c7a3150fa06f27088303189c68b566",
"oid": "0a4828bb3d5c74960a1d636cf3157c894c7bbe3f",
"timestamp": 1778944819
},
{
"id": "10e19b80c053acc1e898995f63cef2dfeda1998c",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "Rebase",
"base": "fca3c519c64da0c0a79ce3f81bd035ad8b8a3c9a",
"oid": "c3cc29b1c46980c4d5c775aa6e362029c2453e38",
"timestamp": 1780127124
}
]
}
}
{
"response": "triggered",
"run_id": {
"id": "98b93ae1-1b2e-453f-92ac-4092c38802b5"
},
"info_url": "https://cci.rad.levitte.org//98b93ae1-1b2e-453f-92ac-4092c38802b5.html"
}
Started at: 2026-05-30 09:47:36.794445+02:00
Commands:
$ rad clone rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 .
✓ Creating checkout in ./...
✓ Remote cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT added
✓ Remote-tracking branch cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT/master created for z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT
✓ Remote cloudhead@z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW added
✓ Remote-tracking branch cloudhead@z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW/master created for z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW
✓ Remote fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM added
✓ Remote-tracking branch fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM/master created for z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM
✓ Remote erikli@z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz added
✓ Remote-tracking branch erikli@z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz/master created for z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz
✓ Remote lorenz@z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz added
✓ Remote-tracking branch lorenz@z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz/master created for z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz
✓ Repository successfully cloned under /opt/radcis/ci.rad.levitte.org/cci/state/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/
╭────────────────────────────────────╮
│ heartwood │
│ Radicle Heartwood Protocol & Stack │
│ 175 issues · 42 patches │
╰────────────────────────────────────╯
Run `cd ./.` to go to the repository directory.
Exit code: 0
$ rad patch checkout 76f3de33768da7d996e02cab5e89d0de06083172
✓ Switched to branch patch/76f3de3 at revision 10e19b8
✓ Branch patch/76f3de3 setup to track rad/patches/76f3de33768da7d996e02cab5e89d0de06083172
Exit code: 0
$ git config advice.detachedHead false
Exit code: 0
$ git checkout c3cc29b1c46980c4d5c775aa6e362029c2453e38
HEAD is now at c3cc29b1 git/repository/user: user::Namespace and user::Namespaces integration tests
Exit code: 0
$ rad patch show 76f3de33768da7d996e02cab5e89d0de06083172 -p
╭─────────────────────────────────────────────────────────────────────────────────────╮
│ Title Test New Repository Abstractions │
│ Patch 76f3de33768da7d996e02cab5e89d0de06083172 │
│ Author fintohaps z6Mkire…SQZ3voM │
│ Head c3cc29b1c46980c4d5c775aa6e362029c2453e38 │
│ Base fca3c519c64da0c0a79ce3f81bd035ad8b8a3c9a │
│ Branches patch/76f3de3 │
│ Commits ahead 29, behind 0 │
│ Status open │
│ │
│ This is the final patch in a series that refactors the Git repository │
│ patterns. │
│ │
│ It adds tests for: │
│ 1. The pure domain types. │
│ 2. The integration of `git2::Repository` as an implementor of the traits. │
│ 3. The integration of `git2::Repository` for the `user::Namespace` and │
│ `user::Namespaces` methods. │
├─────────────────────────────────────────────────────────────────────────────────────┤
│ c3cc29b git/repository/user: user::Namespace and user::Namespaces integration tests │
│ 8c263ba git/repository: git2 adapter integration tests │
│ 9462ede git/repository: Unit tests for git::repository domain types │
│ 6270c50 git/raw: Add composable test fixture │
├─────────────────────────────────────────────────────────────────────────────────────┤
│ ● Revision 76f3de3 @ e3d54ba..2d18300 by fintohaps z6Mkire…SQZ3voM 2 weeks ago │
│ ↑ Revision 9150c42 @ 5c7ab12..0a4828b by fintohaps z6Mkire…SQZ3voM 1 week ago │
│ ↑ Revision 10e19b8 @ fca3c51..c3cc29b by fintohaps z6Mkire…SQZ3voM 2 minutes ago │
╰─────────────────────────────────────────────────────────────────────────────────────╯
commit c3cc29b1c46980c4d5c775aa6e362029c2453e38
Author: Fintan Halpenny <fintan.halpenny@gmail.com>
Date: Thu Apr 30 14:25:50 2026 +0000
git/repository/user: user::Namespace and user::Namespaces integration tests
Integration tests for user-scoped reference access and namespace
discovery.
diff --git a/crates/radicle/src/git/repository/user.rs b/crates/radicle/src/git/repository/user.rs
index b2d61d08a..674bff0ad 100644
--- a/crates/radicle/src/git/repository/user.rs
+++ b/crates/radicle/src/git/repository/user.rs
@@ -9,6 +9,9 @@
pub mod error;
+#[cfg(test)]
+mod test;
+
use std::collections::BTreeMap;
use crypto::PublicKey;
diff --git a/crates/radicle/src/git/repository/user/test.rs b/crates/radicle/src/git/repository/user/test.rs
new file mode 100644
index 000000000..97875f679
--- /dev/null
+++ b/crates/radicle/src/git/repository/user/test.rs
@@ -0,0 +1,2 @@
+mod namespace;
+mod namespaces;
diff --git a/crates/radicle/src/git/repository/user/test/namespace.rs b/crates/radicle/src/git/repository/user/test/namespace.rs
new file mode 100644
index 000000000..593b9890e
--- /dev/null
+++ b/crates/radicle/src/git/repository/user/test/namespace.rs
@@ -0,0 +1,151 @@
+use radicle_git_ref_format::{pattern, qualified};
+
+use crate::git::raw::fixture;
+use crate::git::repository::reference;
+use crate::prelude::Did;
+
+use super::super::Namespace;
+
+fn did(n: u8) -> Did {
+ Did::from(crypto::PublicKey::from([n; 32]))
+}
+
+#[test]
+fn ref_target_found() {
+ let mut repo = fixture::Repository::new();
+ let did_a = did(1);
+ let commit = repo.commit(&[], &[("f", b"x")]);
+ repo.namespaced_ref(did_a, "refs/heads/main", commit);
+
+ let ns = Namespace::new(did_a, repo.raw());
+ assert_eq!(
+ ns.ref_target(&qualified!("refs/heads/main")).unwrap(),
+ Some(commit)
+ );
+}
+
+#[test]
+fn ref_target_not_found() {
+ let repo = fixture::Repository::new();
+ let ns = Namespace::new(did(1), repo.raw());
+ assert!(
+ ns.ref_target(&qualified!("refs/heads/nope"))
+ .unwrap()
+ .is_none()
+ );
+}
+
+#[test]
+fn users_isolated() {
+ let mut repo = fixture::Repository::new();
+ let did_a = did(1);
+ let did_b = did(2);
+ let ca = repo.commit(&[], &[("f", b"a")]);
+ let cb = repo.commit(&[], &[("f", b"b")]);
+ repo.namespaced_ref(did_a, "refs/heads/main", ca);
+ repo.namespaced_ref(did_a, "refs/heads/feature", ca);
+ repo.namespaced_ref(did_b, "refs/heads/main", cb);
+
+ let ns_a = Namespace::new(did_a, repo.raw());
+ let ns_b = Namespace::new(did_b, repo.raw());
+
+ assert_eq!(
+ ns_a.ref_target(&qualified!("refs/heads/main")).unwrap(),
+ Some(ca)
+ );
+ assert_eq!(
+ ns_a.ref_target(&qualified!("refs/heads/feature")).unwrap(),
+ Some(ca)
+ );
+ assert_eq!(
+ ns_b.ref_target(&qualified!("refs/heads/main")).unwrap(),
+ Some(cb)
+ );
+ assert!(
+ ns_b.ref_target(&qualified!("refs/heads/feature"))
+ .unwrap()
+ .is_none()
+ );
+}
+
+#[test]
+fn references_all() {
+ let mut repo = fixture::Repository::new();
+ let did_a = did(1);
+ let c = repo.commit(&[], &[("f", b"x")]);
+ repo.namespaced_ref(did_a, "refs/heads/main", c);
+ repo.namespaced_ref(did_a, "refs/heads/feature", c);
+ repo.namespaced_ref(did_a, "refs/rad/sigrefs", c);
+
+ let ns = Namespace::new(did_a, repo.raw());
+ let refs = ns.references(&pattern!("refs/*")).unwrap();
+ let names: Vec<_> = refs.into_iter().map(|(q, _)| q.to_string()).collect();
+ assert_eq!(names.len(), 3);
+ assert!(names.contains(&"refs/heads/feature".to_string()));
+ assert!(names.contains(&"refs/heads/main".to_string()));
+ assert!(names.contains(&"refs/rad/sigrefs".to_string()));
+}
+
+#[test]
+fn references_filtered() {
+ let mut repo = fixture::Repository::new();
+ let did_a = did(1);
+ let c = repo.commit(&[], &[("f", b"x")]);
+ repo.namespaced_ref(did_a, "refs/heads/main", c);
+ repo.namespaced_ref(did_a, "refs/heads/feature", c);
+ repo.namespaced_ref(did_a, "refs/rad/sigrefs", c);
+
+ let ns = Namespace::new(did_a, repo.raw());
+ let refs = ns.references(&pattern!("refs/heads/*")).unwrap();
+ assert_eq!(refs.into_iter().count(), 2);
+}
+
+#[test]
+fn write_and_read() {
+ let mut repo = fixture::Repository::new();
+ let did_a = did(1);
+ let did_b = did(2);
+ let c = repo.commit(&[], &[("f", b"x")]);
+ repo.namespaced_ref(did_b, "refs/heads/main", c);
+
+ let ns = Namespace::new(did_a, repo.raw());
+ ns.write_ref(
+ &qualified!("refs/heads/new"),
+ reference::Target::create(c),
+ "test",
+ )
+ .unwrap();
+
+ assert_eq!(
+ ns.ref_target(&qualified!("refs/heads/new")).unwrap(),
+ Some(c)
+ );
+ let ns_b = Namespace::new(did_b, repo.raw());
+ assert!(
+ ns_b.ref_target(&qualified!("refs/heads/new"))
+ .unwrap()
+ .is_none()
+ );
+}
+
+#[test]
+fn delete() {
+ let mut repo = fixture::Repository::new();
+ let did_a = did(1);
+ let c = repo.commit(&[], &[("f", b"x")]);
+ repo.namespaced_ref(did_a, "refs/heads/main", c);
+ repo.namespaced_ref(did_a, "refs/heads/feature", c);
+
+ let ns = Namespace::new(did_a, repo.raw());
+ ns.delete_ref(&qualified!("refs/heads/feature")).unwrap();
+ assert!(
+ ns.ref_target(&qualified!("refs/heads/feature"))
+ .unwrap()
+ .is_none()
+ );
+ assert!(
+ ns.ref_target(&qualified!("refs/heads/main"))
+ .unwrap()
+ .is_some()
+ );
+}
diff --git a/crates/radicle/src/git/repository/user/test/namespaces.rs b/crates/radicle/src/git/repository/user/test/namespaces.rs
new file mode 100644
index 000000000..81d11becc
--- /dev/null
+++ b/crates/radicle/src/git/repository/user/test/namespaces.rs
@@ -0,0 +1,114 @@
+use radicle_git_ref_format::refname;
+
+use crate::git::raw::fixture;
+use crate::git::repository::user;
+use crate::git::repository::user::FilterBy;
+use crate::prelude::Did;
+
+use super::super::Namespaces;
+
+fn did(n: u8) -> Did {
+ Did::from(crypto::PublicKey::from([n; 32]))
+}
+
+#[test]
+fn dids_with_sigrefs_filter() {
+ let mut repo = fixture::Repository::new();
+ let did_a = did(1);
+ let did_b = did(2);
+ let c = repo.commit(&[], &[("f", b"x")]);
+ repo.namespaced_ref(did_a, "refs/heads/main", c);
+ repo.namespaced_ref(did_a, "refs/rad/sigrefs", c);
+ repo.namespaced_ref(did_b, "refs/heads/main", c);
+
+ let dids: Vec<Did> = Namespaces::new(repo.raw())
+ .dids(FilterBy::suffix(&refname!("rad/sigrefs")))
+ .unwrap()
+ .collect();
+ assert_eq!(dids, vec![did_a]);
+}
+
+#[test]
+fn dids_unfiltered() {
+ let mut repo = fixture::Repository::new();
+ let did_a = did(1);
+ let did_b = did(2);
+ let c = repo.commit(&[], &[("f", b"x")]);
+ repo.namespaced_ref(did_a, "refs/heads/main", c);
+ repo.namespaced_ref(did_b, "refs/heads/main", c);
+
+ let dids: Vec<Did> = Namespaces::new(repo.raw())
+ .dids(user::FilterBy::Empty)
+ .unwrap()
+ .collect();
+ assert_eq!(dids.len(), 2);
+ assert!(dids.contains(&did_a));
+ assert!(dids.contains(&did_b));
+}
+
+#[test]
+fn dids_empty_repo() {
+ let repo = fixture::Repository::new();
+ let dids: Vec<Did> = Namespaces::new(repo.raw())
+ .dids(user::FilterBy::Empty)
+ .unwrap()
+ .collect();
+ assert!(dids.is_empty());
+}
+
+#[test]
+fn dids_with_errors_ok() {
+ let mut repo = fixture::Repository::new();
+ let did_a = did(1);
+ let c = repo.commit(&[], &[("f", b"x")]);
+ repo.namespaced_ref(did_a, "refs/rad/sigrefs", c);
+
+ let results: Vec<Result<Did, _>> = Namespaces::new(repo.raw())
+ .dids_with_errors(FilterBy::suffix(&refname!("rad/sigrefs")))
+ .unwrap()
+ .collect();
+ assert_eq!(results.len(), 1);
+ assert_eq!(results[0].as_ref().unwrap(), &did_a);
+}
+
+#[test]
+fn dids_skips_invalid_namespace() {
+ let mut repo = fixture::Repository::new();
+ let valid = did(1);
+ let c = repo.commit(&[], &[("f", b"x")]);
+ repo.namespaced_ref(valid, "refs/heads/main", c);
+ repo.reference("refs/namespaces/not-a-key/refs/heads/main", c);
+
+ let dids: Vec<Did> = Namespaces::new(repo.raw())
+ .dids(user::FilterBy::Empty)
+ .unwrap()
+ .collect();
+ assert_eq!(dids, vec![valid]);
+}
+
+#[test]
+fn dids_with_errors_surfaces_invalid_namespace() {
+ let mut repo = fixture::Repository::new();
+ let valid = did(1);
+ let c = repo.commit(&[], &[("f", b"x")]);
+ repo.namespaced_ref(valid, "refs/heads/main", c);
+ repo.reference("refs/namespaces/not-a-key/refs/heads/main", c);
+
+ let results: Vec<Result<Did, _>> = Namespaces::new(repo.raw())
+ .dids_with_errors(user::FilterBy::Empty)
+ .unwrap()
+ .collect();
+ assert_eq!(results.len(), 2);
+
+ let mut oks = Vec::new();
+ let mut errs = Vec::new();
+ for r in results {
+ match r {
+ Ok(d) => oks.push(d),
+ Err(e) => errs.push(e),
+ }
+ }
+ assert_eq!(oks, vec![valid]);
+ assert_eq!(errs.len(), 1);
+ assert!(matches!(errs[0], user::NamespaceError::Did(_)));
+}
commit 8c263ba6a32c732a304af92ad7059d5629845ec0
Author: Fintan Halpenny <fintan.halpenny@gmail.com>
Date: Thu Apr 30 14:19:19 2026 +0000
git/repository: git2 adapter integration tests
Integration tests for the git2 adapater of the `git::repository`
family of traits.
diff --git a/crates/radicle/src/git/repository/adapter/git2.rs b/crates/radicle/src/git/repository/adapter/git2.rs
index 56c8a078b..d80985440 100644
--- a/crates/radicle/src/git/repository/adapter/git2.rs
+++ b/crates/radicle/src/git/repository/adapter/git2.rs
@@ -12,6 +12,9 @@ mod object;
mod reference;
mod revwalk;
+#[cfg(test)]
+mod test;
+
/// Helper trait to enable method chaining to return `None` when the error
/// matches [`ErrorCode::NotFound`].
///
diff --git a/crates/radicle/src/git/repository/adapter/git2/test.rs b/crates/radicle/src/git/repository/adapter/git2/test.rs
new file mode 100644
index 000000000..11ecfaa1f
--- /dev/null
+++ b/crates/radicle/src/git/repository/adapter/git2/test.rs
@@ -0,0 +1,9 @@
+// TODO(finto): The tests within these submodules could be generic and form
+// contracts for any adapter of the Git interfaces. For now, since we only
+// define them for `crate::git::raw`, we will leave them as-is.
+
+mod ancestry;
+mod object;
+mod reference;
+mod revwalk;
+mod symbolic;
diff --git a/crates/radicle/src/git/repository/adapter/git2/test/ancestry.rs b/crates/radicle/src/git/repository/adapter/git2/test/ancestry.rs
new file mode 100644
index 000000000..25c35686a
--- /dev/null
+++ b/crates/radicle/src/git/repository/adapter/git2/test/ancestry.rs
@@ -0,0 +1,166 @@
+use radicle_oid::Oid;
+
+use crate::git::raw::fixture;
+use crate::git::repository::Ancestry;
+use crate::git::repository::ancestry::error;
+
+#[test]
+fn merge_base_parent_child() {
+ let repo = fixture::Repository::new();
+ let parent = repo.commit(&[], &[("f", b"v1")]);
+ let child = repo.commit(&[parent], &[("f", b"v2")]);
+ let base = Ancestry::merge_base(repo.raw(), parent, child).unwrap();
+ assert_eq!(base, Some(parent));
+}
+
+#[test]
+fn merge_base_identity() {
+ let repo = fixture::Repository::new();
+ let c = repo.commit(&[], &[("f", b"v1")]);
+ assert_eq!(Ancestry::merge_base(repo.raw(), c, c).unwrap(), Some(c));
+}
+
+#[test]
+fn merge_base_diverged() {
+ let repo = fixture::Repository::new();
+ let root = repo.commit(&[], &[("f", b"v1")]);
+ let left = repo.commit(&[root], &[("f", b"v2")]);
+ let right = repo.commit(&[root], &[("f", b"v3")]);
+ assert_eq!(
+ Ancestry::merge_base(repo.raw(), left, right).unwrap(),
+ Some(root)
+ );
+}
+
+#[test]
+fn merge_base_diamond() {
+ let repo = fixture::Repository::new();
+ let root = repo.commit(&[], &[("f", b"v1")]);
+ let left = repo.commit(&[root], &[("f", b"v2")]);
+ let right = repo.commit(&[root], &[("f", b"v3")]);
+ let merge = repo.commit(&[left, right], &[("f", b"v2")]);
+
+ assert_eq!(
+ Ancestry::merge_base(repo.raw(), merge, right).unwrap(),
+ Some(right),
+ );
+ assert_eq!(
+ Ancestry::merge_base(repo.raw(), merge, left).unwrap(),
+ Some(left)
+ );
+ assert_eq!(
+ Ancestry::merge_base(repo.raw(), merge, root).unwrap(),
+ Some(root),
+ );
+ assert_eq!(
+ Ancestry::merge_base(repo.raw(), left, right).unwrap(),
+ Some(root)
+ );
+}
+
+#[test]
+fn is_ancestor_true() {
+ let repo = fixture::Repository::new();
+ let parent = repo.commit(&[], &[("f", b"v1")]);
+ let child = repo.commit(&[parent], &[("f", b"v2")]);
+ assert!(Ancestry::is_ancestor(repo.raw(), parent, child).unwrap());
+}
+
+#[test]
+fn is_ancestor_false() {
+ let repo = fixture::Repository::new();
+ let parent = repo.commit(&[], &[("f", b"v1")]);
+ let child = repo.commit(&[parent], &[("f", b"v2")]);
+ assert!(!Ancestry::is_ancestor(repo.raw(), child, parent).unwrap());
+}
+
+#[test]
+fn merge_base_is_ancestor() {
+ let repo = fixture::Repository::new();
+ let grandparent = repo.commit(&[], &[("f", b"v1")]);
+ let parent = repo.commit(&[grandparent], &[("f", b"v2")]);
+ let child = repo.commit(&[parent], &[("f", b"v3")]);
+ assert!(
+ Ancestry::is_ancestor(
+ repo.raw(),
+ Ancestry::merge_base(repo.raw(), grandparent, child)
+ .unwrap()
+ .unwrap(),
+ child
+ )
+ .unwrap()
+ );
+ assert!(
+ Ancestry::is_ancestor(
+ repo.raw(),
+ Ancestry::merge_base(repo.raw(), grandparent, parent)
+ .unwrap()
+ .unwrap(),
+ parent
+ )
+ .unwrap()
+ )
+}
+
+#[test]
+fn ahead_behind_child_parent() {
+ let repo = fixture::Repository::new();
+ let parent = repo.commit(&[], &[("f", b"v1")]);
+ let child = repo.commit(&[parent], &[("f", b"v2")]);
+ let ab = Ancestry::ahead_behind(repo.raw(), child, parent).unwrap();
+ assert_eq!(ab.ahead, 1);
+ assert_eq!(ab.behind, 0);
+ assert!(ab.is_linear());
+ let ab = Ancestry::ahead_behind(repo.raw(), parent, child).unwrap();
+ assert_eq!(ab.ahead, 0);
+ assert_eq!(ab.behind, 1);
+ assert!(ab.is_linear());
+}
+
+#[test]
+fn ahead_behind_diverged() {
+ let repo = fixture::Repository::new();
+ let root = repo.commit(&[], &[("f", b"v1")]);
+ let left = repo.commit(&[root], &[("f", b"v2")]);
+ let right = repo.commit(&[root], &[("f", b"v3")]);
+ let ab = Ancestry::ahead_behind(repo.raw(), left, right).unwrap();
+ assert_eq!(ab.ahead, 1);
+ assert_eq!(ab.behind, 1);
+ assert!(!ab.is_linear());
+}
+
+#[test]
+fn merge_base_missing_commit() {
+ let repo = fixture::Repository::new();
+ let c = repo.commit(&[], &[("f", b"v1")]);
+ let missing = Oid::from_sha1([0xff; 20]);
+ let err = Ancestry::merge_base(repo.raw(), c, missing).unwrap_err();
+ assert!(matches!(err, error::MergeBase::CommitNotFound { oid } if oid == missing));
+}
+
+#[test]
+fn is_ancestor_missing_ancestor() {
+ let repo = fixture::Repository::new();
+ let c = repo.commit(&[], &[("f", b"v1")]);
+ let missing = Oid::from_sha1([0xff; 20]);
+ let err = Ancestry::is_ancestor(repo.raw(), missing, c).unwrap_err();
+ assert!(matches!(err, error::IsAncestor::CommitNotFound { oid } if oid == missing));
+}
+
+#[test]
+fn is_ancestor_missing_head() {
+ let repo = fixture::Repository::new();
+ let c = repo.commit(&[], &[("f", b"v1")]);
+ let missing = Oid::from_sha1([0xff; 20]);
+ let err = Ancestry::is_ancestor(repo.raw(), c, missing).unwrap_err();
+ assert!(matches!(err, error::IsAncestor::CommitNotFound { oid } if oid == missing));
+}
+
+#[test]
+fn ahead_behind_missing_commit() {
+ let repo = fixture::Repository::new();
+ let c = repo.commit(&[], &[("f", b"v1")]);
+ let missing = Oid::from_sha1([0xff; 20]);
+ let err = Ancestry::ahead_behind(repo.raw(), c, missing).unwrap_err();
+ assert!(matches!(err, error::AheadBehind::CommitNotFound { oid } if oid == missing));
+}
diff --git a/crates/radicle/src/git/repository/adapter/git2/test/object.rs b/crates/radicle/src/git/repository/adapter/git2/test/object.rs
new file mode 100644
index 000000000..3588def71
--- /dev/null
+++ b/crates/radicle/src/git/repository/adapter/git2/test/object.rs
@@ -0,0 +1,224 @@
+use std::path::Path;
+
+use radicle_git_metadata::author::{Author, Time};
+use radicle_git_metadata::commit::CommitData;
+use radicle_git_metadata::commit::headers::Headers;
+use radicle_git_metadata::commit::trailers::OwnedTrailer;
+use radicle_oid::Oid;
+
+use crate::git::raw::fixture;
+use crate::git::repository;
+use crate::git::repository::object;
+use crate::git::repository::types::TreeEntry;
+
+#[test]
+fn blob_found() {
+ let repo = fixture::Repository::new();
+ let blob_oid = repo.blob(b"hello");
+ let blob = object::Reader::blob(repo.raw(), blob_oid).unwrap().unwrap();
+ assert_eq!(blob.oid, blob_oid);
+ assert_eq!(blob.content, b"hello");
+}
+
+#[test]
+fn blob_not_found() {
+ let repo = fixture::Repository::new();
+ let missing = Oid::from_sha1([0xff; 20]);
+ assert!(object::Reader::blob(repo.raw(), missing).unwrap().is_none());
+}
+
+#[test]
+fn try_blob_not_found() {
+ let repo = fixture::Repository::new();
+ let missing = Oid::from_sha1([0xff; 20]);
+ let err = object::Reader::try_blob(repo.raw(), missing).unwrap_err();
+ assert!(matches!(err, object::error::read::Blob::NotFound { .. }));
+}
+
+#[test]
+fn blob_at() {
+ let repo = fixture::Repository::new();
+ let commit = repo.commit(&[], &[("hello.txt", b"content")]);
+ let blob = object::Reader::blob_at(repo.raw(), commit, &Path::new("hello.txt")).unwrap();
+ assert_eq!(blob.unwrap().content, b"content");
+}
+
+#[test]
+fn blob_at_nested() {
+ let repo = fixture::Repository::new();
+ let commit = repo.commit(&[], &[("sub/nested.txt", b"deep")]);
+ let blob = object::Reader::blob_at(repo.raw(), commit, &Path::new("sub/nested.txt")).unwrap();
+ assert_eq!(blob.unwrap().content, b"deep");
+}
+
+#[test]
+fn blob_at_missing_path() {
+ let repo = fixture::Repository::new();
+ let commit = repo.commit(&[], &[("file.txt", b"x")]);
+ assert!(
+ object::Reader::blob_at(repo.raw(), commit, &Path::new("nope.txt"))
+ .unwrap()
+ .is_none()
+ );
+}
+
+#[test]
+fn blob_at_missing_commit() {
+ let repo = fixture::Repository::new();
+ let missing = Oid::from_sha1([0xff; 20]);
+ let err = object::Reader::blob_at(repo.raw(), missing, &Path::new("f")).unwrap_err();
+ assert!(matches!(
+ err,
+ object::error::read::BlobAt::CommitNotFound { .. }
+ ));
+}
+
+#[test]
+fn commit() {
+ let repo = fixture::Repository::new();
+ let parent = repo.commit(&[], &[("f", b"v1")]);
+ let child = repo.commit(&[parent], &[("f", b"v2")]);
+ let commit = object::Reader::commit(repo.raw(), child).unwrap().unwrap();
+ assert_eq!(commit.parents().next(), Some(parent));
+}
+
+#[test]
+fn commit_not_found() {
+ let repo = fixture::Repository::new();
+ let missing = Oid::from_sha1([0xff; 20]);
+ assert!(
+ object::Reader::commit(repo.raw(), missing)
+ .unwrap()
+ .is_none()
+ );
+}
+
+#[test]
+fn exists_true() {
+ let repo = fixture::Repository::new();
+ let oid = repo.blob(b"exists");
+ assert!(object::Reader::exists(repo.raw(), oid).unwrap());
+}
+
+#[test]
+fn exists_false() {
+ let repo = fixture::Repository::new();
+ let missing = Oid::from_sha1([0xff; 20]);
+ assert!(!object::Reader::exists(repo.raw(), missing).unwrap());
+}
+
+#[test]
+fn object_kind_blob() {
+ let repo = fixture::Repository::new();
+ let oid = repo.blob(b"kind test");
+ assert_eq!(
+ object::Reader::object_kind(repo.raw(), oid).unwrap(),
+ Some(repository::ObjectKind::Blob)
+ );
+}
+
+#[test]
+fn object_kind_commit() {
+ let repo = fixture::Repository::new();
+ let commit = repo.commit(&[], &[("f", b"x")]);
+ assert_eq!(
+ object::Reader::object_kind(repo.raw(), commit).unwrap(),
+ Some(repository::ObjectKind::Commit)
+ );
+}
+
+#[test]
+fn object_kind_tag() {
+ let repo = fixture::Repository::new();
+ let commit = repo.commit(&[], &[("f", b"x")]);
+ let tag = repo.tag("v1", commit, true);
+ assert_eq!(
+ object::Reader::object_kind(repo.raw(), tag).unwrap(),
+ Some(repository::ObjectKind::Tag)
+ );
+}
+
+#[test]
+fn object_kind_missing() {
+ let repo = fixture::Repository::new();
+ let missing = Oid::from_sha1([0xff; 20]);
+ assert!(
+ object::Reader::object_kind(repo.raw(), missing)
+ .unwrap()
+ .is_none()
+ );
+}
+
+#[test]
+fn write_blob_roundtrip() {
+ let repo = fixture::Repository::new();
+ let oid = object::Writer::write_blob(repo.raw(), b"test content").unwrap();
+ let blob = object::Reader::blob(repo.raw(), oid).unwrap().unwrap();
+ assert_eq!(blob.content, b"test content");
+}
+
+#[test]
+fn write_tree_inline_blob() {
+ let repo = fixture::Repository::new();
+ let entries = vec![TreeEntry::Blob {
+ path: "file.txt".into(),
+ content: b"data".to_vec(),
+ }];
+ let tree_oid = object::Writer::write_tree(repo.raw(), &entries).unwrap();
+ assert!(object::Reader::exists(repo.raw(), tree_oid).unwrap());
+}
+
+#[test]
+fn write_tree_multi_component_path() {
+ let repo = fixture::Repository::new();
+ let entries = vec![TreeEntry::Blob {
+ path: "a/b/c.txt".into(),
+ content: b"deep".to_vec(),
+ }];
+ let tree_oid = object::Writer::write_tree(repo.raw(), &entries).unwrap();
+
+ let author = Author {
+ name: "t".into(),
+ email: "t@t".into(),
+ time: Time::new(0, 0),
+ };
+ let commit = CommitData::new::<_, _, OwnedTrailer>(
+ tree_oid,
+ None::<Oid>,
+ author.clone(),
+ author,
+ Headers::new(),
+ "t\n".to_string(),
+ vec![],
+ );
+ let commit_oid =
+ object::Writer::write_commit(repo.raw(), commit.to_string().as_bytes()).unwrap();
+ let blob = object::Reader::blob_at(repo.raw(), commit_oid, &Path::new("a/b/c.txt")).unwrap();
+ assert_eq!(blob.unwrap().content, b"deep");
+}
+
+#[test]
+fn write_tree_blob_ref() {
+ let repo = fixture::Repository::new();
+ let blob_oid = object::Writer::write_blob(repo.raw(), b"existing").unwrap();
+ let entries = vec![TreeEntry::BlobRef {
+ path: "ref.txt".into(),
+ oid: blob_oid,
+ }];
+ object::Writer::write_tree(repo.raw(), &entries).unwrap();
+}
+
+#[test]
+fn write_tree_blob_ref_missing() {
+ let repo = fixture::Repository::new();
+ let missing = Oid::from_sha1([0xff; 20]);
+ let entries = vec![TreeEntry::BlobRef {
+ path: "bad.txt".into(),
+ oid: missing,
+ }];
+ let err = object::Writer::write_tree(repo.raw(), &entries).unwrap_err();
+ assert!(matches!(
+ err,
+ object::error::write::Tree::MissingBlob { .. }
+ ));
+}
diff --git a/crates/radicle/src/git/repository/adapter/git2/test/reference.rs b/crates/radicle/src/git/repository/adapter/git2/test/reference.rs
new file mode 100644
index 000000000..01fcc4c88
--- /dev/null
+++ b/crates/radicle/src/git/repository/adapter/git2/test/reference.rs
@@ -0,0 +1,203 @@
+use radicle_git_ref_format::{pattern, refname};
+use radicle_oid::Oid;
+
+use crate::git::raw::fixture;
+use crate::git::repository::reference;
+
+#[test]
+fn ref_target_found() {
+ let repo = fixture::Repository::new();
+ let commit = repo.commit(&[], &[("f", b"x")]);
+ repo.reference("refs/heads/main", commit);
+
+ let oid = reference::Reader::ref_target(repo.raw(), &refname!("refs/heads/main")).unwrap();
+ assert_eq!(oid, Some(commit));
+}
+
+#[test]
+fn ref_target_not_found() {
+ let repo = fixture::Repository::new();
+ let oid = reference::Reader::ref_target(repo.raw(), &refname!("refs/heads/nope")).unwrap();
+ assert!(oid.is_none());
+}
+
+#[test]
+fn list_refs() {
+ let repo = fixture::Repository::new();
+ let a = repo.commit(&[], &[("f", b"a")]);
+ let b = repo.commit(&[], &[("f", b"b")]);
+ repo.reference("refs/heads/alpha", a);
+ repo.reference("refs/heads/beta", b);
+ repo.reference("refs/tags/v1", a);
+
+ let refs: Vec<_> = reference::Reader::list_refs(repo.raw(), &pattern!("refs/heads/*"))
+ .unwrap()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
+
+ assert_eq!(refs.len(), 2);
+ let names: Vec<_> = refs.iter().map(|(q, _)| q.as_str()).collect();
+ assert!(names.contains(&"refs/heads/alpha"));
+ assert!(names.contains(&"refs/heads/beta"));
+
+ let refs: Vec<_> = reference::Reader::list_refs(repo.raw(), &pattern!("refs/*"))
+ .unwrap()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
+
+ assert_eq!(refs.len(), 3);
+ let names: Vec<_> = refs.iter().map(|(q, _)| q.as_str()).collect();
+ assert!(names.contains(&"refs/heads/alpha"));
+ assert!(names.contains(&"refs/heads/beta"));
+ assert!(names.contains(&"refs/tags/v1"));
+
+ let refs: Vec<_> = reference::Reader::list_refs(repo.raw(), &pattern!("refs/nope/*"))
+ .unwrap()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
+ assert!(refs.is_empty())
+}
+
+#[test]
+fn list_refs_empty() {
+ let repo = fixture::Repository::new();
+ let refs: Vec<_> = reference::Reader::list_refs(repo.raw(), &pattern!("refs/*"))
+ .unwrap()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
+ assert!(refs.is_empty());
+}
+
+#[test]
+fn write_ref_create() {
+ let repo = fixture::Repository::new();
+ let commit = repo.commit(&[], &[("f", b"x")]);
+ let name = refname!("refs/heads/new");
+
+ reference::Writer::write_ref(repo.raw(), &name, reference::Target::create(commit), "test")
+ .unwrap();
+ assert_eq!(
+ reference::Reader::ref_target(repo.raw(), &name).unwrap(),
+ Some(commit)
+ );
+}
+
+#[test]
+fn write_ref_create_existing() {
+ let repo = fixture::Repository::new();
+ let a = repo.commit(&[], &[("f", b"a")]);
+ let b = repo.commit(&[], &[("f", b"b")]);
+ repo.reference("refs/heads/main", a);
+
+ let err = reference::Writer::write_ref(
+ repo.raw(),
+ &refname!("refs/heads/main"),
+ reference::Target::create(b),
+ "test",
+ )
+ .unwrap_err();
+ assert!(matches!(
+ err,
+ reference::error::write::WriteRef::ReferenceExists { .. }
+ ));
+}
+
+#[test]
+fn write_ref_upsert_new() {
+ let repo = fixture::Repository::new();
+ let commit = repo.commit(&[], &[("f", b"x")]);
+ let name = refname!("refs/heads/upserted");
+
+ reference::Writer::write_ref(
+ repo.raw(),
+ &name,
+ reference::Target::Upsert { target: commit },
+ "test",
+ )
+ .unwrap();
+ assert_eq!(
+ reference::Reader::ref_target(repo.raw(), &name).unwrap(),
+ Some(commit)
+ );
+}
+
+#[test]
+fn write_ref_upsert_existing() {
+ let repo = fixture::Repository::new();
+ let a = repo.commit(&[], &[("f", b"a")]);
+ let b = repo.commit(&[], &[("f", b"b")]);
+ repo.reference("refs/heads/main", a);
+
+ reference::Writer::write_ref(
+ repo.raw(),
+ &refname!("refs/heads/main"),
+ reference::Target::Upsert { target: b },
+ "test",
+ )
+ .unwrap();
+ assert_eq!(
+ reference::Reader::ref_target(repo.raw(), &refname!("refs/heads/main")).unwrap(),
+ Some(b)
+ );
+}
+
+#[test]
+fn write_ref_cas_success() {
+ let repo = fixture::Repository::new();
+ let a = repo.commit(&[], &[("f", b"a")]);
+ let b = repo.commit(&[], &[("f", b"b")]);
+ repo.reference("refs/heads/main", a);
+
+ reference::Writer::write_ref(
+ repo.raw(),
+ &refname!("refs/heads/main"),
+ reference::Target::cas(b, a),
+ "test",
+ )
+ .unwrap();
+ assert_eq!(
+ reference::Reader::ref_target(repo.raw(), &refname!("refs/heads/main")).unwrap(),
+ Some(b)
+ );
+}
+
+#[test]
+fn write_ref_cas_wrong_expected() {
+ let repo = fixture::Repository::new();
+ let a = repo.commit(&[], &[("f", b"a")]);
+ let b = repo.commit(&[], &[("f", b"b")]);
+ repo.reference("refs/heads/main", a);
+
+ let wrong = Oid::from_sha1([0xaa; 20]);
+ let err = reference::Writer::write_ref(
+ repo.raw(),
+ &refname!("refs/heads/main"),
+ reference::Target::cas(b, wrong),
+ "test",
+ )
+ .unwrap_err();
+ assert!(matches!(
+ err,
+ reference::error::write::WriteRef::CasFailed { .. }
+ ));
+}
+
+#[test]
+fn delete_ref_existing() {
+ let repo = fixture::Repository::new();
+ let commit = repo.commit(&[], &[("f", b"x")]);
+ repo.reference("refs/tags/v1.0", commit);
+
+ reference::Writer::delete_ref(repo.raw(), &refname!("refs/tags/v1.0")).unwrap();
+ assert!(
+ reference::Reader::ref_target(repo.raw(), &refname!("refs/tags/v1.0"))
+ .unwrap()
+ .is_none()
+ );
+}
+
+#[test]
+fn delete_ref_idempotent() {
+ let repo = fixture::Repository::new();
+ reference::Writer::delete_ref(repo.raw(), &refname!("refs/heads/nonexistent")).unwrap();
+}
diff --git a/crates/radicle/src/git/repository/adapter/git2/test/revwalk.rs b/crates/radicle/src/git/repository/adapter/git2/test/revwalk.rs
new file mode 100644
index 000000000..2416c440e
--- /dev/null
+++ b/crates/radicle/src/git/repository/adapter/git2/test/revwalk.rs
@@ -0,0 +1,198 @@
+use crate::git::raw::fixture;
+use crate::git::repository::{Revwalk, RevwalkPlan, SortOrder};
+
+/// Helper to build a diamond: root → left, root → right, merge(left, right)
+fn diamond(
+ repo: &fixture::Repository,
+) -> (
+ radicle_oid::Oid,
+ radicle_oid::Oid,
+ radicle_oid::Oid,
+ radicle_oid::Oid,
+) {
+ let root = repo.commit(&[], &[("f", b"v1")]);
+ let left = repo.commit(&[root], &[("f", b"v2")]);
+ let right = repo.commit(&[root], &[("f", b"v3")]);
+ let merge = repo.commit(&[left, right], &[("f", b"v2")]);
+ (root, left, right, merge)
+}
+
+#[test]
+fn linear_chain() {
+ let repo = fixture::Repository::new();
+ let root = repo.commit(&[], &[("f", b"v1")]);
+ let child = repo.commit(&[root], &[("f", b"v2")]);
+
+ let plan = RevwalkPlan::new().push(child);
+ let oids: Vec<_> = Revwalk::revwalk_oids(repo.raw(), &plan)
+ .unwrap()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
+ assert_eq!(oids.len(), 2);
+ assert_eq!(oids[0], child);
+ assert!(oids.contains(&root));
+}
+
+#[test]
+fn commit_data_iter() {
+ let repo = fixture::Repository::new();
+ let root = repo.commit(&[], &[("f", b"v1")]);
+ let child = repo.commit(&[root], &[("f", b"v2")]);
+
+ let plan = RevwalkPlan::new().push(child);
+ let commits: Vec<_> = Revwalk::revwalk_commits(repo.raw(), &plan)
+ .unwrap()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
+ assert_eq!(commits.len(), 2);
+}
+
+#[test]
+fn range() {
+ let repo = fixture::Repository::new();
+ let root = repo.commit(&[], &[("f", b"v1")]);
+ let child = repo.commit(&[root], &[("f", b"v2")]);
+ let grandchild = repo.commit(&[child], &[("f", b"v3")]);
+
+ let plan = RevwalkPlan::new().range(root, child);
+ let oids: Vec<_> = Revwalk::revwalk_oids(repo.raw(), &plan)
+ .unwrap()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
+ assert_eq!(oids, vec![child]);
+ assert!(!oids.contains(&root));
+ assert!(!oids.contains(&grandchild));
+}
+
+#[test]
+fn hide() {
+ let repo = fixture::Repository::new();
+ let root = repo.commit(&[], &[("f", b"v1")]);
+ let child = repo.commit(&[root], &[("f", b"v2")]);
+
+ let plan = RevwalkPlan::new().push(child).hide(root);
+ let oids: Vec<_> = Revwalk::revwalk_oids(repo.raw(), &plan)
+ .unwrap()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
+ assert_eq!(oids, vec![child]);
+}
+
+#[test]
+fn from_merge_sees_all() {
+ let repo = fixture::Repository::new();
+ let (root, left, right, merge) = diamond(&repo);
+
+ let plan = RevwalkPlan::new().push(merge);
+ let oids: Vec<_> = Revwalk::revwalk_oids(repo.raw(), &plan)
+ .unwrap()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
+ assert_eq!(oids.len(), 4);
+ assert!(oids.contains(&merge));
+ assert!(oids.contains(&left));
+ assert!(oids.contains(&right));
+ assert!(oids.contains(&root));
+}
+
+#[test]
+fn hide_one_branch() {
+ let repo = fixture::Repository::new();
+ let (root, left, right, merge) = diamond(&repo);
+
+ let plan = RevwalkPlan::new().push(merge).hide(left);
+ let oids: Vec<_> = Revwalk::revwalk_oids(repo.raw(), &plan)
+ .unwrap()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
+ assert!(oids.contains(&merge));
+ assert!(oids.contains(&right));
+ assert!(!oids.contains(&left));
+ // root hidden since root is reachable from left
+ assert!(!oids.contains(&root));
+}
+
+#[test]
+fn multiple_push_points() {
+ let repo = fixture::Repository::new();
+ let (root, left, right, _merge) = diamond(&repo);
+
+ let plan = RevwalkPlan::new().push(left).push(right);
+ let oids: Vec<_> = Revwalk::revwalk_oids(repo.raw(), &plan)
+ .unwrap()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
+ assert_eq!(oids.len(), 3);
+ assert!(oids.contains(&left));
+ assert!(oids.contains(&right));
+ assert!(oids.contains(&root));
+}
+
+#[test]
+fn push_and_hide_compose() {
+ let repo = fixture::Repository::new();
+ let (root, left, right, _merge) = diamond(&repo);
+
+ let plan = RevwalkPlan::new().push(left).push(right).hide(root);
+ let oids: Vec<_> = Revwalk::revwalk_oids(repo.raw(), &plan)
+ .unwrap()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
+ assert_eq!(oids.len(), 2);
+ assert!(oids.contains(&left));
+ assert!(oids.contains(&right));
+ assert!(!oids.contains(&root));
+}
+
+#[test]
+fn range_on_branch() {
+ let repo = fixture::Repository::new();
+ let (root, _left, right, _merge) = diamond(&repo);
+
+ let plan = RevwalkPlan::new().range(root, right);
+ let oids: Vec<_> = Revwalk::revwalk_oids(repo.raw(), &plan)
+ .unwrap()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
+ assert_eq!(oids, vec![right]);
+}
+
+#[test]
+fn topological_order() {
+ let repo = fixture::Repository::new();
+ let (root, left, right, merge) = diamond(&repo);
+
+ let plan = RevwalkPlan::new()
+ .push(merge)
+ .sort(SortOrder::Topological { reverse: false });
+ let oids: Vec<_> = Revwalk::revwalk_oids(repo.raw(), &plan)
+ .unwrap()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
+ assert_eq!(*oids.first().unwrap(), merge);
+ assert_eq!(*oids.last().unwrap(), root);
+
+ // root must come after both left and right
+ let root_pos = oids.iter().position(|o| *o == root).unwrap();
+ let left_pos = oids.iter().position(|o| *o == left).unwrap();
+ let right_pos = oids.iter().position(|o| *o == right).unwrap();
+ assert!(root_pos > left_pos);
+ assert!(root_pos > right_pos);
+}
+
+#[test]
+fn reverse_chronological() {
+ let repo = fixture::Repository::new();
+ let root = repo.commit(&[], &[("f", b"v1")]);
+ let child = repo.commit(&[root], &[("f", b"v2")]);
+
+ let plan = RevwalkPlan::new()
+ .push(child)
+ .sort(SortOrder::Chronological { reverse: true });
+ let oids: Vec<_> = Revwalk::revwalk_oids(repo.raw(), &plan)
+ .unwrap()
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
+ assert_eq!(*oids.first().unwrap(), root);
+ assert_eq!(*oids.last().unwrap(), child);
+}
diff --git a/crates/radicle/src/git/repository/adapter/git2/test/symbolic.rs b/crates/radicle/src/git/repository/adapter/git2/test/symbolic.rs
new file mode 100644
index 000000000..94f8f9184
--- /dev/null
+++ b/crates/radicle/src/git/repository/adapter/git2/test/symbolic.rs
@@ -0,0 +1,168 @@
+use radicle_git_ref_format::refname;
+
+use crate::git::raw::fixture;
+use crate::git::repository::reference;
+use crate::git::repository::reference::symbolic;
+
+#[test]
+fn write_symbolic_ref_new() {
+ let repo = fixture::Repository::new();
+ let commit = repo.commit(&[], &[("f", b"x")]);
+ repo.reference("refs/heads/main", commit);
+
+ symbolic::Writer::write_symbolic_ref(
+ repo.raw(),
+ &refname!("refs/heads/sym"),
+ symbolic::Target::create(refname!("refs/heads/main")),
+ "test",
+ )
+ .unwrap();
+
+ let oid = reference::Reader::ref_target(repo.raw(), &refname!("refs/heads/sym")).unwrap();
+ assert_eq!(oid, Some(commit));
+}
+
+#[test]
+fn write_symbolic_ref_existing_fails() {
+ let repo = fixture::Repository::new();
+ let commit = repo.commit(&[], &[("f", b"x")]);
+ repo.reference("refs/heads/main", commit);
+
+ symbolic::Writer::write_symbolic_ref(
+ repo.raw(),
+ &refname!("refs/heads/sym"),
+ symbolic::Target::create(refname!("refs/heads/main")),
+ "test",
+ )
+ .unwrap();
+
+ let err = symbolic::Writer::write_symbolic_ref(
+ repo.raw(),
+ &refname!("refs/heads/sym"),
+ symbolic::Target::create(refname!("refs/heads/main")),
+ "test",
+ )
+ .unwrap_err();
+ assert!(matches!(
+ err,
+ reference::error::write::WriteSymbolicRef::ReferenceExists { .. }
+ ));
+}
+
+#[test]
+fn upsert_symbolic_ref_new() {
+ let repo = fixture::Repository::new();
+ let commit = repo.commit(&[], &[("f", b"x")]);
+ repo.reference("refs/heads/main", commit);
+
+ symbolic::Writer::write_symbolic_ref(
+ repo.raw(),
+ &refname!("refs/heads/sym"),
+ symbolic::Target::upsert(refname!("refs/heads/main")),
+ "test",
+ )
+ .unwrap();
+
+ let oid = reference::Reader::ref_target(repo.raw(), &refname!("refs/heads/sym")).unwrap();
+ assert_eq!(oid, Some(commit));
+}
+
+#[test]
+fn upsert_symbolic_ref_existing() {
+ let repo = fixture::Repository::new();
+ let a = repo.commit(&[], &[("f", b"a")]);
+ let b = repo.commit(&[], &[("f", b"b")]);
+ repo.reference("refs/heads/main", a);
+ repo.reference("refs/heads/other", b);
+
+ symbolic::Writer::write_symbolic_ref(
+ repo.raw(),
+ &refname!("refs/heads/sym"),
+ symbolic::Target::create(refname!("refs/heads/main")),
+ "test",
+ )
+ .unwrap();
+
+ symbolic::Writer::write_symbolic_ref(
+ repo.raw(),
+ &refname!("refs/heads/sym"),
+ symbolic::Target::upsert(refname!("refs/heads/other")),
+ "test",
+ )
+ .unwrap();
+
+ let oid = reference::Reader::ref_target(repo.raw(), &refname!("refs/heads/sym")).unwrap();
+ assert_eq!(oid, Some(b));
+}
+
+#[test]
+fn cas_symbolic_ref_success() {
+ let repo = fixture::Repository::new();
+ let a = repo.commit(&[], &[("f", b"a")]);
+ let b = repo.commit(&[], &[("f", b"b")]);
+ repo.reference("refs/heads/main", a);
+ repo.reference("refs/heads/other", b);
+
+ symbolic::Writer::write_symbolic_ref(
+ repo.raw(),
+ &refname!("refs/heads/sym"),
+ symbolic::Target::create(refname!("refs/heads/main")),
+ "test",
+ )
+ .unwrap();
+
+ symbolic::Writer::write_symbolic_ref(
+ repo.raw(),
+ &refname!("refs/heads/sym"),
+ symbolic::Target::cas(refname!("refs/heads/other"), refname!("refs/heads/main")),
+ "test",
+ )
+ .unwrap();
+
+ let oid = reference::Reader::ref_target(repo.raw(), &refname!("refs/heads/sym")).unwrap();
+ assert_eq!(oid, Some(b));
+}
+
+#[test]
+fn cas_symbolic_ref_wrong_expected() {
+ let repo = fixture::Repository::new();
+ let commit = repo.commit(&[], &[("f", b"x")]);
+ repo.reference("refs/heads/main", commit);
+ repo.reference("refs/tags/v1.0", commit);
+
+ symbolic::Writer::write_symbolic_ref(
+ repo.raw(),
+ &refname!("refs/heads/sym"),
+ symbolic::Target::create(refname!("refs/heads/main")),
+ "test",
+ )
+ .unwrap();
+
+ let err = symbolic::Writer::write_symbolic_ref(
+ repo.raw(),
+ &refname!("refs/heads/sym"),
+ symbolic::Target::cas(refname!("refs/tags/v1.0"), refname!("refs/tags/v1.0")),
+ "test",
+ )
+ .unwrap_err();
+ assert!(matches!(
+ err,
+ reference::error::write::WriteSymbolicRef::CasFailed { .. }
+ ));
+}
+
+#[test]
+fn symbolic_ref_missing_target() {
+ let repo = fixture::Repository::new();
+ let err = symbolic::Writer::write_symbolic_ref(
+ repo.raw(),
+ &refname!("refs/heads/sym"),
+ symbolic::Target::create(refname!("refs/heads/does-not-exist")),
+ "test",
+ )
+ .unwrap_err();
+ assert!(matches!(
+ err,
+ reference::error::write::WriteSymbolicRef::MissingTarget { .. }
+ ));
+}
commit 9462ede07e96136fea17383c73f4dff894e33083
Author: Fintan Halpenny <fintan.halpenny@gmail.com>
Date: Thu Apr 30 14:10:51 2026 +0000
git/repository: Unit tests for git::repository domain types
Add unit tests for domain types:
- ancestry: AheadBehind::is_linear
- revwalk: RevwalkPlan builder
- reference: Target constructors
- symbolic: Target constructors
diff --git a/crates/radicle/src/git/repository/ancestry.rs b/crates/radicle/src/git/repository/ancestry.rs
index 660f09ed4..87bd0ccec 100644
--- a/crates/radicle/src/git/repository/ancestry.rs
+++ b/crates/radicle/src/git/repository/ancestry.rs
@@ -67,3 +67,52 @@ pub trait Ancestry {
/// [`Backend`]: error::AheadBehind::Backend
fn ahead_behind(&self, commit: Oid, upstream: Oid) -> Result<AheadBehind, error::AheadBehind>;
}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn is_linear_ahead_only() {
+ assert!(
+ AheadBehind {
+ ahead: 3,
+ behind: 0
+ }
+ .is_linear()
+ );
+ }
+
+ #[test]
+ fn is_linear_behind_only() {
+ assert!(
+ AheadBehind {
+ ahead: 0,
+ behind: 5
+ }
+ .is_linear()
+ );
+ }
+
+ #[test]
+ fn is_linear_same_commit() {
+ assert!(
+ AheadBehind {
+ ahead: 0,
+ behind: 0
+ }
+ .is_linear()
+ );
+ }
+
+ #[test]
+ fn is_linear_diverged() {
+ assert!(
+ !AheadBehind {
+ ahead: 2,
+ behind: 1
+ }
+ .is_linear()
+ );
+ }
+}
diff --git a/crates/radicle/src/git/repository/reference.rs b/crates/radicle/src/git/repository/reference.rs
index 81f3fea8e..d5447a33c 100644
--- a/crates/radicle/src/git/repository/reference.rs
+++ b/crates/radicle/src/git/repository/reference.rs
@@ -185,3 +185,33 @@ pub trait Writer {
where
R: AsRef<RefStr>;
}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ fn oid(n: u8) -> Oid {
+ Oid::from_sha1([n; 20])
+ }
+
+ #[test]
+ fn target_create() {
+ let t = Target::create(oid(1));
+ assert_eq!(t.target(), oid(1));
+ assert!(matches!(t, Target::Create { .. }));
+ }
+
+ #[test]
+ fn target_upsert() {
+ let t = Target::Upsert { target: oid(2) };
+ assert_eq!(t.target(), oid(2));
+ assert!(matches!(t, Target::Upsert { .. }));
+ }
+
+ #[test]
+ fn target_cas() {
+ let t = Target::cas(oid(3), oid(4));
+ assert_eq!(t.target(), oid(3));
+ assert!(matches!(t, Target::Cas { expected, .. } if expected == oid(4)));
+ }
+}
diff --git a/crates/radicle/src/git/repository/reference/symbolic.rs b/crates/radicle/src/git/repository/reference/symbolic.rs
index 0dbc41a6f..4bf4440d6 100644
--- a/crates/radicle/src/git/repository/reference/symbolic.rs
+++ b/crates/radicle/src/git/repository/reference/symbolic.rs
@@ -113,3 +113,31 @@ pub trait Writer: super::Writer {
where
R: AsRef<RefStr>;
}
+
+#[cfg(test)]
+mod test {
+ use radicle_git_ref_format::refname;
+
+ use super::*;
+
+ #[test]
+ fn target_create() {
+ let t = Target::create(refname!("refs/heads/main"));
+ assert_eq!(t.target().as_str(), "refs/heads/main");
+ assert!(matches!(t, Target::Create { .. }));
+ }
+
+ #[test]
+ fn target_upsert() {
+ let t = Target::upsert(refname!("refs/heads/main"));
+ assert_eq!(t.target().as_str(), "refs/heads/main");
+ assert!(matches!(t, Target::Upsert { .. }));
+ }
+
+ #[test]
+ fn target_cas() {
+ let t = Target::cas(refname!("refs/heads/main"), refname!("refs/heads/old"));
+ assert_eq!(t.target().as_str(), "refs/heads/main");
+ assert!(matches!(t, Target::Cas { expected, .. } if expected.as_str() == "refs/heads/old"));
+ }
+}
diff --git a/crates/radicle/src/git/repository/revwalk.rs b/crates/radicle/src/git/repository/revwalk.rs
index f7c8372d6..0b215a1cb 100644
--- a/crates/radicle/src/git/repository/revwalk.rs
+++ b/crates/radicle/src/git/repository/revwalk.rs
@@ -141,3 +141,50 @@ pub trait Revwalk {
plan: &RevwalkPlan,
) -> Result<Self::RevwalkCommits<'a>, error::Init>;
}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ fn oid(n: u8) -> Oid {
+ Oid::from_sha1([n; 20])
+ }
+
+ #[test]
+ fn plan_push_accumulates() {
+ let plan = RevwalkPlan::new().push(oid(1)).push(oid(2));
+ assert_eq!(plan.starts(), &[oid(1), oid(2)]);
+ }
+
+ #[test]
+ fn plan_hide_accumulates() {
+ let plan = RevwalkPlan::new().hide(oid(3)).hide(oid(4));
+ assert_eq!(plan.hidden(), &[oid(3), oid(4)]);
+ }
+
+ #[test]
+ fn plan_range() {
+ let plan = RevwalkPlan::new().range(oid(1), oid(2));
+ assert_eq!(plan.range_bounds(), Some((oid(1), oid(2))));
+ }
+
+ #[test]
+ fn plan_sort() {
+ let plan = RevwalkPlan::new().sort(SortOrder::Topological { reverse: true });
+ assert_eq!(plan.sort_order(), SortOrder::Topological { reverse: true });
+ }
+
+ #[test]
+ fn plan_compose() {
+ let plan = RevwalkPlan::new()
+ .push(oid(1))
+ .hide(oid(2))
+ .range(oid(3), oid(4))
+ .sort(SortOrder::Topological { reverse: false });
+
+ assert_eq!(plan.starts(), &[oid(1)]);
+ assert_eq!(plan.hidden(), &[oid(2)]);
+ assert_eq!(plan.range_bounds(), Some((oid(3), oid(4))));
+ assert_eq!(plan.sort_order(), SortOrder::Topological { reverse: false });
+ }
+}
commit 6270c50328fce6508bc82da73e110a9240fe4b69
Author: Fintan Halpenny <fintan.halpenny@gmail.com>
Date: Fri May 1 15:32:21 2026 +0000
git/raw: Add composable test fixture
Add git::raw::fixture::Repository — a bare git2::Repository in a TempDir
with helpers for constructing test state without boilerplate.
diff --git a/crates/radicle/src/git/raw.rs b/crates/radicle/src/git/raw.rs
index 8508b5487..f20f24027 100644
--- a/crates/radicle/src/git/raw.rs
+++ b/crates/radicle/src/git/raw.rs
@@ -42,6 +42,9 @@ pub(crate) mod transport {
};
}
+#[cfg(any(test, feature = "test"))]
+pub mod fixture;
+
/// An extension trait for [`git2::Error`] to more conveniently handle
/// errors with the code [`git2::ErrorCode::NotFound`].
pub trait ErrorExt {
diff --git a/crates/radicle/src/git/raw/fixture.rs b/crates/radicle/src/git/raw/fixture.rs
new file mode 100644
index 000000000..eae52c9c2
--- /dev/null
+++ b/crates/radicle/src/git/raw/fixture.rs
@@ -0,0 +1,169 @@
+#![allow(clippy::unwrap_used)]
+
+//! Composable test fixture for building Git repositories with known state.
+//!
+//! [`Repository`] wraps a bare [`super::Repository`] in a [`TempDir`] and
+//! provides helpers to create commits, references, and namespaced references
+//! without boilerplate.
+//!
+//! # Example
+//!
+//! ```rust,ignore
+//! let mut repo = Repository::new();
+//! let root = repo.commit(&[], &[("file.txt", b"hello")]);
+//! let child = repo.commit(&[root], &[("file.txt", b"updated")]);
+//! repo.reference("refs/heads/main", child);
+//! ```
+
+use std::collections::BTreeSet;
+
+use radicle_oid::Oid;
+use tempfile::TempDir;
+
+use crate::prelude::Did;
+
+/// A bare Git repository in a temporary directory, with helpers for
+/// constructing test state.
+///
+/// Use [`Repository::raw`] to get the raw handle on the underlying
+/// [`super::Repository`].
+///
+/// For manipulating the underlying repository with fixture data see:
+/// - [`Repository::commit`]
+/// - [`Repository::reference`]
+/// - [`Repository::namespaced_ref`]
+/// - [`Repository::blob`]
+pub struct Repository {
+ inner: super::Repository,
+ dids: BTreeSet<Did>,
+ _dir: TempDir,
+}
+
+impl Default for Repository {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl Repository {
+ /// Create a new empty bare repository.
+ pub fn new() -> Self {
+ let dir = TempDir::new().expect("failed to create temp dir");
+ let inner = super::Repository::init_bare(dir.path()).expect("failed to init bare repo");
+ Self {
+ inner,
+ dids: BTreeSet::new(),
+ _dir: dir,
+ }
+ }
+
+ /// Access the underlying [`super::Repository`].
+ pub fn raw(&self) -> &super::Repository {
+ &self.inner
+ }
+
+ /// The set of [`Did`]s registered via [`Self::namespaced_ref`].
+ pub fn known_dids(&self) -> &BTreeSet<Did> {
+ &self.dids
+ }
+
+ /// Create a commit with the given tree content and parent commits.
+ ///
+ /// `files` is a list of `(path, content)` pairs. Each path may be
+ /// multi-component (e.g. `"a/b/c.txt"`) — intermediate trees are
+ /// created automatically via [`super::build::TreeUpdateBuilder`].
+ ///
+ /// Returns the [`Oid`] of the new commit.
+ pub fn commit(&self, parents: &[Oid], files: &[(&str, &[u8])]) -> Oid {
+ let sig = super::Signature::new("test", "test@test", &super::Time::new(0, 0))
+ .expect("valid signature");
+
+ let tree_oid = self.build_tree(files);
+ let tree = self.inner.find_tree(tree_oid).expect("tree just written");
+
+ let parent_commits: Vec<super::Commit<'_>> = parents
+ .iter()
+ .map(|oid| {
+ self.inner
+ .find_commit((*oid).into())
+ .unwrap_or_else(|_| panic!("parent commit {oid} not found"))
+ })
+ .collect();
+ let parent_refs: Vec<&super::Commit<'_>> = parent_commits.iter().collect();
+
+ self.inner
+ .commit(None, &sig, &sig, "test commit", &tree, &parent_refs)
+ .expect("failed to create commit")
+ .into()
+ }
+
+ /// Create a tag with the given `name`, pointing to the object identified by
+ /// the given [`Oid`].
+ ///
+ /// Returns the [`Oid`] of the tag object.
+ pub fn tag(&self, name: &str, oid: Oid, force: bool) -> Oid {
+ let sig = super::Signature::new("test", "test@test", &super::Time::new(0, 0))
+ .expect("valid signature");
+ let target = self.inner.find_object(oid.into(), None).unwrap();
+ self.inner
+ .tag(name, &target, &sig, "fixture tag", force)
+ .unwrap()
+ .into()
+ }
+
+ /// Create a direct reference pointing to `target`.
+ ///
+ /// Panics if the reference already exists. Use [`Self::raw`] for
+ /// more control.
+ pub fn reference(&self, name: &str, target: Oid) {
+ self.inner
+ .reference(name, target.into(), false, "fixture")
+ .unwrap_or_else(|e| panic!("failed to create reference {name}: {e}"));
+ }
+
+ /// Create a namespaced reference for a [`Did`].
+ ///
+ /// The `refname` is a qualified name like `refs/heads/main`. It is
+ /// prefixed with `refs/namespaces/<key>/` internally.
+ ///
+ /// The [`Did`] is recorded in [`Self::known_dids`].
+ pub fn namespaced_ref(&mut self, did: Did, refname: &str, target: Oid) {
+ let key = did.as_key();
+ let full = format!("refs/namespaces/{key}/{refname}");
+ self.inner
+ .reference(&full, target.into(), false, "fixture")
+ .unwrap_or_else(|e| panic!("failed to create namespaced ref {full}: {e}"));
+ self.dids.insert(did);
+ }
+
+ /// Write a blob and return its [`Oid`].
+ pub fn blob(&self, content: &[u8]) -> Oid {
+ self.inner
+ .blob(content)
+ .expect("failed to write blob")
+ .into()
+ }
+
+ fn build_tree(&self, files: &[(&str, &[u8])]) -> super::Oid {
+ // Start from the empty tree, then apply updates for each file.
+ let empty_tree = {
+ let oid = self
+ .inner
+ .treebuilder(None)
+ .expect("treebuilder")
+ .write()
+ .expect("write empty tree");
+ self.inner.find_tree(oid).expect("find empty tree")
+ };
+
+ let mut builder = super::build::TreeUpdateBuilder::new();
+ for (path, content) in files {
+ let blob_oid = self.inner.blob(content).expect("failed to write blob");
+ builder.upsert(path, blob_oid, super::FileMode::Blob);
+ }
+
+ builder
+ .create_updated(&self.inner, &empty_tree)
+ .expect("failed to build tree")
+ }
+}
Exit code: 0
shell: 'export RUSTDOCFLAGS=''-D warnings'' cargo --version rustc --version cargo fmt --check cargo clippy --all-targets --workspace -- --deny warnings cargo build --all-targets --workspace cargo doc --workspace --no-deps --all-features cargo test --workspace --no-fail-fast '
Commands:
$ podman run --name 98b93ae1-1b2e-453f-92ac-4092c38802b5 -v /opt/radcis/ci.rad.levitte.org/cci/state/98b93ae1-1b2e-453f-92ac-4092c38802b5/s:/98b93ae1-1b2e-453f-92ac-4092c38802b5/s:ro -v /opt/radcis/ci.rad.levitte.org/cci/state/98b93ae1-1b2e-453f-92ac-4092c38802b5/w:/98b93ae1-1b2e-453f-92ac-4092c38802b5/w -w /98b93ae1-1b2e-453f-92ac-4092c38802b5/w -v /opt/radcis/ci.rad.levitte.org/.radicle:/${id}/.radicle:ro -e RAD_HOME=/${id}/.radicle rust:trixie bash /98b93ae1-1b2e-453f-92ac-4092c38802b5/s/script.sh
+ export 'RUSTDOCFLAGS=-D warnings'
+ RUSTDOCFLAGS='-D warnings'
+ cargo --version
info: syncing channel updates for '1.95-x86_64-unknown-linux-gnu'
info: latest update on 2026-04-16, rust version 1.95.0 (59807616e 2026-04-14)
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.95.0 (f2d3ce0bd 2026-03-21)
+ rustc --version
rustc 1.95.0 (59807616e 2026-04-14)
+ cargo fmt --check
+ cargo clippy --all-targets --workspace -- --deny warnings
Updating crates.io index
Downloading crates ...
Downloaded aead v0.5.2
Downloaded adler2 v2.0.1
Downloaded dunce v1.0.5
Downloaded anstyle-parse v1.0.0
Downloaded filetime v0.2.27
Downloaded group v0.13.0
Downloaded gix-path v0.12.0
Downloaded errno v0.3.14
Downloaded aes-gcm v0.10.3
Downloaded fnv v1.0.7
Downloaded ed25519 v2.2.3
Downloaded getrandom v0.4.2
Downloaded gix-negotiate v0.31.0
Downloaded icu_provider v2.1.1
Downloaded lock_api v0.4.14
Downloaded dyn-clone v1.0.20
Downloaded gix-quote v0.7.1
Downloaded match-lookup v0.1.2
Downloaded gix-validate v0.11.1
Downloaded gix-config-value v0.18.0
Downloaded elliptic-curve v0.13.8
Downloaded heck v0.5.0
Downloaded ed25519 v1.5.3
Downloaded memmap2 v0.9.10
Downloaded nonempty v0.9.0
Downloaded num-iter v0.1.45
Downloaded cyphergraphy v0.3.1
Downloaded bytesize v2.3.1
Downloaded memchr v2.8.0
Downloaded radicle-git-ext v0.12.0
Downloaded outref v0.5.2
Downloaded quick-error v1.2.3
Downloaded pin-project-lite v0.2.17
Downloaded radicle-std-ext v0.2.0
Downloaded rand_chacha v0.3.1
Downloaded pastey v0.2.1
Downloaded rfc6979 v0.4.0
Downloaded gix-glob v0.26.0
Downloaded rand_xorshift v0.4.0
Downloaded parking_lot v0.12.5
Downloaded shlex v1.3.0
Downloaded same-file v1.0.6
Downloaded rand_core v0.6.4
Downloaded secrecy v0.10.3
Downloaded sha1 v0.10.6
Downloaded normalize-line-endings v0.3.0
Downloaded num-bigint v0.4.6
Downloaded signature v2.2.0
Downloaded signal-hook-mio v0.2.5
Downloaded ryu v1.0.23
Downloaded signature v1.6.4
Downloaded ssh-encoding v0.2.0
Downloaded signal-hook-registry v1.4.8
Downloaded gix-tempfile v23.0.0
Downloaded subtle v2.6.1
Downloaded rsa v0.9.10
Downloaded sval_dynamic v2.17.0
Downloaded test-log v0.2.19
Downloaded synstructure v0.13.2
Downloaded sval_fmt v2.17.0
Downloaded tinyvec_macros v0.1.1
Downloaded sval_serde v2.17.0
Downloaded p384 v0.13.1
Downloaded toml_datetime v0.7.5+spec-1.1.0
Downloaded thiserror v2.0.18
Downloaded unit-prefix v0.5.2
Downloaded value-bag-serde1 v1.12.0
Downloaded uuid-simd v0.8.0
Downloaded strsim v0.11.1
Downloaded yoke-derive v0.8.1
Downloaded walkdir v2.5.0
Downloaded wait-timeout v0.2.1
Downloaded zerofrom v0.1.6
Downloaded litrs v1.0.0
Downloaded zeroize v1.8.2
Downloaded value-bag v1.12.0
Downloaded zerovec-derive v0.11.2
Downloaded zmij v1.0.21
Downloaded typenum v1.19.0
Downloaded unicode-segmentation v1.12.0
Downloaded uuid v1.22.0
Downloaded url v2.5.8
Downloaded zerotrie v0.2.3
Downloaded getrandom v0.2.17
Downloaded utf8parse v0.2.2
Downloaded jobserver v0.1.34
Downloaded hmac v0.12.1
Downloaded gix-features v0.48.0
Downloaded litemap v0.8.1
Downloaded xattr v1.6.1
Downloaded tinystr v0.8.2
Downloaded tracing-subscriber v0.3.23
Downloaded gix-protocol v0.61.0
Downloaded tree-sitter v0.24.7
Downloaded zlib-rs v0.6.3
Downloaded syn v1.0.109
Downloaded writeable v0.6.2
Downloaded gix-object v0.60.0
Downloaded yoke v0.8.1
Downloaded tree-sitter-md v0.3.2
Downloaded tempfile v3.27.0
Downloaded p256 v0.13.2
Downloaded icu_locale_core v2.1.1
Downloaded signal-hook v0.3.18
Downloaded similar v2.7.0
Downloaded tree-sitter-bash v0.23.3
Downloaded unicode-ident v1.0.24
Downloaded tree-sitter-css v0.23.2
Downloaded indicatif v0.18.4
Downloaded tar v0.4.45
Downloaded yansi v1.0.1
Downloaded rand v0.9.2
Downloaded prodash v31.0.0
Downloaded tree-sitter-go v0.23.4
Downloaded zerovec v0.11.5
Downloaded libm v0.2.16
Downloaded portable-atomic v1.13.1
Downloaded tree-sitter-python v0.23.6
Downloaded icu_properties_data v2.1.2
Downloaded unicode-normalization v0.1.25
Downloaded radicle-surf v0.27.1
Downloaded sha1-checked v0.10.0
Downloaded jsonschema v0.30.0
Downloaded zerocopy v0.8.42
Downloaded sha3 v0.10.8
Downloaded itertools v0.14.0
Downloaded tree-sitter-typescript v0.23.2
Downloaded tree-sitter-rust v0.23.3
Downloaded tokio v1.50.0
Downloaded hashbrown v0.16.1
Downloaded gimli v0.32.3
Downloaded indexmap v2.13.0
Downloaded git2 v0.20.4
Downloaded tracing v0.1.44
Downloaded idna v1.1.0
Downloaded fraction v0.15.3
Downloaded tree-sitter-ruby v0.23.1
Downloaded fancy-regex v0.14.0
Downloaded regex-automata v0.4.14
Downloaded rand v0.8.5
Downloaded inquire v0.9.4
Downloaded icu_collections v2.1.1
Downloaded heapless v0.8.0
Downloaded socket2 v0.5.10
Downloaded icu_normalizer_data v2.1.1
Downloaded gix-transport v0.57.0
Downloaded sharded-slab v0.1.7
Downloaded jiff v0.2.23
Downloaded icu_properties v2.1.2
Downloaded icu_normalizer v2.1.1
Downloaded libc v0.2.183
Downloaded gix-ref v0.63.0
Downloaded libz-sys v1.1.25
Downloaded getrandom v0.3.4
Downloaded syn v2.0.117
Downloaded rustix v1.1.4
Downloaded vcpkg v0.2.15
Downloaded unicode-width v0.2.2
Downloaded gix-packetline v0.21.3
Downloaded flate2 v1.1.9
Downloaded regex-syntax v0.8.10
Downloaded jiff-static v0.2.23
Downloaded tree-sitter-c v0.23.4
Downloaded referencing v0.30.0
Downloaded timeago v0.4.2
Downloaded systemd-journal-logger v2.2.2
Downloaded sysinfo v0.37.2
Downloaded structured-logger v1.0.5
Downloaded streaming-iterator v0.1.9
Downloaded object v0.37.3
Downloaded universal-hash v0.5.1
Downloaded unicode-display-width v0.3.0
Downloaded tree-sitter-language v0.1.7
Downloaded schemars_derive v1.2.1
Downloaded rustc-demangle v0.1.27
Downloaded polyval v0.6.2
Downloaded phf v0.11.3
Downloaded num-rational v0.4.2
Downloaded serde_json v1.0.149
Downloaded tinyvec v1.11.0
Downloaded gix-odb v0.80.0
Downloaded zerofrom-derive v0.1.6
Downloaded vsimd v0.8.0
Downloaded tracing-core v0.1.36
Downloaded iana-time-zone v0.1.65
Downloaded poly1305 v0.8.0
Downloaded gix-url v0.36.0
Downloaded bloomy v1.2.0
Downloaded tree-sitter-html v0.23.2
Downloaded proptest v1.10.0
Downloaded value-bag-sval2 v1.12.0
Downloaded tree-sitter-toml-ng v0.6.0
Downloaded toml v0.9.12+spec-1.1.0
Downloaded version_check v0.9.5
Downloaded tree-sitter-highlight v0.24.7
Downloaded utf8_iter v1.0.4
Downloaded tree-sitter-json v0.24.8
Downloaded sval v2.17.0
Downloaded ssh-key v0.6.7
Downloaded ssh-agent-lib v0.6.0
Downloaded regex v1.12.3
Downloaded gix-hash v0.25.0
Downloaded curve25519-dalek v4.1.3
Downloaded unarray v0.1.4
Downloaded typeid v1.0.3
Downloaded tracing-log v0.2.0
Downloaded libgit2-sys v0.18.3+1.9.2
Downloaded toml_writer v1.0.7+spec-1.1.0
Downloaded thiserror-impl v2.0.18
Downloaded phf_shared v0.11.3
Downloaded nu-ansi-term v0.50.3
Downloaded itoa v1.0.17
Downloaded gix-revwalk v0.31.0
Downloaded gix-commitgraph v0.37.0
Downloaded thiserror-impl v1.0.69
Downloaded thiserror v1.0.69
Downloaded test-log-macros v0.2.19
Downloaded sval_nested v2.17.0
Downloaded sval_json v2.17.0
Downloaded socks5-client v0.4.3
Downloaded siphasher v0.3.11
Downloaded gix-refspec v0.41.0
Downloaded bstr v1.12.1
Downloaded thread_local v1.1.9
Downloaded sval_ref v2.17.0
Downloaded gix-traverse v0.57.0
Downloaded gix-credentials v0.38.0
Downloaded sqlite3-sys v0.18.0
Downloaded sqlite v0.37.0
Downloaded serde v1.0.228
Downloaded schemars v1.2.1
Downloaded p521 v0.13.3
Downloaded chrono v0.4.44
Downloaded sval_buffer v2.17.0
Downloaded spin v0.9.8
Downloaded snapbox v0.4.17
Downloaded smallvec v1.15.1
Downloaded simd-adler32 v0.3.8
Downloaded sha2 v0.10.9
Downloaded sqlite3-src v0.7.0
Downloaded scopeguard v1.2.0
Downloaded ref-cast-impl v1.0.25
Downloaded ref-cast v1.0.25
Downloaded stable_deref_trait v1.2.1
Downloaded ssh-cipher v0.2.0
Downloaded snapbox-macros v0.3.10
Downloaded signals_receipts v0.2.5
Downloaded gix-fs v0.21.1
Downloaded spki v0.7.3
Downloaded siphasher v1.0.2
Downloaded serde_derive v1.0.228
Downloaded serde_core v1.0.228
Downloaded num-bigint-dig v0.8.6
Downloaded sem_safe v0.2.1
Downloaded sec1 v0.7.3
Downloaded pretty_assertions v1.4.1
Downloaded nonempty v0.12.0
Downloaded human-panic v2.0.6
Downloaded lexopt v0.3.2
Downloaded humantime v2.3.0
Downloaded gix-error v0.2.3
Downloaded gix-command v0.9.0
Downloaded shell-words v1.1.1
Downloaded serde_spanned v1.0.4
Downloaded serde_fmt v1.1.0
Downloaded serde_derive_internals v0.29.1
Downloaded serde-untagged v0.1.9
Downloaded semver v1.0.27
Downloaded rusty-fork v0.3.1
Downloaded aho-corasick v1.1.4
Downloaded gix-shallow v0.12.0
Downloaded clap_builder v4.6.0
Downloaded linux-raw-sys v0.12.1
Downloaded salsa20 v0.10.2
Downloaded rand_core v0.9.5
Downloaded scrypt v0.11.0
Downloaded rustversion v1.0.22
Downloaded is_terminal_polyfill v1.70.2
Downloaded idna_adapter v1.2.1
Downloaded git-ref-format-core v0.6.0
Downloaded mio v1.1.1
Downloaded inout v0.1.4
Downloaded gix-utils v0.3.2
Downloaded gix-lock v23.0.0
Downloaded rustc_version v0.4.1
Downloaded proc-macro2 v1.0.106
Downloaded gix-sec v0.14.0
Downloaded multibase v0.9.2
Downloaded gix-date v0.15.3
Downloaded rand_chacha v0.9.0
Downloaded primeorder v0.13.6
Downloaded parking_lot_core v0.9.12
Downloaded num-traits v0.2.19
Downloaded quote v1.0.45
Downloaded qcheck-macros v1.0.0
Downloaded proc-macro-error-attr2 v2.0.0
Downloaded cyphernet v0.5.4
Downloaded pkcs8 v0.10.2
Downloaded log v0.4.29
Downloaded faster-hex v0.10.0
Downloaded fast-glob v0.3.3
Downloaded ed25519-dalek v2.2.0
Downloaded der v0.7.10
Downloaded clap v4.6.0
Downloaded chacha20poly1305 v0.10.1
Downloaded qcheck v1.0.0
Downloaded proc-macro-error2 v2.0.1
Downloaded ppv-lite86 v0.2.21
Downloaded potential_utf v0.1.4
Downloaded pkg-config v0.3.32
Downloaded pkcs1 v0.7.5
Downloaded pbkdf2 v0.12.2
Downloaded miniz_oxide v0.8.9
Downloaded keccak v0.1.6
Downloaded fluent-uri v0.3.2
Downloaded escargot v0.5.15
Downloaded pem-rfc7468 v0.7.0
Downloaded num-complex v0.4.6
Downloaded emojis v0.6.4
Downloaded percent-encoding v2.3.2
Downloaded hash32 v0.3.1
Downloaded gix-hashtable v0.15.0
Downloaded fastrand v2.3.0
Downloaded cc v1.2.57
Downloaded once_cell v1.21.4
Downloaded noise-framework v0.4.1
Downloaded digest v0.10.7
Downloaded cbc v0.1.2
Downloaded bit-vec v0.8.0
Downloaded amplify_num v0.5.3
Downloaded opaque-debug v0.3.1
Downloaded num-integer v0.1.46
Downloaded num-cmp v0.1.0
Downloaded lazy_static v1.5.0
Downloaded form_urlencoded v1.2.2
Downloaded matchers v0.2.0
Downloaded git-ref-format v0.6.0
Downloaded ghash v0.5.1
Downloaded crossterm v0.29.0
Downloaded backtrace v0.3.76
Downloaded num v0.4.3
Downloaded gix-pack v0.70.0
Downloaded maybe-async v0.2.10
Downloaded gix-revision v0.45.0
Downloaded crossbeam-channel v0.5.15
Downloaded clap_complete v4.6.0
Downloaded anstyle v1.0.14
Downloaded erased-serde v0.4.10
Downloaded equivalent v1.0.2
Downloaded data-encoding v2.10.0
Downloaded convert_case v0.10.0
Downloaded colored v2.2.0
Downloaded amplify v4.9.0
Downloaded data-encoding-macro-internal v0.1.17
Downloaded bytes v1.11.1
Downloaded blowfish v0.9.1
Downloaded amplify_derive v4.0.1
Downloaded find-msvc-tools v0.1.9
Downloaded env_filter v1.0.0
Downloaded ecdsa v0.16.9
Downloaded data-encoding-macro v0.1.19
Downloaded crc32fast v1.5.0
Downloaded clap_derive v4.6.0
Downloaded base256emoji v1.0.2
Downloaded anstream v0.6.21
Downloaded displaydoc v0.2.5
Downloaded bytecount v0.6.9
Downloaded base64ct v1.8.3
Downloaded base64 v0.22.1
Downloaded base64 v0.21.7
Downloaded gix-trace v0.1.19
Downloaded git-ref-format-macro v0.6.0
Downloaded curve25519-dalek-derive v0.1.1
Downloaded cpufeatures v0.2.17
Downloaded base16ct v0.2.0
Downloaded ascii v1.1.0
Downloaded gix-chunk v0.7.1
Downloaded gix-actor v0.41.0
Downloaded ct-codecs v1.1.6
Downloaded derive_more-impl v2.1.1
Downloaded cypheraddr v0.4.1
Downloaded colorchoice v1.0.5
Downloaded block-buffer v0.10.4
Downloaded ahash v0.8.12
Downloaded gix-diff v0.63.0
Downloaded ec25519 v0.1.0
Downloaded crypto-common v0.1.7
Downloaded crossbeam-utils v0.8.21
Downloaded cfg-if v1.0.4
Downloaded bitflags v2.11.0
Downloaded bcrypt-pbkdf v0.10.0
Downloaded gix-prompt v0.15.0
Downloaded ff v0.13.1
Downloaded const-str v0.4.3
Downloaded clap_lex v1.1.0
Downloaded chacha20 v0.9.1
Downloaded amplify_syn v2.0.1
Downloaded generic-array v0.14.7
Downloaded env_logger v0.11.9
Downloaded email_address v0.2.9
Downloaded document-features v0.2.12
Downloaded cipher v0.4.4
Downloaded byteorder v1.5.0
Downloaded base32 v0.4.0
Downloaded base-x v0.2.11
Downloaded autocfg v1.5.0
Downloaded block-padding v0.3.3
Downloaded const-oid v0.9.6
Downloaded anyhow v1.0.102
Downloaded anstyle-query v1.1.5
Downloaded either v1.15.0
Downloaded derive_more v2.1.1
Downloaded ctr v0.9.2
Downloaded console v0.16.3
Downloaded borrow-or-share v0.2.4
Downloaded bit-set v0.8.0
Downloaded arc-swap v1.9.1
Downloaded aes v0.8.4
Downloaded diff v0.1.13
Downloaded crypto-bigint v0.5.5
Downloaded addr2line v0.25.1
Downloaded anstyle-parse v0.2.7
Downloaded anstream v1.0.0
Compiling libc v0.2.183
Compiling proc-macro2 v1.0.106
Compiling quote v1.0.45
Compiling unicode-ident v1.0.24
Checking cfg-if v1.0.4
Checking zeroize v1.8.2
Compiling version_check v0.9.5
Compiling typenum v1.19.0
Compiling generic-array v0.14.7
Compiling syn v2.0.117
Checking getrandom v0.2.17
Checking rand_core v0.6.4
Checking memchr v2.8.0
Compiling jobserver v0.1.34
Compiling find-msvc-tools v0.1.9
Compiling shlex v1.3.0
Checking crypto-common v0.1.7
Checking subtle v2.6.1
Compiling cc v1.2.57
Compiling serde_core v1.0.228
Checking regex-syntax v0.8.10
Checking aho-corasick v1.1.4
Checking const-oid v0.9.6
Checking smallvec v1.15.1
Checking block-buffer v0.10.4
Checking digest v0.10.7
Checking cpufeatures v0.2.17
Checking stable_deref_trait v1.2.1
Compiling thiserror v2.0.18
Checking fastrand v2.3.0
Checking regex-automata v0.4.14
Compiling parking_lot_core v0.9.12
Checking scopeguard v1.2.0
Checking lock_api v0.4.14
Checking bitflags v2.11.0
Checking parking_lot v0.12.5
Compiling typeid v1.0.3
Compiling crc32fast v1.5.0
Compiling erased-serde v0.4.10
Checking gix-trace v0.1.19
Checking tinyvec_macros v0.1.1
Checking tinyvec v1.11.0
Compiling serde v1.0.228
Checking unicode-normalization v0.1.25
Checking itoa v1.0.17
Checking byteorder v1.5.0
Checking gix-utils v0.3.2
Compiling thiserror-impl v2.0.18
Compiling serde_derive v1.0.228
Checking serde_fmt v1.1.0
Checking hashbrown v0.16.1
Checking bstr v1.12.1
Checking value-bag-serde1 v1.12.0
Compiling synstructure v0.13.2
Checking gix-validate v0.11.1
Checking value-bag v1.12.0
Checking gix-path v0.12.0
Checking log v0.4.29
Checking same-file v1.0.6
Compiling zerofrom-derive v0.1.6
Checking walkdir v2.5.0
Compiling yoke-derive v0.8.1
Checking prodash v31.0.0
Checking zlib-rs v0.6.3
Compiling rustix v1.1.4
Checking zerofrom v0.1.6
Checking yoke v0.8.1
Compiling pkg-config v0.3.32
Compiling heapless v0.8.0
Checking gix-features v0.48.0
Checking hash32 v0.3.1
Compiling zerovec-derive v0.11.2
Compiling libm v0.2.16
Checking linux-raw-sys v0.12.1
Compiling autocfg v1.5.0
Compiling num-traits v0.2.19
Compiling displaydoc v0.2.5
Compiling getrandom v0.4.2
Checking faster-hex v0.10.0
Checking zerovec v0.11.5
Checking block-padding v0.3.3
Compiling zerocopy v0.8.42
Checking inout v0.1.4
Checking sha1 v0.10.6
Checking sha2 v0.10.9
Checking sha1-checked v0.10.0
Checking cipher v0.4.4
Checking tinystr v0.8.2
Checking percent-encoding v2.3.2
Checking once_cell v1.21.4
Checking writeable v0.6.2
Checking litemap v0.8.1
Checking gix-hash v0.25.0
Checking icu_locale_core v2.1.1
Checking potential_utf v0.1.4
Checking zerotrie v0.2.3
Compiling zmij v1.0.21
Compiling icu_normalizer_data v2.1.1
Compiling icu_properties_data v2.1.2
Checking icu_provider v2.1.1
Checking icu_collections v2.1.1
Checking der v0.7.10
Checking equivalent v1.0.2
Compiling serde_json v1.0.149
Checking indexmap v2.13.0
Compiling syn v1.0.109
Compiling thiserror v1.0.69
Compiling ref-cast v1.0.25
Compiling vcpkg v0.2.15
Checking icu_normalizer v2.1.1
Checking icu_properties v2.1.2
Compiling libz-sys v1.1.25
Checking tempfile v3.27.0
Checking ppv-lite86 v0.2.21
Compiling ref-cast-impl v1.0.25
Compiling thiserror-impl v1.0.69
Checking spin v0.9.8
Checking lazy_static v1.5.0
Checking num-integer v0.1.46
Checking idna_adapter v1.2.1
Checking hmac v0.12.1
Checking universal-hash v0.5.1
Checking dyn-clone v1.0.20
Checking opaque-debug v0.3.1
Checking utf8_iter v1.0.4
Compiling tree-sitter-language v0.1.7
Checking idna v1.1.0
Checking spki v0.7.3
Compiling libgit2-sys v0.18.3+1.9.2
Checking signature v2.2.0
Checking ff v0.13.1
Checking base16ct v0.2.0
Checking group v0.13.0
Checking sec1 v0.7.3
Checking rand_chacha v0.3.1
Checking form_urlencoded v1.2.2
Compiling serde_derive_internals v0.29.1
Checking crypto-bigint v0.5.5
Compiling schemars_derive v1.2.1
Checking elliptic-curve v0.13.8
Compiling amplify_syn v2.0.1
Checking rand v0.8.5
Checking url v2.5.8
Checking num-iter v0.1.45
Checking aead v0.5.2
Compiling semver v1.0.27
Checking signature v1.6.4
Checking ed25519 v1.5.3
Checking schemars v1.2.1
Compiling rustc_version v0.4.1
Compiling amplify_derive v4.0.1
Checking poly1305 v0.8.0
Checking rfc6979 v0.4.0
Checking chacha20 v0.9.1
Checking ct-codecs v1.1.6
Checking ascii v1.1.0
Checking amplify_num v0.5.3
Checking ec25519 v0.1.0
Checking ecdsa v0.16.9
Compiling curve25519-dalek v4.1.3
Checking git-ref-format-core v0.6.0
Checking primeorder v0.13.6
Checking amplify v4.9.0
Checking polyval v0.6.2
Compiling num-bigint-dig v0.8.6
Checking base64ct v1.8.3
Checking ghash v0.5.1
Checking cyphergraphy v0.3.1
Checking pem-rfc7468 v0.7.0
Checking pkcs8 v0.10.2
Checking pbkdf2 v0.12.2
Checking ctr v0.9.2
Checking aes v0.8.4
Compiling sqlite3-src v0.7.0
Checking gix-error v0.2.3
Compiling curve25519-dalek-derive v0.1.1
Checking keccak v0.1.6
Checking aes-gcm v0.10.3
Checking sha3 v0.10.8
Checking pkcs1 v0.7.5
Checking ssh-encoding v0.2.0
Checking ed25519 v2.2.3
Checking blowfish v0.9.1
Checking cbc v0.1.2
Checking base32 v0.4.0
Compiling data-encoding v2.10.0
Compiling crossbeam-utils v0.8.21
Checking rsa v0.9.10
Compiling data-encoding-macro-internal v0.1.17
Checking cypheraddr v0.4.1
Checking ssh-cipher v0.2.0
Checking bcrypt-pbkdf v0.10.0
Checking ed25519-dalek v2.2.0
Checking p384 v0.13.1
Checking p521 v0.13.3
Checking p256 v0.13.2
Checking chacha20poly1305 v0.10.1
Checking qcheck v1.0.0
Compiling match-lookup v0.1.2
Checking const-str v0.4.3
Checking data-encoding-macro v0.1.19
Checking base256emoji v1.0.2
Checking noise-framework v0.4.1
Checking ssh-key v0.6.7
Checking socks5-client v0.4.3
Checking secrecy v0.10.3
Checking base-x v0.2.11
Checking multibase v0.9.2
Checking ssh-agent-lib v0.6.0
Checking crossbeam-channel v0.5.15
Checking cyphernet v0.5.4
Checking anstyle-query v1.1.5
Checking errno v0.3.14
Checking utf8parse v0.2.2
Checking jiff v0.2.23
Checking nonempty v0.9.0
Checking siphasher v1.0.2
Checking radicle-localtime v0.1.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle-localtime)
Checking radicle-git-metadata v0.2.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle-git-metadata)
Checking radicle-dag v0.10.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle-dag)
Checking anstyle v1.0.14
Checking is_terminal_polyfill v1.70.2
Checking colorchoice v1.0.5
Checking radicle-git-ref-format v0.1.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle-git-ref-format)
Checking gix-hashtable v0.15.0
Compiling unicode-segmentation v1.12.0
Checking base64 v0.21.7
Compiling radicle v0.24.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle)
Compiling signal-hook v0.3.18
Compiling convert_case v0.10.0
Checking gix-date v0.15.3
Checking signal-hook-registry v1.4.8
Checking gix-actor v0.41.0
Checking serde-untagged v0.1.9
Checking gix-object v0.60.0
Checking bytesize v2.3.1
Checking memmap2 v0.9.10
Checking dunce v1.0.5
Checking fast-glob v0.3.3
Checking nonempty v0.12.0
Compiling derive_more-impl v2.1.1
Checking gix-chunk v0.7.1
Checking mio v1.1.1
Checking regex v1.12.3
Checking sem_safe v0.2.1
Compiling portable-atomic v1.13.1
Checking unicode-width v0.2.2
Compiling litrs v1.0.0
Checking signals_receipts v0.2.5
Checking derive_more v2.1.1
Checking signal-hook-mio v0.2.5
Compiling document-features v0.2.12
Checking gix-commitgraph v0.37.0
Checking anstyle-parse v0.2.7
Checking crossterm v0.29.0
Checking anstream v0.6.21
Checking gix-revwalk v0.31.0
Checking console v0.16.3
Checking gix-fs v0.21.1
Checking unit-prefix v0.5.2
Checking indicatif v0.18.4
Checking gix-tempfile v23.0.0
Checking inquire v0.9.4
Checking unicode-display-width v0.3.0
Checking radicle-signals v0.11.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle-signals)
Checking gix-quote v0.7.1
Checking either v1.15.0
Checking shell-words v1.1.1
Checking iana-time-zone v0.1.65
Checking gix-command v0.9.0
Checking chrono v0.4.44
Checking colored v2.2.0
Compiling rustversion v1.0.22
Compiling object v0.37.3
Checking gix-lock v23.0.0
Checking gix-url v0.36.0
Checking gix-config-value v0.18.0
Checking gix-sec v0.14.0
Checking gimli v0.32.3
Checking adler2 v2.0.1
Checking miniz_oxide v0.8.9
Checking gix-prompt v0.15.0
Checking addr2line v0.25.1
Checking gix-revision v0.45.0
Checking gix-traverse v0.57.0
Checking gix-diff v0.63.0
Checking gix-packetline v0.21.3
Checking gix-glob v0.26.0
Compiling tree-sitter v0.24.7
Compiling anyhow v1.0.102
Checking rustc-demangle v0.1.27
Checking backtrace v0.3.76
Checking gix-refspec v0.41.0
Checking gix-transport v0.57.0
Checking gix-pack v0.70.0
Checking arc-swap v1.9.1
Checking gix-credentials v0.38.0
Checking gix-shallow v0.12.0
Checking gix-ref v0.63.0
Checking gix-negotiate v0.31.0
Compiling maybe-async v0.2.10
Compiling proc-macro-error-attr2 v2.0.0
Compiling simd-adler32 v0.3.8
Compiling getrandom v0.3.4
Checking gix-protocol v0.61.0
Compiling proc-macro-error2 v2.0.1
Checking gix-odb v0.80.0
Compiling xattr v1.6.1
Compiling filetime v0.2.27
Checking anstyle-parse v1.0.0
Checking uuid v1.22.0
Checking bytes v1.11.1
Checking anstream v1.0.0
Compiling flate2 v1.1.9
Compiling tar v0.4.45
Compiling git-ref-format-macro v0.6.0
Checking snapbox-macros v0.3.10
Checking salsa20 v0.10.2
Checking streaming-iterator v0.1.9
Checking similar v2.7.0
Checking clap_lex v1.1.0
Compiling heck v0.5.0
Checking normalize-line-endings v0.3.0
Checking strsim v0.11.1
Checking siphasher v0.3.11
Checking bloomy v1.2.0
Checking clap_builder v4.6.0
Compiling clap_derive v4.6.0
Checking snapbox v0.4.17
Compiling radicle-surf v0.27.1
Checking scrypt v0.11.0
Checking git-ref-format v0.6.0
Checking systemd-journal-logger v2.2.2
Checking toml_datetime v0.7.5+spec-1.1.0
Checking serde_spanned v1.0.4
Compiling tree-sitter-css v0.23.2
Compiling tree-sitter-json v0.24.8
Compiling tree-sitter-md v0.3.2
Compiling tree-sitter-ruby v0.23.1
Compiling tree-sitter-python v0.23.6
Compiling tree-sitter-toml-ng v0.6.0
Checking sqlite3-sys v0.18.0
Compiling tree-sitter-c v0.23.4
Compiling tree-sitter-rust v0.23.3
Checking sqlite v0.37.0
Compiling tree-sitter-bash v0.23.3
Compiling tree-sitter-html v0.23.2
Checking radicle-crypto v0.17.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle-crypto)
Compiling tree-sitter-typescript v0.23.2
Compiling tree-sitter-go v0.23.4
Checking toml_writer v1.0.7+spec-1.1.0
Checking pin-project-lite v0.2.17
Checking radicle-std-ext v0.2.0
Checking toml v0.9.12+spec-1.1.0
Checking tokio v1.50.0
Checking clap v4.6.0
Checking sysinfo v0.37.2
Compiling radicle-node v0.20.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle-node)
Compiling radicle-cli v0.21.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle-cli)
Checking yansi v1.0.1
Checking diff v0.1.13
Checking pretty_assertions v1.4.1
Checking human-panic v2.0.6
Checking clap_complete v4.6.0
Checking structured-logger v1.0.5
Checking radicle-systemd v0.13.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle-systemd)
Checking tree-sitter-highlight v0.24.7
Checking itertools v0.14.0
Compiling qcheck-macros v1.0.0
Checking socket2 v0.5.10
Checking timeago v0.4.2
Checking humantime v2.3.0
Compiling escargot v0.5.15
Checking lexopt v0.3.2
Checking bit-vec v0.8.0
Checking bit-set v0.8.0
Checking rand_core v0.9.5
Checking num-bigint v0.4.6
Compiling ahash v0.8.12
Checking num-complex v0.4.6
Checking env_filter v1.0.0
Checking borrow-or-share v0.2.4
Checking env_logger v0.11.9
Checking fluent-uri v0.3.2
Checking phf_shared v0.11.3
Compiling test-log-macros v0.2.19
Checking wait-timeout v0.2.1
Checking num-rational v0.4.2
Checking vsimd v0.8.0
Checking outref v0.5.2
Checking num v0.4.3
Compiling radicle-remote-helper v0.17.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle-remote-helper)
Checking fnv v1.0.7
Checking quick-error v1.2.3
Checking uuid-simd v0.8.0
Checking rusty-fork v0.3.1
Checking fraction v0.15.3
Checking test-log v0.2.19
Checking phf v0.11.3
Checking referencing v0.30.0
Checking rand_xorshift v0.4.0
Checking rand v0.9.2
Checking rand_chacha v0.9.0
Checking fancy-regex v0.14.0
Checking email_address v0.2.9
Checking base64 v0.22.1
Checking bytecount v0.6.9
Checking unarray v0.1.4
Checking num-cmp v0.1.0
Checking emojis v0.6.4
Checking proptest v1.10.0
Checking jsonschema v0.30.0
Compiling pastey v0.2.1
Checking radicle-windows v0.1.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle-windows)
Checking git2 v0.20.4
Checking radicle-oid v0.2.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle-oid)
Checking radicle-term v0.18.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle-term)
Checking radicle-git-ext v0.12.0
Checking radicle-cob v0.20.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle-cob)
Checking radicle-core v0.3.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle-core)
Checking radicle-log v0.1.0 (/98b93ae1-1b2e-453f-92ac-4092c38802b5/w/crates/radicle-log)
error[E0433]: cannot find module or crate `read` in this scope
--> crates/radicle/src/git/repository/adapter/git2/object.rs:69:67
|
69 | fn object_kind(&self, oid: Oid) -> Result<Option<ObjectKind>, read::ObjectKind> {
| ^^^^ use of unresolved module or unlinked crate `read`
|
= help: if you wanted to use a crate named `read`, use `cargo add read` to add it to your `Cargo.toml`
note: module `crate::storage::git::trailers::object::error::read` exists but is inaccessible
--> crates/radicle/src/git/repository/object/error.rs:3:1
|
3 | pub mod read;
| ^^^^^^^^^^^^^ not accessible
error[E0433]: cannot find module or crate `read` in this scope
--> crates/radicle/src/git/repository/adapter/git2/object.rs:70:38
|
70 | let odb = self.odb().map_err(read::ObjectKind::backend)?;
| ^^^^ use of unresolved module or unlinked crate `read`
|
= help: if you wanted to use a crate named `read`, use `cargo add read` to add it to your `Cargo.toml`
note: these enums exist but are inaccessible
--> crates/radicle/src/git/repository/object/error/read.rs:130:1
|
130 | pub enum ObjectKind {
| ^^^^^^^^^^^^^^^^^^^ `crate::storage::git::trailers::object::error::read::ObjectKind`: not accessible
|
::: crates/radicle/src/git/repository/types.rs:19:1
|
19 | pub enum ObjectKind {
| ^^^^^^^^^^^^^^^^^^^ `crate::storage::git::trailers::repository::ObjectKind`: not accessible
error[E0433]: cannot find module or crate `read` in this scope
--> crates/radicle/src/git/repository/adapter/git2/object.rs:74:27
|
74 | Err(e) => Err(read::ObjectKind::backend(e)),
| ^^^^ use of unresolved module or unlinked crate `read`
|
= help: if you wanted to use a crate named `read`, use `cargo add read` to add it to your `Cargo.toml`
note: these enums exist but are inaccessible
--> crates/radicle/src/git/repository/object/error/read.rs:130:1
|
130 | pub enum ObjectKind {
| ^^^^^^^^^^^^^^^^^^^ `crate::storage::git::trailers::object::error::read::ObjectKind`: not accessible
|
::: crates/radicle/src/git/repository/types.rs:19:1
|
19 | pub enum ObjectKind {
| ^^^^^^^^^^^^^^^^^^^ `crate::storage::git::trailers::repository::ObjectKind`: not accessible
error[E0433]: cannot find module or crate `read` in this scope
--> crates/radicle/src/git/repository/adapter/git2/object.rs:69:67
|
69 | fn object_kind(&self, oid: Oid) -> Result<Option<ObjectKind>, read::ObjectKind> {
| ^^^^ use of unresolved module or unlinked crate `read`
|
= help: if you wanted to use a crate named `read`, use `cargo add read` to add it to your `Cargo.toml`
note: module `crate::storage::git::tests::object::error::read` exists but is inaccessible
--> crates/radicle/src/git/repository/object/error.rs:3:1
|
3 | pub mod read;
| ^^^^^^^^^^^^^ not accessible
error[E0433]: cannot find module or crate `read` in this scope
--> crates/radicle/src/git/repository/adapter/git2/object.rs:70:38
|
70 | let odb = self.odb().map_err(read::ObjectKind::backend)?;
| ^^^^ use of unresolved module or unlinked crate `read`
|
= help: if you wanted to use a crate named `read`, use `cargo add read` to add it to your `Cargo.toml`
note: these enums exist but are inaccessible
--> crates/radicle/src/git/repository/object/error/read.rs:130:1
|
130 | pub enum ObjectKind {
| ^^^^^^^^^^^^^^^^^^^ `crate::storage::git::tests::object::error::read::ObjectKind`: not accessible
|
::: crates/radicle/src/git/repository/types.rs:19:1
|
19 | pub enum ObjectKind {
| ^^^^^^^^^^^^^^^^^^^ `crate::storage::git::tests::repository::ObjectKind`: not accessible
error[E0433]: cannot find module or crate `read` in this scope
--> crates/radicle/src/git/repository/adapter/git2/object.rs:74:27
|
74 | Err(e) => Err(read::ObjectKind::backend(e)),
| ^^^^ use of unresolved module or unlinked crate `read`
|
= help: if you wanted to use a crate named `read`, use `cargo add read` to add it to your `Cargo.toml`
note: these enums exist but are inaccessible
--> crates/radicle/src/git/repository/object/error/read.rs:130:1
|
130 | pub enum ObjectKind {
| ^^^^^^^^^^^^^^^^^^^ `crate::storage::git::tests::object::error::read::ObjectKind`: not accessible
|
::: crates/radicle/src/git/repository/types.rs:19:1
|
19 | pub enum ObjectKind {
| ^^^^^^^^^^^^^^^^^^^ `crate::storage::git::tests::repository::ObjectKind`: not accessible
For more information about this error, try `rustc --explain E0433`.
error: could not compile `radicle` (lib) due to 3 previous errors
warning: build failed, waiting for other jobs to finish...
error: could not compile `radicle` (lib test) due to 3 previous errors
Exit code: 101
{
"response": "finished",
"result": "failure"
}