Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
Update gix-* crates
Merged fintohaps opened 2 months ago

Fixes CVE-2026-0810.

The gix crates require updating due to the security vulnerability above. They require updating together in lock-step so both radicle-fetch and radicle-oid are affected.

radicle-oid handles the non-exhaustive nature of ObjectId.

radicle-fetch updates to the new API types, however, this comes with some updates to the ls_refs protocol.

A regression is documented in gitoxide issue 2429. The regression highlighted that it is the duty of the caller to also filter the outcome of ls-refs, and ref-prefix is simply an optimisation.

The ls-refs code is refactored to better represent and handle this operation.

11 files changed +501 -278 980ed561 84320919
modified Cargo.lock
@@ -192,9 +192,12 @@ checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"

[[package]]
name = "arc-swap"
-
version = "1.7.1"
+
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
+
checksum = "f9f3647c145568cec02c42054e07bdf9a5a698e15b466fb2341bfc393cd24aa5"
+
dependencies = [
+
 "rustversion",
+
]

[[package]]
name = "ascii"
@@ -617,7 +620,7 @@ dependencies = [
 "document-features",
 "mio 1.0.4",
 "parking_lot",
-
 "rustix 1.0.7",
+
 "rustix 1.1.3",
 "signal-hook",
 "signal-hook-mio",
 "winapi",
@@ -1023,7 +1026,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
dependencies = [
 "crc32fast",
-
 "libz-rs-sys",
 "miniz_oxide",
]

@@ -1170,32 +1172,53 @@ dependencies = [

[[package]]
name = "gix-actor"
-
version = "0.35.4"
+
version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "2d36dcf9efe32b51b12dfa33cedff8414926124e760a32f9e7a6b5580d280967"
+
checksum = "b50ce5433eaa46187349e59089eea71b0397caa71991b2fa3e124120426d7d15"
dependencies = [
 "bstr",
-
 "gix-date",
+
 "gix-date 0.13.0",
 "gix-utils",
 "itoa",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
+
 "winnow",
+
]
+

+
[[package]]
+
name = "gix-actor"
+
version = "0.39.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "0c44f13049925e8dc3955c20ecec5391cedfb041e0952416b583ecc57bc68325"
+
dependencies = [
+
 "bstr",
+
 "gix-date 0.14.0",
+
 "gix-error 0.1.0",
 "winnow",
]

[[package]]
name = "gix-chunk"
-
version = "0.4.11"
+
version = "0.5.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "63e516efaac951ed21115b11d5514b120c26ccb493d0c0b9ea6cc10edf4fdf44"
+
dependencies = [
+
 "gix-error 0.0.0",
+
]
+

+
[[package]]
+
name = "gix-chunk"
+
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "0b1f1d8764958699dc764e3f727cef280ff4d1bd92c107bbf8acd85b30c1bd6f"
+
checksum = "d14ee09ab454481a91fe969ca5afbd41c8a9b05680197b6554ebb69bdcf7d571"
dependencies = [
-
 "thiserror 2.0.17",
+
 "gix-error 0.1.0",
]

[[package]]
name = "gix-command"
-
version = "0.6.2"
+
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "6b31b65ca48a352ae86312b27a514a0c661935f96b481ac8b4371f65815eb196"
+
checksum = "2962172c6f78731e2b7773bf762f7b8d1746a342a4c0a8914a612206e1295953"
dependencies = [
 "bstr",
 "gix-path",
@@ -1206,163 +1229,240 @@ dependencies = [

[[package]]
name = "gix-commitgraph"
-
version = "0.29.0"
+
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "6bb23121e952f43a5b07e3e80890336cb847297467a410475036242732980d06"
+
checksum = "d0dda2e4d5a61d4a16a780f61f2b7e9406ad1f8da97c35c09ef501f3fdf74de0"
dependencies = [
 "bstr",
-
 "gix-chunk",
+
 "gix-chunk 0.5.0",
+
 "gix-error 0.0.0",
+
 "gix-hash",
+
 "memmap2",
+
]
+

+
[[package]]
+
name = "gix-commitgraph"
+
version = "0.33.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "f9dc2a550978b510b4e58b0bf5a15481433c3b21981330f3898d93f25b07d9a5"
+
dependencies = [
+
 "bstr",
+
 "gix-chunk 0.6.0",
+
 "gix-error 0.1.0",
 "gix-hash",
 "memmap2",
-
 "thiserror 2.0.17",
]

[[package]]
name = "gix-config-value"
-
version = "0.15.1"
+
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "9f012703eb67e263c6c1fc96649fec47694dd3e5d2a91abfc65e4a6a6dc85309"
+
checksum = "441a300bc3645a1f45cba495b9175f90f47256ce43f2ee161da0031e3ac77c92"
dependencies = [
 "bitflags 2.9.1",
 "bstr",
 "gix-path",
 "libc",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-credentials"
-
version = "0.30.0"
+
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "0039dd3ac606dd80b16353a41b61fc237ca5cb8b612f67a9f880adfad4be4e05"
+
checksum = "64b5ef8d1d86b9598df695fd61989e535dc7d139040ed9f31944bc7dcd81b713"
dependencies = [
 "bstr",
 "gix-command",
 "gix-config-value",
-
 "gix-date",
+
 "gix-date 0.14.0",
 "gix-path",
 "gix-prompt",
 "gix-sec",
 "gix-trace",
 "gix-url",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-date"
-
version = "0.10.5"
+
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "996b6b90bafb287330af92b274c3e64309dc78359221d8612d11cd10c8b9fe1c"
+
checksum = "12553b32d1da25671f31c0b084bf1e5cb6d5ef529254d04ec33cdc890bd7f687"
dependencies = [
 "bstr",
+
 "gix-error 0.0.0",
+
 "itoa",
+
 "jiff",
+
 "smallvec",
+
]
+

+
[[package]]
+
name = "gix-date"
+
version = "0.14.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "e66a5117b22495fe7cb4b443777cf3f024a1b1db0009771db440fc8b38a0a6fd"
+
dependencies = [
+
 "bstr",
+
 "gix-error 0.1.0",
 "itoa",
 "jiff",
 "smallvec",
-
 "thiserror 2.0.17",
]

[[package]]
name = "gix-diff"
-
version = "0.53.0"
+
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "de854852010d44a317f30c92d67a983e691c9478c8a3fb4117c1f48626bcdea8"
+
checksum = "26bcd367b2c5dbf6bec9ce02ca59eab179fc82cf39f15ec83549ee25c255c99f"
dependencies = [
 "bstr",
 "gix-hash",
-
 "gix-object",
-
 "thiserror 2.0.17",
+
 "gix-object 0.55.0",
+
 "thiserror 2.0.18",
+
]
+

+
[[package]]
+
name = "gix-error"
+
version = "0.0.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "7dffc9ca4dfa4f519a3d2cf1c038919160544923577ac60f45bcb602a24d82c6"
+
dependencies = [
+
 "bstr",
+
]
+

+
[[package]]
+
name = "gix-error"
+
version = "0.1.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "92e37b10e97c822fc17550fc1a187283ad3ce79617e920bf179f301ee12abcbc"
+
dependencies = [
+
 "bstr",
]

[[package]]
name = "gix-features"
-
version = "0.43.1"
+
version = "0.46.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "cd1543cd9b8abcbcebaa1a666a5c168ee2cda4dea50d3961ee0e6d1c42f81e5b"
+
checksum = "a83a5fe8927de3bb02b0cfb87165dbfb49f04d4c297767443f2e1011ecc15bdd"
dependencies = [
 "crc32fast",
-
 "flate2",
 "gix-path",
 "gix-trace",
 "gix-utils",
 "libc",
 "prodash",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
 "walkdir",
+
 "zlib-rs",
]

[[package]]
name = "gix-fs"
-
version = "0.16.1"
+
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "9a4d90307d064fa7230e0f87b03231be28f8ba63b913fc15346f489519d0c304"
+
checksum = "de4bd0d8e6c6ef03485205f8eecc0359042a866d26dba569075db1ebcc005970"
dependencies = [
 "bstr",
 "fastrand",
 "gix-features",
 "gix-path",
 "gix-utils",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
+
]
+

+
[[package]]
+
name = "gix-glob"
+
version = "0.24.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "b03e6cd88cc0dc1eafa1fddac0fb719e4e74b6ea58dd016e71125fde4a326bee"
+
dependencies = [
+
 "bitflags 2.9.1",
+
 "bstr",
+
 "gix-features",
+
 "gix-path",
]

[[package]]
name = "gix-hash"
-
version = "0.19.0"
+
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "251fad79796a731a2a7664d9ea95ee29a9e99474de2769e152238d4fdb69d50e"
+
checksum = "d8ced05d2d7b13bff08b2f7eb4e47cfeaf00b974c2ddce08377c4fe1f706b3eb"
dependencies = [
 "faster-hex",
 "gix-features",
 "sha1-checked",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-hashtable"
-
version = "0.9.0"
+
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "c35300b54896153e55d53f4180460931ccd69b7e8d2f6b9d6401122cdedc4f07"
+
checksum = "52f1eecdd006390cbed81f105417dbf82a6fe40842022006550f2e32484101da"
dependencies = [
 "gix-hash",
-
 "hashbrown 0.15.5",
+
 "hashbrown 0.16.1",
 "parking_lot",
]

[[package]]
name = "gix-lock"
-
version = "18.0.0"
+
version = "21.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "b9fa71da90365668a621e184eb5b979904471af1b3b09b943a84bc50e8ad42ed"
+
checksum = "cbe09cf05ba7c679bba189acc29eeea137f643e7fff1b5dff879dfd45248be31"
dependencies = [
 "gix-tempfile",
 "gix-utils",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-negotiate"
-
version = "0.21.0"
+
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "1d58d4c9118885233be971e0d7a589f5cfb1a8bd6cb6e2ecfb0fc6b1b293c83b"
+
checksum = "68d01c3303ed336b72eb3a48543c028be4650279ff3c3b561f13fa9d2b1979e7"
dependencies = [
 "bitflags 2.9.1",
-
 "gix-commitgraph",
-
 "gix-date",
+
 "gix-commitgraph 0.33.0",
+
 "gix-date 0.14.0",
+
 "gix-hash",
+
 "gix-object 0.56.0",
+
 "gix-revwalk 0.27.0",
+
 "smallvec",
+
 "thiserror 2.0.18",
+
]
+

+
[[package]]
+
name = "gix-object"
+
version = "0.55.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "4d3f705c977d90ace597049252ae1d7fec907edc0fa7616cc91bf5508d0f4006"
+
dependencies = [
+
 "bstr",
+
 "gix-actor 0.38.0",
+
 "gix-date 0.13.0",
+
 "gix-features",
 "gix-hash",
-
 "gix-object",
-
 "gix-revwalk",
+
 "gix-hashtable",
+
 "gix-path",
+
 "gix-utils",
+
 "gix-validate",
+
 "itoa",
 "smallvec",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
+
 "winnow",
]

[[package]]
name = "gix-object"
-
version = "0.50.2"
+
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "d69ce108ab67b65fbd4fb7e1331502429d78baeb2eee10008bdef55765397c07"
+
checksum = "227e12fad42022ff08d6fbcc4bae34d6e2aa180576e9e1106d9a29a06798e750"
dependencies = [
 "bstr",
-
 "gix-actor",
-
 "gix-date",
+
 "gix-actor 0.39.0",
+
 "gix-date 0.14.0",
 "gix-features",
 "gix-hash",
 "gix-hashtable",
@@ -1371,241 +1471,257 @@ dependencies = [
 "gix-validate",
 "itoa",
 "smallvec",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
 "winnow",
]

[[package]]
name = "gix-odb"
-
version = "0.70.0"
+
version = "0.75.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "9c9d7af10fda9df0bb4f7f9bd507963560b3c66cb15a5b825caf752e0eb109ac"
+
checksum = "1d59882d2fdab5e609b0c452a6ef9a3bd12ef6b694be4f82ab8f126ad0969864"
dependencies = [
 "arc-swap",
-
 "gix-date",
 "gix-features",
 "gix-fs",
 "gix-hash",
 "gix-hashtable",
-
 "gix-object",
+
 "gix-object 0.55.0",
 "gix-pack",
 "gix-path",
 "gix-quote",
 "parking_lot",
 "tempfile",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-pack"
-
version = "0.60.0"
+
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "d8571df89bfca5abb49c3e3372393f7af7e6f8b8dbe2b96303593cef5b263019"
+
checksum = "8c44db57ebbbeaad9972c2a60662142660427a1f0a7529314d53fefb4fedad24"
dependencies = [
-
 "gix-chunk",
+
 "gix-chunk 0.5.0",
 "gix-diff",
+
 "gix-error 0.0.0",
 "gix-features",
 "gix-hash",
 "gix-hashtable",
-
 "gix-object",
+
 "gix-object 0.55.0",
 "gix-path",
 "gix-tempfile",
 "gix-traverse",
 "memmap2",
 "parking_lot",
 "smallvec",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-packetline"
-
version = "0.19.1"
+
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "2592fbd36249a2fea11056f7055cc376301ef38d903d157de41998335bbf1f93"
+
checksum = "25429ee1ef792d9b653ee5de09bb525489fc8e6908334cfd5d5824269f0b7073"
dependencies = [
 "bstr",
 "faster-hex",
 "gix-trace",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-path"
-
version = "0.10.20"
+
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "06d37034a4c67bbdda76f7bcd037b2f7bc0fba0c09a6662b19697a5716e7b2fd"
+
checksum = "7163b1633d35846a52ef8093f390cec240e2d55da99b60151883035e5169cd85"
dependencies = [
 "bstr",
 "gix-trace",
 "gix-validate",
-
 "home",
-
 "once_cell",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-prompt"
-
version = "0.11.1"
+
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "6ffa1a7a34c81710aaa666a428c142b6c5d640492fcd41267db0740d923c7906"
+
checksum = "4806f1ebf969cd54d178ccd975911ef1829aeccea0b27630e63c9d26c8347d7f"
dependencies = [
 "gix-command",
 "gix-config-value",
 "parking_lot",
-
 "rustix 1.0.7",
-
 "thiserror 2.0.17",
+
 "rustix 1.1.3",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-protocol"
-
version = "0.51.0"
+
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "12b4b807c47ffcf7c1e5b8119585368a56449f3493da93b931e1d4239364e922"
+
checksum = "a13680c03e847d8f32a59cf1511dd05334d429da33f5af08891367680f15cbca"
dependencies = [
 "bstr",
 "gix-credentials",
-
 "gix-date",
+
 "gix-date 0.14.0",
 "gix-features",
 "gix-hash",
 "gix-lock",
 "gix-negotiate",
-
 "gix-object",
+
 "gix-object 0.56.0",
 "gix-ref",
 "gix-refspec",
-
 "gix-revwalk",
+
 "gix-revwalk 0.27.0",
 "gix-shallow",
 "gix-trace",
 "gix-transport",
 "gix-utils",
 "maybe-async",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
 "winnow",
]

[[package]]
name = "gix-quote"
-
version = "0.6.0"
+
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "4a375a75b4d663e8bafe3bf4940a18a23755644c13582fa326e99f8f987d83fd"
+
checksum = "96fc2ff2ec8cc0c92807f02eab1f00eb02619fc2810d13dc42679492fcc36757"
dependencies = [
 "bstr",
 "gix-utils",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-ref"
-
version = "0.53.1"
+
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "b966f578079a42f4a51413b17bce476544cca1cf605753466669082f94721758"
+
checksum = "c92fd2e86d65efe972a5226f303ed72cfb49a8ad03740e5cc2b27c07970a5d78"
dependencies = [
-
 "gix-actor",
+
 "gix-actor 0.39.0",
 "gix-features",
 "gix-fs",
 "gix-hash",
 "gix-lock",
-
 "gix-object",
+
 "gix-object 0.56.0",
 "gix-path",
 "gix-tempfile",
 "gix-utils",
 "gix-validate",
 "memmap2",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
 "winnow",
]

[[package]]
name = "gix-refspec"
-
version = "0.31.0"
+
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "7d29cae1ae31108826e7156a5e60bffacab405f4413f5bc0375e19772cce0055"
+
checksum = "40adba15f8099159d37d0a21e1cfb6602c2e49b6c05f6f8a57a47d0fb21611af"
dependencies = [
 "bstr",
+
 "gix-error 0.1.0",
+
 "gix-glob",
 "gix-hash",
 "gix-revision",
 "gix-validate",
 "smallvec",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-revision"
-
version = "0.35.0"
+
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "f651f2b1742f760bb8161d6743229206e962b73d9c33c41f4e4aefa6586cbd3d"
+
checksum = "a75ef94c9d76de0765e429ae22a01c512c0d50459269195a90ea11099a5fc752"
dependencies = [
 "bstr",
-
 "gix-commitgraph",
-
 "gix-date",
+
 "gix-commitgraph 0.33.0",
+
 "gix-date 0.14.0",
+
 "gix-error 0.1.0",
+
 "gix-hash",
+
 "gix-object 0.56.0",
+
 "gix-revwalk 0.27.0",
+
]
+

+
[[package]]
+
name = "gix-revwalk"
+
version = "0.26.0"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "194a50b30aa0c6e6de43c723359c5809a96275a3aa92d323ef7f58b1cdd60f16"
+
dependencies = [
+
 "gix-commitgraph 0.32.0",
+
 "gix-date 0.13.0",
+
 "gix-error 0.0.0",
 "gix-hash",
-
 "gix-object",
-
 "gix-revwalk",
-
 "thiserror 2.0.17",
+
 "gix-hashtable",
+
 "gix-object 0.55.0",
+
 "smallvec",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-revwalk"
-
version = "0.21.0"
+
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "06e74f91709729e099af6721bd0fa7d62f243f2005085152301ca5cdd86ec02c"
+
checksum = "07efcad1d064abdac3e79dfc6e23e05accb579559d015e944c9dcf64be95eb49"
dependencies = [
-
 "gix-commitgraph",
-
 "gix-date",
+
 "gix-commitgraph 0.33.0",
+
 "gix-date 0.14.0",
+
 "gix-error 0.1.0",
 "gix-hash",
 "gix-hashtable",
-
 "gix-object",
+
 "gix-object 0.56.0",
 "smallvec",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-sec"
-
version = "0.12.0"
+
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "09f7053ed7c66633b56c57bc6ed3377be3166eaf3dc2df9f1c5ec446df6fdf2c"
+
checksum = "e014df75f3d7f5c98b18b45c202422da6236a1c0c0a50997c3f41e601f3ad511"
dependencies = [
 "bitflags 2.9.1",
 "gix-path",
 "libc",
-
 "windows-sys 0.59.0",
+
 "windows-sys 0.61.2",
]

[[package]]
name = "gix-shallow"
-
version = "0.5.0"
+
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "d936745103243ae4c510f19e0760ce73fb0f08096588fdbe0f0d7fb7ce8944b7"
+
checksum = "189386b5da5285216cc0ede89eff5a943d5261fc794241ee6ec5360b77df15ad"
dependencies = [
 "bstr",
 "gix-hash",
 "gix-lock",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-tempfile"
-
version = "18.0.0"
+
version = "21.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "666c0041bcdedf5fa05e9bef663c897debab24b7dc1741605742412d1d47da57"
+
checksum = "9d9ab2c89fe4bfd4f1d8700aa4516534c170d8a21ae2c554167374607c2eaf16"
dependencies = [
 "gix-fs",
 "libc",
-
 "once_cell",
 "parking_lot",
 "tempfile",
]

[[package]]
name = "gix-trace"
-
version = "0.1.13"
+
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "e2ccaf54b0b1743a695b482ca0ab9d7603744d8d10b2e5d1a332fef337bee658"
+
checksum = "f69a13643b8437d4ca6845e08143e847a36ca82903eed13303475d0ae8b162e0"

[[package]]
name = "gix-transport"
-
version = "0.48.0"
+
version = "0.54.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "12f7cc0179fc89d53c54e1f9ce51229494864ab4bf136132d69db1b011741ca3"
+
checksum = "9561a98f4f1cada03b565121c475c95d060998082bdb1277044fa6e11fe2aa17"
dependencies = [
 "bstr",
 "gix-command",
@@ -1614,45 +1730,43 @@ dependencies = [
 "gix-quote",
 "gix-sec",
 "gix-url",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-traverse"
-
version = "0.47.0"
+
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "c7cdc82509d792ba0ad815f86f6b469c7afe10f94362e96c4494525a6601bdd5"
+
checksum = "37f8b53b4c56b01c43a4491c4edfe2ce66c654eb86232205172ceb1650d21c55"
dependencies = [
 "bitflags 2.9.1",
-
 "gix-commitgraph",
-
 "gix-date",
+
 "gix-commitgraph 0.32.0",
+
 "gix-date 0.13.0",
 "gix-hash",
 "gix-hashtable",
-
 "gix-object",
-
 "gix-revwalk",
+
 "gix-object 0.55.0",
+
 "gix-revwalk 0.26.0",
 "smallvec",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-url"
-
version = "0.32.0"
+
version = "0.35.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "1b76a9d266254ad287ffd44467cd88e7868799b08f4d52e02d942b93e514d16f"
+
checksum = "507752d41afcdf5961ab494eb062c3bf21f68b2ee67e45568e9028cccdd00c34"
dependencies = [
 "bstr",
-
 "gix-features",
 "gix-path",
 "percent-encoding",
-
 "thiserror 2.0.17",
-
 "url",
+
 "thiserror 2.0.18",
]

[[package]]
name = "gix-utils"
-
version = "0.3.0"
+
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "5351af2b172caf41a3728eb4455326d84e0d70fe26fc4de74ab0bd37df4191c5"
+
checksum = "befcdbdfb1238d2854591f760a48711bed85e72d80a10e8f2f93f656746ef7c5"
dependencies = [
 "fastrand",
 "unicode-normalization",
@@ -1660,12 +1774,11 @@ dependencies = [

[[package]]
name = "gix-validate"
-
version = "0.10.0"
+
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "77b9e00cacde5b51388d28ed746c493b18a6add1f19b5e01d686b3b9ece66d4d"
+
checksum = "0ec1eff98d91941f47766367cba1be746bab662bad761d9891ae6f7882f7840b"
dependencies = [
 "bstr",
-
 "thiserror 2.0.17",
]

[[package]]
@@ -1696,9 +1809,9 @@ checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"

[[package]]
name = "hashbrown"
-
version = "0.15.5"
+
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
+
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"

[[package]]
name = "heapless"
@@ -1726,15 +1839,6 @@ dependencies = [
]

[[package]]
-
name = "home"
-
version = "0.5.9"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
-
dependencies = [
-
 "windows-sys 0.52.0",
-
]
-

-
[[package]]
name = "human-panic"
version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1991,30 +2095,30 @@ dependencies = [

[[package]]
name = "itoa"
-
version = "1.0.11"
+
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"

[[package]]
name = "jiff"
-
version = "0.2.15"
+
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49"
+
checksum = "c867c356cc096b33f4981825ab281ecba3db0acefe60329f044c1789d94c6543"
dependencies = [
 "jiff-static",
 "jiff-tzdb-platform",
 "log",
 "portable-atomic",
 "portable-atomic-util",
-
 "serde",
-
 "windows-sys 0.59.0",
+
 "serde_core",
+
 "windows-sys 0.60.2",
]

[[package]]
name = "jiff-static"
-
version = "0.2.15"
+
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
+
checksum = "f7946b4325269738f270bb55b3c19ab5c5040525f83fd625259422a9d25d9be5"
dependencies = [
 "proc-macro2",
 "quote",
@@ -2107,9 +2211,9 @@ checksum = "baff4b617f7df3d896f97fe922b64817f6cd9a756bb81d40f8883f2f66dcb401"

[[package]]
name = "libc"
-
version = "0.2.174"
+
version = "0.2.182"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
+
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"

[[package]]
name = "libgit2-sys"
@@ -2130,15 +2234,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"

[[package]]
-
name = "libz-rs-sys"
-
version = "0.5.2"
-
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "840db8cf39d9ec4dd794376f38acc40d0fc65eec2a8f484f7fd375b84602becd"
-
dependencies = [
-
 "zlib-rs",
-
]
-

-
[[package]]
name = "libz-sys"
version = "1.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2158,9 +2253,9 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"

[[package]]
name = "linux-raw-sys"
-
version = "0.9.4"
+
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
+
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"

[[package]]
name = "litemap"
@@ -2722,9 +2817,9 @@ dependencies = [

[[package]]
name = "prodash"
-
version = "30.0.1"
+
version = "31.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "5a6efc566849d3d9d737c5cb06cc50e48950ebe3d3f9d70631490fff3a07b139"
+
checksum = "962200e2d7d551451297d9fdce85138374019ada198e30ea9ede38034e27604c"
dependencies = [
 "parking_lot",
]
@@ -2829,7 +2924,7 @@ dependencies = [
 "siphasher 1.0.1",
 "sqlite",
 "tempfile",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
 "uds_windows",
 "unicode-normalization",
]
@@ -2862,7 +2957,7 @@ dependencies = [
 "serde_json",
 "shlex",
 "tempfile",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
 "timeago",
 "tree-sitter",
 "tree-sitter-bash",
@@ -2892,7 +2987,7 @@ dependencies = [
 "radicle",
 "shlex",
 "snapbox",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
 "winsplit",
]

@@ -2916,7 +3011,7 @@ dependencies = [
 "serde_json",
 "signature 2.2.0",
 "tempfile",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
@@ -2935,7 +3030,7 @@ dependencies = [
 "serde",
 "serde_json",
 "sqlite",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
@@ -2957,7 +3052,7 @@ dependencies = [
 "sqlite",
 "ssh-key",
 "tempfile",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
 "zeroize",
]

@@ -2979,13 +3074,14 @@ dependencies = [
 "gix-odb",
 "gix-pack",
 "gix-protocol",
+
 "gix-refspec",
 "gix-transport",
 "log",
 "nonempty",
 "radicle",
 "radicle-git-ref-format",
 "radicle-oid",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
@@ -3006,7 +3102,7 @@ dependencies = [
name = "radicle-git-metadata"
version = "0.1.0"
dependencies = [
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
@@ -3060,7 +3156,7 @@ dependencies = [
 "structured-logger",
 "tempfile",
 "test-log",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
 "uds_windows",
]

@@ -3100,7 +3196,7 @@ dependencies = [
 "serde",
 "serde_json",
 "sqlite",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
@@ -3112,7 +3208,7 @@ dependencies = [
 "radicle",
 "radicle-cli",
 "radicle-crypto",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
]

[[package]]
@@ -3139,7 +3235,7 @@ dependencies = [
name = "radicle-ssh"
version = "0.10.0"
dependencies = [
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
 "winpipe",
 "zeroize",
]
@@ -3192,7 +3288,7 @@ dependencies = [
 "radicle-signals",
 "shlex",
 "tempfile",
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
 "unicode-display-width",
 "unicode-segmentation",
 "winsplit",
@@ -3203,7 +3299,7 @@ dependencies = [
name = "radicle-windows"
version = "0.1.0"
dependencies = [
-
 "thiserror 2.0.17",
+
 "thiserror 2.0.18",
 "windows 0.62.2",
]

@@ -3408,15 +3504,15 @@ dependencies = [

[[package]]
name = "rustix"
-
version = "1.0.7"
+
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
+
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
dependencies = [
 "bitflags 2.9.1",
 "errno",
 "libc",
-
 "linux-raw-sys 0.9.4",
-
 "windows-sys 0.59.0",
+
 "linux-raw-sys 0.11.0",
+
 "windows-sys 0.60.2",
]

[[package]]
@@ -4076,7 +4172,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7266304d24ca5a4b230545fc558c80e18bd3e1d2eb1be149b6bcd04398d3e79c"
dependencies = [
 "log",
-
 "rustix 1.0.7",
+
 "rustix 1.1.3",
]

[[package]]
@@ -4092,14 +4188,14 @@ dependencies = [

[[package]]
name = "tempfile"
-
version = "3.23.0"
+
version = "3.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
+
checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1"
dependencies = [
 "fastrand",
 "getrandom 0.3.3",
 "once_cell",
-
 "rustix 1.0.7",
+
 "rustix 1.1.3",
 "windows-sys 0.60.2",
]

@@ -4136,11 +4232,11 @@ dependencies = [

[[package]]
name = "thiserror"
-
version = "2.0.17"
+
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
+
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
-
 "thiserror-impl 2.0.17",
+
 "thiserror-impl 2.0.18",
]

[[package]]
@@ -4156,9 +4252,9 @@ dependencies = [

[[package]]
name = "thiserror-impl"
-
version = "2.0.17"
+
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
+
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
 "proc-macro2",
 "quote",
@@ -5070,6 +5166,15 @@ dependencies = [
]

[[package]]
+
name = "windows-sys"
+
version = "0.61.2"
+
source = "registry+https://github.com/rust-lang/crates.io-index"
+
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+
dependencies = [
+
 "windows-link 0.2.1",
+
]
+

+
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5274,9 +5379,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"

[[package]]
name = "winnow"
-
version = "0.7.13"
+
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
+
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
dependencies = [
 "memchr",
]
@@ -5433,6 +5538,6 @@ dependencies = [

[[package]]
name = "zlib-rs"
-
version = "0.5.2"
+
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-
checksum = "2f06ae92f42f5e5c42443fd094f245eb656abf56dd7cce9b8b263236565e00f2"
+
checksum = "a7948af682ccbc3342b6e9420e8c51c1fe5d7bf7756002b4a3c6cabfe96a7e3c"
modified Cargo.toml
@@ -30,7 +30,7 @@ cyphernet = "0.5.2"
dunce = "1.0.5"
fastrand = { version = "2.0.0", default-features = false }
git2 = { version = "0.19.0", default-features = false, features = ["vendored-libgit2"] }
-
gix-hash = { version = "0.19.0", default-features = false }
+
gix-hash = { version = "0.22.1", default-features = false, features = ["sha1"] }
human-panic = "2.0.6"
itertools = "0.14"
lexopt = "0.3.0"
modified crates/radicle-fetch/Cargo.toml
@@ -11,12 +11,13 @@ rust-version.workspace = true
[dependencies]
bstr = { workspace = true }
either = "1.9.0"
-
gix-features = { version = "0.43.1", features = ["progress"] }
+
gix-features = { version = "0.46", features = ["progress"] }
gix-hash = { workspace = true }
-
gix-odb = "0.70.0"
-
gix-pack = "0.60.0"
-
gix-protocol = { version = "0.51.0", features = ["blocking-client"] }
-
gix-transport = { version = "0.48.0", features = ["blocking-client"] }
+
gix-odb = "0.75.0"
+
gix-pack = "0.65.0"
+
gix-protocol = { version = "0.57.0", features = ["blocking-client"] }
+
gix-refspec = "0.37"
+
gix-transport = { version = "0.54.0", features = ["blocking-client"] }
log = { workspace = true, features = ["std"] }
nonempty = { workspace = true }
radicle = { workspace = true }
modified crates/radicle-fetch/src/lib.rs
@@ -12,7 +12,7 @@ mod state;
use std::io;
use std::time::Instant;

-
use gix_protocol::handshake;
+
use gix_protocol::{handshake, Handshake};

pub use gix_protocol::{transport::bstr::ByteSlice, RemoteProgress};
pub use handle::Handle;
@@ -131,7 +131,7 @@ where
    result
}

-
fn perform_handshake<R, S>(handle: &mut Handle<R, S>) -> Result<handshake::Outcome, Error>
+
fn perform_handshake<R, S>(handle: &mut Handle<R, S>) -> Result<Handshake, Error>
where
    S: transport::ConnectionStream,
{
modified crates/radicle-fetch/src/stage.rs
@@ -32,7 +32,7 @@

use std::collections::{BTreeMap, BTreeSet, HashSet};

-
use bstr::BString;
+
use bstr::{BStr, BString};
use either::Either;
use gix_protocol::handshake::Ref;
use nonempty::NonEmpty;
@@ -89,6 +89,72 @@ pub mod error {
    }
}

+
/// A `ref-prefix` used in the `ls-refs` step of the fetch protocol.
+
///
+
/// Since the Radicle protocol only wants to filter by very specific references,
+
/// this type captures the possible reference prefixes.
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
+
pub(crate) enum RefPrefix {
+
    /// Represents `refs/rad/id`.
+
    RadId,
+
    /// Represents `"refs/namespaces/<namespace>/refs/rad/id"`.
+
    NamespacedRadId { namespace: PublicKey },
+
    /// Represents `"refs/namespaces/<namespace>/refs/rad/sigrefs"`.
+
    NamespacedRadSigrefs { namespace: PublicKey },
+
    /// Represents `"refs/namespaces"`
+
    AllNamespaces,
+
}
+

+
impl RefPrefix {
+
    /// Convert the [`RefPrefix`] into its equivalent [`BString`].
+
    ///
+
    /// See the [`RefPrefix`] variants for their [`BString`] values.
+
    pub fn into_bstring(self) -> BString {
+
        match self {
+
            RefPrefix::RadId => refs::REFS_RAD_ID.as_bstr().into(),
+
            RefPrefix::NamespacedRadId { namespace } => {
+
                radicle::git::refs::storage::id(&namespace).as_bstr().into()
+
            }
+
            RefPrefix::NamespacedRadSigrefs { namespace } => {
+
                radicle::git::refs::storage::sigrefs(&namespace)
+
                    .as_bstr()
+
                    .into()
+
            }
+
            RefPrefix::AllNamespaces => "refs/namespaces".into(),
+
        }
+
    }
+

+
    /// Convert the [`RefPrefix`] into its equivalent [`RefSpec`].
+
    ///
+
    /// See the [`RefPrefix`] variants for their [`BString`] values.
+
    ///
+
    /// # Panics
+
    ///
+
    /// This will panic if the reference as a [`BString`] value no longer parses
+
    /// in the upstream [`gix_refspec`] crate.
+
    ///
+
    /// [`RefSpec`]: gix_refspec::RefSpec
+
    pub fn as_refspec(&self) -> gix_refspec::RefSpec {
+
        use gix_refspec::parse::Operation;
+
        let parse = |spec: &BStr| -> gix_refspec::RefSpec {
+
            gix_refspec::parse(spec, Operation::Fetch)
+
                .expect("RefPrefix should be valid refspec")
+
                .to_owned()
+
        };
+

+
        match self {
+
            RefPrefix::RadId => parse(refs::REFS_RAD_ID.as_bstr()),
+
            RefPrefix::NamespacedRadId { namespace } => {
+
                parse(radicle::git::refs::storage::id(namespace).as_bstr())
+
            }
+
            RefPrefix::NamespacedRadSigrefs { namespace } => {
+
                parse(radicle::git::refs::storage::sigrefs(namespace).as_bstr())
+
            }
+
            RefPrefix::AllNamespaces => parse(BStr::new("refs/namespaces")),
+
        }
+
    }
+
}
+

/// A [`ProtocolStage`] describes a single roundtrip with the Radicle
/// node that is serving the data.
///
@@ -107,7 +173,7 @@ pub mod error {
///      refdb (in-memory and production).
pub(crate) trait ProtocolStage {
    /// If and how to perform `ls-refs`.
-
    fn ls_refs(&self) -> Option<NonEmpty<BString>>;
+
    fn ls_refs(&self) -> Option<NonEmpty<RefPrefix>>;

    /// Filter a remote-advertised [`Ref`].
    ///
@@ -163,8 +229,8 @@ pub struct CanonicalId {
}

impl ProtocolStage for CanonicalId {
-
    fn ls_refs(&self) -> Option<NonEmpty<BString>> {
-
        Some(NonEmpty::new(refs::REFS_RAD_ID.as_bstr().into()))
+
    fn ls_refs(&self) -> Option<NonEmpty<RefPrefix>> {
+
        Some(NonEmpty::new(RefPrefix::RadId))
    }

    fn ref_filter(&self, r: Ref) -> Option<ReceivedRef> {
@@ -250,17 +316,17 @@ pub struct SpecialRefs {
}

impl ProtocolStage for SpecialRefs {
-
    fn ls_refs(&self) -> Option<NonEmpty<BString>> {
+
    fn ls_refs(&self) -> Option<NonEmpty<RefPrefix>> {
        match &self.followed {
-
            policy::Allowed::All => Some(NonEmpty::new("refs/namespaces".into())),
+
            policy::Allowed::All => Some(NonEmpty::new(RefPrefix::AllNamespaces)),
            policy::Allowed::Followed { remotes } => NonEmpty::collect(
                remotes
                    .iter()
                    .chain(self.delegates.iter())
                    .flat_map(|remote| {
                        [
-
                            BString::from(radicle::git::refs::storage::id(remote).to_string()),
-
                            BString::from(radicle::git::refs::storage::sigrefs(remote).to_string()),
+
                            RefPrefix::NamespacedRadSigrefs { namespace: *remote },
+
                            RefPrefix::NamespacedRadId { namespace: *remote },
                        ]
                    }),
            ),
@@ -331,12 +397,16 @@ pub struct SigrefsAt {
}

impl ProtocolStage for SigrefsAt {
-
    fn ls_refs(&self) -> Option<NonEmpty<BString>> {
+
    fn ls_refs(&self) -> Option<NonEmpty<RefPrefix>> {
        // N.b. the `Oid`s are known but the `rad/sigrefs` are still
        // asked for to mark them for updating the fetch state.
-
        NonEmpty::collect(self.refs_at.iter().map(|refs_at| {
-
            BString::from(radicle::git::refs::storage::sigrefs(&refs_at.remote).to_string())
-
        }))
+
        NonEmpty::collect(
+
            self.refs_at
+
                .iter()
+
                .map(|refs_at| RefPrefix::NamespacedRadSigrefs {
+
                    namespace: refs_at.remote,
+
                }),
+
        )
    }

    // We only asked for `rad/sigrefs` so we should only get
@@ -421,7 +491,7 @@ pub struct DataRefs {
impl ProtocolStage for DataRefs {
    // We don't need to ask for refs since we have all reference names
    // and `Oid`s in `rad/sigrefs`.
-
    fn ls_refs(&self) -> Option<NonEmpty<BString>> {
+
    fn ls_refs(&self) -> Option<NonEmpty<RefPrefix>> {
        None
    }

@@ -617,3 +687,26 @@ where

    Ok(())
}
+

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

+
    /// Ensure that the call to [`RefPrefix::as_refspec`] does not panic
+
    #[test]
+
    fn valid_refspecs() {
+
        let namespace = "z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk"
+
            .parse()
+
            .unwrap();
+
        let prefixes = [
+
            RefPrefix::AllNamespaces,
+
            RefPrefix::RadId,
+
            RefPrefix::NamespacedRadId { namespace },
+
            RefPrefix::NamespacedRadSigrefs { namespace },
+
        ];
+

+
        for prefix in prefixes {
+
            prefix.as_refspec();
+
        }
+
    }
+
}
modified crates/radicle-fetch/src/state.rs
@@ -1,7 +1,7 @@
use std::collections::{BTreeMap, BTreeSet};
use std::time::Instant;

-
use gix_protocol::handshake;
+
use gix_protocol::Handshake;
use radicle::crypto::PublicKey;
use radicle::git::{fmt::Qualified, Oid};
use radicle::identity::{Did, Doc, DocError};
@@ -214,7 +214,7 @@ impl FetchState {
    pub(super) fn run_stage<R, S, F>(
        &mut self,
        handle: &mut Handle<R, S>,
-
        handshake: &handshake::Outcome,
+
        handshake: &Handshake,
        step: &F,
    ) -> Result<BTreeSet<PublicKey>, error::Step>
    where
@@ -225,13 +225,13 @@ impl FetchState {
        let refs = match step.ls_refs() {
            Some(refs) => handle
                .transport
-
                .ls_refs(refs.into(), handshake)?
+
                .ls_refs(refs, handshake)?
                .into_iter()
                .filter_map(|r| step.ref_filter(r))
                .collect::<Vec<_>>(),
            None => vec![],
        };
-
        log::trace!("Received refs {refs:?}");
+
        log::trace!("Received refs {refs:#?}");
        step.pre_validate(&refs)?;

        let wants_haves = step.wants_haves(handle.repository(), &refs)?;
@@ -288,7 +288,7 @@ impl FetchState {
    fn run_special_refs<R, S>(
        &mut self,
        handle: &mut Handle<R, S>,
-
        handshake: &handshake::Outcome,
+
        handshake: &Handshake,
        delegates: BTreeSet<PublicKey>,
        threshold: usize,
        limit: &FetchLimit,
@@ -357,7 +357,7 @@ impl FetchState {
    pub(super) fn run<R, S>(
        mut self,
        handle: &mut Handle<R, S>,
-
        handshake: &handshake::Outcome,
+
        handshake: &Handshake,
        limit: FetchLimit,
        remote: PublicKey,
        refs_at: Option<Vec<RefsAt>>,
modified crates/radicle-fetch/src/transport.rs
@@ -10,6 +10,7 @@ use std::sync::Arc;
use bstr::BString;
use gix_features::progress::prodash::progress;
use gix_protocol::handshake;
+
use gix_protocol::Handshake;
use gix_transport::client;
use gix_transport::Protocol;
use gix_transport::Service;
@@ -20,6 +21,7 @@ use thiserror::Error;

use crate::git::packfile::Keepfile;
use crate::git::repository;
+
use crate::stage::RefPrefix;

/// Open a reader and writer stream to pass to the ls-refs and fetch
/// processes for communicating during their respective protocols.
@@ -90,11 +92,12 @@ where

    /// Perform the handshake with the server side.
    #[allow(clippy::result_large_err)]
-
    pub(crate) fn handshake(&mut self) -> Result<handshake::Outcome, handshake::Error> {
+
    pub(crate) fn handshake(&mut self) -> Result<Handshake, handshake::Error> {
        log::trace!("Performing handshake for {}", self.repo);
        let (read, write) = self.stream.open();
-
        gix_protocol::fetch::handshake(
+
        gix_protocol::handshake(
            &mut Connection::new(read, write, self.repo.clone()),
+
            Service::UploadPack,
            |_| Ok(None),
            vec![],
            &mut progress::Discard,
@@ -104,11 +107,10 @@ where
    /// Perform ls-refs with the server side.
    pub(crate) fn ls_refs(
        &mut self,
-
        mut prefixes: Vec<BString>,
-
        handshake: &handshake::Outcome,
+
        prefixes: impl IntoIterator<Item = RefPrefix>,
+
        handshake: &Handshake,
    ) -> Result<Vec<handshake::Ref>, Error> {
-
        prefixes.sort();
-
        prefixes.dedup();
+
        let prefixes = prefixes.into_iter().collect::<BTreeSet<_>>();
        let (read, write) = self.stream.open();
        Ok(ls_refs::run(
            ls_refs::Config {
@@ -126,7 +128,7 @@ where
        &mut self,
        wants_haves: WantsHaves,
        interrupt: Arc<AtomicBool>,
-
        handshake: &handshake::Outcome,
+
        handshake: &Handshake,
    ) -> Result<Option<Keepfile>, Error> {
        log::trace!(
            "Running fetch wants={:?}, haves={:?}",
@@ -179,7 +181,7 @@ where
}

pub(crate) struct Connection<R, W> {
-
    inner: client::git::Connection<R, W>,
+
    inner: client::git::blocking_io::Connection<R, W>,
}

impl<R, W> Connection<R, W>
@@ -189,7 +191,7 @@ where
{
    pub fn new(read: R, write: W, repo: BString) -> Self {
        Self {
-
            inner: client::git::Connection::new(
+
            inner: client::git::blocking_io::Connection::new(
                read,
                write,
                Protocol::V2,
@@ -202,16 +204,25 @@ where
    }
}

-
impl<R, W> client::Transport for Connection<R, W>
+
impl<R, W> client::blocking_io::Transport for Connection<R, W>
where
    R: std::io::Read,
    W: std::io::Write,
{
+
    fn request(
+
        &mut self,
+
        write_mode: client::WriteMode,
+
        on_into_read: client::MessageKind,
+
        trace: bool,
+
    ) -> Result<client::blocking_io::RequestWriter<'_>, client::Error> {
+
        self.inner.request(write_mode, on_into_read, trace)
+
    }
+

    fn handshake<'b>(
        &mut self,
        service: Service,
        extra_parameters: &'b [(&'b str, Option<&'b str>)],
-
    ) -> Result<client::SetServiceResponse<'_>, client::Error> {
+
    ) -> Result<client::blocking_io::SetServiceResponse<'_>, client::Error> {
        self.inner.handshake(service, extra_parameters)
    }
}
@@ -221,15 +232,6 @@ where
    R: std::io::Read,
    W: std::io::Write,
{
-
    fn request(
-
        &mut self,
-
        write_mode: client::WriteMode,
-
        on_into_read: client::MessageKind,
-
        trace: bool,
-
    ) -> Result<client::RequestWriter<'_>, client::Error> {
-
        self.inner.request(write_mode, on_into_read, trace)
-
    }
-

    fn to_url(&self) -> std::borrow::Cow<'_, bstr::BStr> {
        self.inner.to_url()
    }
modified crates/radicle-fetch/src/transport/fetch.rs
@@ -4,10 +4,9 @@ use std::sync::{atomic::AtomicBool, Arc};

use gix_features::progress::{DynNestedProgress, NestedProgress};
use gix_pack as pack;
-
use gix_protocol::fetch;
use gix_protocol::fetch::negotiate::one_round::State;
-
use gix_protocol::handshake;
use gix_protocol::handshake::Ref;
+
use gix_protocol::{fetch, Handshake};

use crate::git::packfile;

@@ -149,7 +148,7 @@ impl fetch::Negotiate for Negotiate {
pub(crate) fn run<P, R, W>(
    wants_haves: WantsHaves,
    pack_writer: PackWriter,
-
    handshake: &handshake::Outcome,
+
    handshake: &Handshake,
    mut conn: Connection<R, W>,
    progress: &mut P,
) -> Result<FetchOut, Error>
modified crates/radicle-fetch/src/transport/ls_refs.rs
@@ -1,11 +1,14 @@
use std::borrow::Cow;
+
use std::collections::BTreeSet;
use std::io;

use gix_features::progress::Progress;
-
use gix_protocol::handshake::{self, Ref};
-
use gix_protocol::ls_refs;
+
use gix_protocol::handshake::Ref;
use gix_protocol::transport::Protocol;
-
use gix_transport::bstr::{BString, ByteVec};
+
use gix_protocol::{ls_refs, Handshake};
+
use gix_transport::bstr::BString;
+

+
use crate::stage::RefPrefix;

use super::{agent_name, Connection};

@@ -17,7 +20,7 @@ pub struct Config {
    #[allow(dead_code)]
    pub repo: BString,
    /// Ref prefixes for filtering the output of the ls-refs process.
-
    pub prefixes: Vec<BString>,
+
    pub prefixes: BTreeSet<RefPrefix>,
}

/// Run the ls-refs process using the provided `config`.
@@ -30,7 +33,7 @@ pub struct Config {
/// the `config`.
pub(crate) fn run<R, W>(
    config: Config,
-
    handshake: &handshake::Outcome,
+
    handshake: &Handshake,
    mut conn: Connection<R, W>,
    progress: &mut impl Progress,
) -> Result<Vec<Ref>, ls_refs::Error>
@@ -39,7 +42,7 @@ where
    W: io::Write,
{
    log::trace!("Performing ls-refs: {:?}", config.prefixes);
-
    let handshake::Outcome {
+
    let Handshake {
        server_protocol_version: protocol,
        capabilities,
        ..
@@ -51,21 +54,39 @@ where
        )));
    }

-
    let refs = gix_protocol::ls_refs(
-
        &mut conn,
+
    let (refspecs, prefixes) = {
+
        let n = config.prefixes.len();
+
        config.prefixes.into_iter().fold(
+
            (Vec::with_capacity(n), Vec::with_capacity(n)),
+
            |(mut specs, mut prefixes), prefix| {
+
                specs.push(prefix.as_refspec());
+
                prefixes.push(prefix.into_bstring());
+
                (specs, prefixes)
+
            },
+
        )
+
    };
+

+
    log::trace!("ls-refs prefixes: {:#?}", refspecs);
+

+
    let ls_refs = gix_protocol::LsRefsCommand::new(
+
        Some(&refspecs),
        capabilities,
-
        |_caps, args, features| {
-
            for prefix in &config.prefixes {
-
                let mut arg = BString::from("ref-prefix ");
-
                arg.push_str(prefix);
-
                args.push(arg)
-
            }
-
            features.push(("agent", Some(Cow::Owned(agent_name()))));
-
            Ok(gix_protocol::ls_refs::Action::Continue)
-
        },
-
        progress,
-
        false, /* trace packetlines */
-
    )?;
+
        ("agent", Some(Cow::Owned(agent_name()))),
+
    );
+

+
    // According to [1], in the section on `ls-refs`, we must still filter on
+
    // this side, since `ref-prefix` is simply an optimization.
+
    //
+
    // [1]: https://mirrors.edge.kernel.org/pub/software/scm/git/docs/gitprotocol-v2.html
+
    let refs = ls_refs
+
        .invoke_blocking(&mut conn, progress, false)?
+
        .into_iter()
+
        .filter(|r| {
+
            let (refname, _, _) = r.unpack();
+
            prefixes.iter().any(|prefix| refname.starts_with(prefix))
+
        })
+
        .collect();

+
    log::trace!("ls-refs received: {refs:#?}");
    Ok(refs)
}
modified crates/radicle-node/src/tests/e2e.rs
@@ -454,7 +454,7 @@ fn test_fetch_followed_remotes() {
        .collect::<Result<HashSet<_>, _>>()
        .unwrap();

-
    assert!(bob_remotes.len() == followed.len() + 1);
+
    assert_eq!(bob_remotes.len(), followed.len() + 1);
    assert!(bob_remotes.is_superset(&followed));
    assert!(bob_remotes.contains(&alice.id));
}
modified crates/radicle-oid/src/lib.rs
@@ -367,6 +367,7 @@ mod gix {
        fn from(other: Other) -> Self {
            match other {
                Other::Sha1(digest) => Self::Sha1(digest),
+
                _ => panic!("unexpected SHA variant was returned for `gix_hash::ObjectId`"),
            }
        }
    }
@@ -383,6 +384,7 @@ mod gix {
        fn eq(&self, other: &Other) -> bool {
            match (self, other) {
                (Oid::Sha1(a), Other::Sha1(b)) => a == b,
+
                _ => panic!("unexpected SHA variant was returned for `gix_hash::ObjectId`"),
            }
        }
    }