radicle: update radicle-git-ext

This change updates to the latest radicle-git-ext, which required a
few changes within the radicle family of crates.

One set of changes is that the radicle-git-ext crate subsumes
git-commit, git-trailers, and git-ref-format -- so all those imports
go through radicle-git-ext.

The commit code requires that parent `Oid`s point to commits, which
made some tests fail. One of the reasons for these failures is that
the patch tests used fabricated `Oid`s and so the verification would
fail. This is fixed by allowing the test context code to create
`PatchRequest`s that create valid commits.
The other reason for failure was that the identity `Action` passed an
`Oid` that pointed to a blob. This is fixed by removing that code and
making a note of it in documentation.

The final major change was moving the Refspec type into git-ref-format
and removing the AsRefspecs trait. The trait was not required and
could its usage could be replaced by simpler code.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
Fintan Halpenny 2023-05-05 12:59:59 +01:00
parent 293a76948f
commit 8508eab8fa
No known key found for this signature in database
GPG Key ID: 2552FB6F64066CB7
37 changed files with 400 additions and 394 deletions

78
Cargo.lock generated
View File

@ -912,22 +912,11 @@ dependencies = [
"wasi 0.11.0+wasi-snapshot-preview1", "wasi 0.11.0+wasi-snapshot-preview1",
] ]
[[package]]
name = "git-commit"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42480682cb6d05a34304eaea858ff0008fe3ac8167e31eda360931c0b51a35c1"
dependencies = [
"git-trailers",
"git2",
"thiserror",
]
[[package]] [[package]]
name = "git-ref-format" name = "git-ref-format"
version = "0.2.0" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c0eb2e3e4837359b843ab8cc5ad482bb84d4aa528153dc882dbbba44a2f4f47" checksum = "dee7296543085890905b49ee720902b25133a8d878b7221488d0450bbd965741"
dependencies = [ dependencies = [
"git-ref-format-core", "git-ref-format-core",
"git-ref-format-macro", "git-ref-format-macro",
@ -935,19 +924,20 @@ dependencies = [
[[package]] [[package]]
name = "git-ref-format-core" name = "git-ref-format-core"
version = "0.2.0" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cbf4ccf1d4cd0ac87a1461d6e59be34aec975bc2097850d7c3e164e3ff66436" checksum = "54d2d2c6b5f0b38d20c51883abe9ee5879fb50e56a8255bb35ac0b0c679504c5"
dependencies = [ dependencies = [
"minicbor",
"serde", "serde",
"thiserror", "thiserror",
] ]
[[package]] [[package]]
name = "git-ref-format-macro" name = "git-ref-format-macro"
version = "0.2.0" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3058716dac23a95ef68bd44c855c8e8368ee36feefc875bd8bdcb42ae582d98e" checksum = "f6b7367435d968adc55ba8cd0bb3c12a3cb517270f110b5300df9c6f0726c762"
dependencies = [ dependencies = [
"git-ref-format-core", "git-ref-format-core",
"proc-macro-error", "proc-macro-error",
@ -955,21 +945,11 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "git-trailers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0934f57135449b88bea0e28efd80aab0c1b53692f8207c7e232086db824c7a8"
dependencies = [
"nom",
"thiserror",
]
[[package]] [[package]]
name = "git2" name = "git2"
version = "0.16.1" version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf7f68c2995f392c49fffb4f95ae2c873297830eb25c6bc4c114ce8f4562acc" checksum = "8b7905cdfe33d31a88bb2e8419ddd054451f5432d1da9eaf2ac7804ee1ea12d5"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"libc", "libc",
@ -1345,9 +1325,9 @@ checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
[[package]] [[package]]
name = "libgit2-sys" name = "libgit2-sys"
version = "0.14.2+1.5.1" version = "0.15.1+1.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" checksum = "fb4577bde8cdfc7d6a2a4bcb7b049598597de33ffd337276e9c7db6cd4a2cee7"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@ -1437,10 +1417,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]] [[package]]
name = "minimal-lexical" name = "minicbor"
version = "0.2.1" version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" checksum = "124d887cb82f0b1469bdac3d1b65764a381eed1a54fdab0070e5772b13114521"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
@ -1508,16 +1488,6 @@ dependencies = [
"cyphergraphy", "cyphergraphy",
] ]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]] [[package]]
name = "nonempty" name = "nonempty"
version = "0.5.0" version = "0.5.0"
@ -1872,7 +1842,6 @@ dependencies = [
"crossbeam-channel", "crossbeam-channel",
"cyphernet", "cyphernet",
"fastrand", "fastrand",
"git-ref-format",
"git2", "git2",
"localtime", "localtime",
"log", "log",
@ -1912,6 +1881,7 @@ dependencies = [
"radicle-cli-test", "radicle-cli-test",
"radicle-cob", "radicle-cob",
"radicle-crypto", "radicle-crypto",
"radicle-git-ext",
"radicle-node", "radicle-node",
"radicle-term", "radicle-term",
"serde", "serde",
@ -1941,9 +1911,6 @@ name = "radicle-cob"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"fastrand", "fastrand",
"git-commit",
"git-ref-format",
"git-trailers",
"git2", "git2",
"log", "log",
"nonempty 0.8.1", "nonempty 0.8.1",
@ -1980,10 +1947,10 @@ dependencies = [
"cyphernet", "cyphernet",
"ec25519", "ec25519",
"fastrand", "fastrand",
"git-ref-format",
"multibase", "multibase",
"qcheck", "qcheck",
"qcheck-macros", "qcheck-macros",
"radicle-git-ext",
"radicle-ssh", "radicle-ssh",
"serde", "serde",
"sqlite", "sqlite",
@ -2002,9 +1969,9 @@ dependencies = [
[[package]] [[package]]
name = "radicle-git-ext" name = "radicle-git-ext"
version = "0.2.1" version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c68edd5604b96b0ede289513ce523b9cef82a9c4ab07f9e059a24036fc77a2da" checksum = "69d38277eb376789c3ed50a673c99b7fead98c172dd976060b029b527e605148"
dependencies = [ dependencies = [
"git-ref-format", "git-ref-format",
"git2", "git2",
@ -2060,7 +2027,6 @@ dependencies = [
"crossbeam-channel", "crossbeam-channel",
"cyphernet", "cyphernet",
"fastrand", "fastrand",
"git-ref-format",
"io-reactor", "io-reactor",
"lexopt", "lexopt",
"libc", "libc",
@ -2072,6 +2038,7 @@ dependencies = [
"qcheck-macros", "qcheck-macros",
"radicle", "radicle",
"radicle-crypto", "radicle-crypto",
"radicle-git-ext",
"radicle-term", "radicle-term",
"scrypt", "scrypt",
"serde", "serde",
@ -2110,14 +2077,13 @@ checksum = "db20136bbc9ae63f3fec8e5a6c369f4902fac2244501b5dfc6d668e43475aaa4"
[[package]] [[package]]
name = "radicle-surf" name = "radicle-surf"
version = "0.9.0" version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10168a9ee60ee17511fe0062cc2fa3115654f0c1f2d1692d8109a3f254c855de" checksum = "7607a6c93d4cc399479cc0da0d259a42a0ed0b7ae2b051ed80ea8ed35f1246b1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",
"flate2", "flate2",
"git-ref-format",
"git2", "git2",
"log", "log",
"nonempty 0.5.0", "nonempty 0.5.0",
@ -2149,9 +2115,9 @@ name = "radicle-tools"
version = "0.2.0" version = "0.2.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"git-ref-format",
"radicle", "radicle",
"radicle-cli", "radicle-cli",
"radicle-git-ext",
] ]
[[package]] [[package]]

View File

@ -18,6 +18,9 @@ json-color = { version = "0.7" }
lexopt = { version = "0.2" } lexopt = { version = "0.2" }
log = { version = "0.4", features = ["std"] } log = { version = "0.4", features = ["std"] }
nonempty = { version = "0.8" } nonempty = { version = "0.8" }
# N.b. this is required to use macros, even though it's re-exported
# through radicle
radicle-git-ext = { version = "0", features = ["serde"] }
serde = { version = "1.0" } serde = { version = "1.0" }
serde_json = { version = "1" } serde_json = { version = "1" }
serde_yaml = { version = "0.8" } serde_yaml = { version = "0.8" }

View File

@ -6,7 +6,7 @@ delegates creating proposals concurrently.
``` ```
$ rad id edit --title "Add Alice" --description "Add Alice as a delegate" --delegates did:key:z6MkedTZGJGqgQ2py2b8kGecfxdt2yRdHWF6JpaZC47fovFn --no-confirm $ rad id edit --title "Add Alice" --description "Add Alice as a delegate" --delegates did:key:z6MkedTZGJGqgQ2py2b8kGecfxdt2yRdHWF6JpaZC47fovFn --no-confirm
✓ Identity proposal 'f4579d0a9a068453cb24da1ad048ca1bfdcb6d98' created ✓ Identity proposal '04603c0d3ea4d137487024a51c9360adfc511114' created
title: Add Alice title: Add Alice
description: Add Alice as a delegate description: Add Alice as a delegate
status: open status: open
@ -48,7 +48,7 @@ Quorum Reached
``` ```
$ rad id edit --title "Add Bob" --description "Add Bob as a delegate" --delegates did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG --no-confirm $ rad id edit --title "Add Bob" --description "Add Bob as a delegate" --delegates did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG --no-confirm
✓ Identity proposal '5d6c0faeeb5ed5e0d66c72d853a6131a35228d3f' created ✓ Identity proposal '3f6ae4f8645c8b0cbcd35ea924df7b13aca52774' created
title: Add Bob title: Add Bob
description: Add Bob as a delegate description: Add Bob as a delegate
status: open status: open
@ -93,7 +93,7 @@ second proposal, then the identity would be out of date. So let's run
through that and see what happens. through that and see what happens.
``` ```
$ rad id accept f4579d0a9a068453cb24da1ad048ca1bfdcb6d98 --no-confirm $ rad id accept 04603c0d3ea4d137487024a51c9360adfc511114 --no-confirm
✓ Accepted proposal ✓ ✓ Accepted proposal ✓
title: Add Alice title: Add Alice
description: Add Alice as a delegate description: Add Alice as a delegate
@ -137,7 +137,7 @@ Quorum Reached
``` ```
``` ```
$ rad id commit f4579d0a9a068453cb24da1ad048ca1bfdcb6d98 --no-confirm $ rad id commit 04603c0d3ea4d137487024a51c9360adfc511114 --no-confirm
✓ Committed new identity '29ae4b72f5a315328f06fbd68dc1c396a2d5c45e' ✓ Committed new identity '29ae4b72f5a315328f06fbd68dc1c396a2d5c45e'
title: Add Alice title: Add Alice
description: Add Alice as a delegate description: Add Alice as a delegate
@ -183,7 +183,7 @@ Quorum Reached
Now, when we go to accept the second proposal: Now, when we go to accept the second proposal:
``` ```
$ rad id accept 5d6c0faeeb5ed5e0d66c72d853a6131a35228d3f --no-confirm $ rad id accept 3f6ae4f8645c8b0cbcd35ea924df7b13aca52774 --no-confirm
! Warning: Revision is out of date ! Warning: Revision is out of date
! Warning: d96f425412c9f8ad5d9a9a05c9831d0728e2338d =/= 475cdfbc8662853dd132ec564e4f5eb0f152dd7f ! Warning: d96f425412c9f8ad5d9a9a05c9831d0728e2338d =/= 475cdfbc8662853dd132ec564e4f5eb0f152dd7f
👉 Consider using 'rad id rebase' to update the proposal to the latest identity 👉 Consider using 'rad id rebase' to update the proposal to the latest identity
@ -238,19 +238,19 @@ Note that a warning was emitted:
If we attempt to commit this revision, the command will fail: If we attempt to commit this revision, the command will fail:
``` ```
$ rad id commit 5d6c0faeeb5ed5e0d66c72d853a6131a35228d3f --no-confirm $ rad id commit 3f6ae4f8645c8b0cbcd35ea924df7b13aca52774 --no-confirm
! Warning: Revision is out of date ! Warning: Revision is out of date
! Warning: d96f425412c9f8ad5d9a9a05c9831d0728e2338d =/= 475cdfbc8662853dd132ec564e4f5eb0f152dd7f ! Warning: d96f425412c9f8ad5d9a9a05c9831d0728e2338d =/= 475cdfbc8662853dd132ec564e4f5eb0f152dd7f
👉 Consider using 'rad id rebase' to update the proposal to the latest identity 👉 Consider using 'rad id rebase' to update the proposal to the latest identity
✗ Id failed: the identity hashes do match 'd96f425412c9f8ad5d9a9a05c9831d0728e2338d =/= 475cdfbc8662853dd132ec564e4f5eb0f152dd7f' for the revision '5d6c0faeeb5ed5e0d66c72d853a6131a35228d3f' ✗ Id failed: the identity hashes do match 'd96f425412c9f8ad5d9a9a05c9831d0728e2338d =/= 475cdfbc8662853dd132ec564e4f5eb0f152dd7f' for the revision '3f6ae4f8645c8b0cbcd35ea924df7b13aca52774'
``` ```
So, let's fix this by running a rebase on the proposal's revision: So, let's fix this by running a rebase on the proposal's revision:
``` ```
$ rad id rebase 5d6c0faeeb5ed5e0d66c72d853a6131a35228d3f --no-confirm $ rad id rebase 3f6ae4f8645c8b0cbcd35ea924df7b13aca52774 --no-confirm
✓ Identity proposal '5d6c0faeeb5ed5e0d66c72d853a6131a35228d3f' rebased ✓ Identity proposal '3f6ae4f8645c8b0cbcd35ea924df7b13aca52774' rebased
✓ Revision '45477572abd02ea6fc3d3e710e034bb60c74038b' ✓ Revision '42b9428df59ad349f706b1397750b75ea3b42574'
title: Add Bob title: Add Bob
description: Add Bob as a delegate description: Add Bob as a delegate
status: open status: open
@ -293,9 +293,9 @@ Quorum Reached
We can now update the proposal to have both keys in the delegates set: We can now update the proposal to have both keys in the delegates set:
``` ```
$ rad id update 5d6c0faeeb5ed5e0d66c72d853a6131a35228d3f --rev 45477572abd02ea6fc3d3e710e034bb60c74038b --delegates did:key:z6MkedTZGJGqgQ2py2b8kGecfxdt2yRdHWF6JpaZC47fovFn --no-confirm $ rad id update 3f6ae4f8645c8b0cbcd35ea924df7b13aca52774 --rev 42b9428df59ad349f706b1397750b75ea3b42574 --delegates did:key:z6MkedTZGJGqgQ2py2b8kGecfxdt2yRdHWF6JpaZC47fovFn --no-confirm
✓ Identity proposal '5d6c0faeeb5ed5e0d66c72d853a6131a35228d3f' updated ✓ Identity proposal '3f6ae4f8645c8b0cbcd35ea924df7b13aca52774' updated
✓ Revision '66f68597d031f9678e704204bea5ce7f020c98e7' ✓ Revision '1b4ded759249e4f76d19c3e580b4736bf2a2d1c4'
title: Add Bob title: Add Bob
description: Add Bob as a delegate description: Add Bob as a delegate
status: open status: open
@ -338,10 +338,10 @@ Quorum Reached
Finally, we can accept and commit this proposal, creating the final Finally, we can accept and commit this proposal, creating the final
state of our new Radicle identity: state of our new Radicle identity:
$ rad id show 5d6c0faeeb5ed5e0d66c72d853a6131a35228d3f --revisions $ rad id show 3f6ae4f8645c8b0cbcd35ea924df7b13aca52774 --revisions
``` ```
$ rad id accept 5d6c0faeeb5ed5e0d66c72d853a6131a35228d3f --rev 66f68597d031f9678e704204bea5ce7f020c98e7 --no-confirm $ rad id accept 3f6ae4f8645c8b0cbcd35ea924df7b13aca52774 --rev 1b4ded759249e4f76d19c3e580b4736bf2a2d1c4 --no-confirm
✓ Accepted proposal ✓ ✓ Accepted proposal ✓
title: Add Bob title: Add Bob
description: Add Bob as a delegate description: Add Bob as a delegate
@ -385,7 +385,7 @@ Quorum Reached
``` ```
``` ```
$ rad id commit 5d6c0faeeb5ed5e0d66c72d853a6131a35228d3f --rev 66f68597d031f9678e704204bea5ce7f020c98e7 --no-confirm $ rad id commit 3f6ae4f8645c8b0cbcd35ea924df7b13aca52774 --rev 1b4ded759249e4f76d19c3e580b4736bf2a2d1c4 --no-confirm
✓ Committed new identity '60de897bc24898f6908fd1272633c0b15aa4096f' ✓ Committed new identity '60de897bc24898f6908fd1272633c0b15aa4096f'
title: Add Bob title: Add Bob
description: Add Bob as a delegate description: Add Bob as a delegate

View File

@ -14,7 +14,7 @@ Let's add Bob as a delegate using their DID
``` ```
$ rad id edit --title "Add Bob" --description "Add Bob as a delegate" --delegates did:key:z6MkedTZGJGqgQ2py2b8kGecfxdt2yRdHWF6JpaZC47fovFn --no-confirm $ rad id edit --title "Add Bob" --description "Add Bob as a delegate" --delegates did:key:z6MkedTZGJGqgQ2py2b8kGecfxdt2yRdHWF6JpaZC47fovFn --no-confirm
✓ Identity proposal 'c2fe23ff387d5e040d0ed8eb2651942f5d0c2aa5' created ✓ Identity proposal '0d396a83a5e1dda2b8929f7dc401d19dd1a79fb8' created
title: Add Bob title: Add Bob
description: Add Bob as a delegate description: Add Bob as a delegate
status: open status: open
@ -89,7 +89,7 @@ Finally, we can see whether the `Quorum` was reached:
Let's see what happens when we reject the change: Let's see what happens when we reject the change:
``` ```
$ rad id reject c2fe23ff387d5e040d0ed8eb2651942f5d0c2aa5 --no-confirm $ rad id reject 0d396a --no-confirm
✓ Rejected proposal 👎 ✓ Rejected proposal 👎
title: Add Bob title: Add Bob
description: Add Bob as a delegate description: Add Bob as a delegate
@ -145,7 +145,7 @@ increased to `1`.
Instead, let's accept the proposal: Instead, let's accept the proposal:
``` ```
$ rad id accept c2fe23f --no-confirm $ rad id accept 0d396a --no-confirm
✓ Accepted proposal ✓ ✓ Accepted proposal ✓
title: Add Bob title: Add Bob
description: Add Bob as a delegate description: Add Bob as a delegate
@ -207,7 +207,7 @@ As well as that, the `Quorum` has now been reached:
At this point, we can commit the proposal and update the identity: At this point, we can commit the proposal and update the identity:
``` ```
$ rad id commit c2fe23ff387d5e040d0ed8eb2651942f5d0c2aa5 --no-confirm $ rad id commit 0d396a --no-confirm
✓ Committed new identity 'c96e764965aaeff1c6ea3e5b97e2b9828773c8b0' ✓ Committed new identity 'c96e764965aaeff1c6ea3e5b97e2b9828773c8b0'
title: Add Bob title: Add Bob
description: Add Bob as a delegate description: Add Bob as a delegate
@ -255,7 +255,7 @@ the `--threshold` option:
``` ```
$ rad id edit --title "Update threshold" --description "Update to safer threshold" --threshold 2 --no-confirm $ rad id edit --title "Update threshold" --description "Update to safer threshold" --threshold 2 --no-confirm
✓ Identity proposal '27864167e392d63e77df2a7413c988857bd32839' created ✓ Identity proposal 'f435d6e89c8f922ede691287c0d8b7f82afa591e' created
title: Update threshold title: Update threshold
description: Update to safer threshold description: Update to safer threshold
status: open status: open
@ -298,8 +298,8 @@ Quorum Reached
But we change our minds and decide to close the proposal instead: But we change our minds and decide to close the proposal instead:
``` ```
$ rad id close 27864167e392d63e77df2a7413c988857bd32839 --no-confirm $ rad id close f435d6 --no-confirm
✓ Closed identity proposal '27864167e392d63e77df2a7413c988857bd32839' ✓ Closed identity proposal 'f435d6e89c8f922ede691287c0d8b7f82afa591e'
title: Update threshold title: Update threshold
description: Update to safer threshold description: Update to safer threshold
status: closed status: closed
@ -348,15 +348,15 @@ Radicle identity, then we can use the list command:
``` ```
$ rad id list $ rad id list
27864167e392d63e77df2a7413c988857bd32839 "Update threshold" closed 0d396a83a5e1dda2b8929f7dc401d19dd1a79fb8 "Add Bob" committed
c2fe23ff387d5e040d0ed8eb2651942f5d0c2aa5 "Add Bob" committed f435d6e89c8f922ede691287c0d8b7f82afa591e "Update threshold" closed
``` ```
And if we want to view the latest state of any proposal we can use the And if we want to view the latest state of any proposal we can use the
show command: show command:
``` ```
$ rad id show 27864167e392d63e77df2a7413c988857bd32839 $ rad id show f435d6
title: Update threshold title: Update threshold
description: Update to safer threshold description: Update to safer threshold
status: closed status: closed

View File

@ -513,6 +513,8 @@ fn test_clone_without_seeds() {
#[test] #[test]
fn test_cob_replication() { fn test_cob_replication() {
logger::init(log::Level::Debug);
let mut environment = Environment::new(); let mut environment = Environment::new();
let working = tempfile::tempdir().unwrap(); let working = tempfile::tempdir().unwrap();
let mut alice = environment.node("alice"); let mut alice = environment.node("alice");

View File

@ -14,17 +14,14 @@ keywords = ["radicle", "collaborative objects", "cob", "cobs"]
[dependencies] [dependencies]
fastrand = { version = "1.9.0" } fastrand = { version = "1.9.0" }
git-commit = { version = "0.3" }
git-ref-format = { version = "0.2" }
git-trailers = { version = "0.1" }
log = { version = "0.4.17" } log = { version = "0.4.17" }
nonempty = { version = "0.8.1", features = ["serialize"] } nonempty = { version = "0.8.1", features = ["serialize"] }
radicle-git-ext = { version = "0" } radicle-git-ext = { version = "0", features = ["serde"] }
serde_json = { version = "1.0" } serde_json = { version = "1.0" }
thiserror = { version = "1.0" } thiserror = { version = "1.0" }
[dependencies.git2] [dependencies.git2]
version = "0.16.1" version = "0.17.0"
default-features = false default-features = false
features = ["vendored-libgit2"] features = ["vendored-libgit2"]
@ -43,7 +40,6 @@ features = ["derive"]
[dev-dependencies] [dev-dependencies]
fastrand = { version = "1.9.0", default-features = false } fastrand = { version = "1.9.0", default-features = false }
git-ref-format = { version = "0.2", features = ["macro"] }
tempfile = { version = "3" } tempfile = { version = "3" }
qcheck = { version = "1", default-features = false } qcheck = { version = "1", default-features = false }
qcheck-macros = { version = "1", default-features = false } qcheck-macros = { version = "1", default-features = false }

View File

@ -3,10 +3,12 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use git_commit::{self as commit, Commit}; use git_ext::author;
use git_ext::author::Author;
use git_ext::commit::{headers::Headers, Commit};
use git_ext::Oid; use git_ext::Oid;
use git_trailers::OwnedTrailer;
use nonempty::NonEmpty; use nonempty::NonEmpty;
use radicle_git_ext::commit::trailers::OwnedTrailer;
use crate::history::entry::Timestamp; use crate::history::entry::Timestamp;
use crate::signatures; use crate::signatures;
@ -23,14 +25,16 @@ pub mod error {
use std::str::Utf8Error; use std::str::Utf8Error;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use git_ext::commit;
use git_ext::Oid; use git_ext::Oid;
use git_trailers::Error as TrailerError;
use thiserror::Error; use thiserror::Error;
use crate::signatures::error::Signatures; use crate::signatures::error::Signatures;
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum Create { pub enum Create {
#[error(transparent)]
WriteCommit(#[from] commit::error::Write),
#[error(transparent)] #[error(transparent)]
FromUtf8(#[from] FromUtf8Error), FromUtf8(#[from] FromUtf8Error),
#[error(transparent)] #[error(transparent)]
@ -46,7 +50,7 @@ pub mod error {
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum Load { pub enum Load {
#[error(transparent)] #[error(transparent)]
Read(#[from] git_commit::error::Read), Read(#[from] commit::error::Read),
#[error(transparent)] #[error(transparent)]
Signatures(#[from] Signatures), Signatures(#[from] Signatures),
#[error(transparent)] #[error(transparent)]
@ -73,8 +77,6 @@ pub mod error {
ResourceTrailer(#[from] super::trailers::error::InvalidResourceTrailer), ResourceTrailer(#[from] super::trailers::error::InvalidResourceTrailer),
#[error("non utf-8 characters in commit message")] #[error("non utf-8 characters in commit message")]
Utf8(#[from] FromUtf8Error), Utf8(#[from] FromUtf8Error),
#[error(transparent)]
Trailer(#[from] TrailerError),
} }
} }
@ -262,7 +264,7 @@ where
let author = repo.signature()?; let author = repo.signature()?;
let timestamp = author.when().seconds(); let timestamp = author.when().seconds();
let mut headers = commit::Headers::new(); let mut headers = Headers::new();
headers.push( headers.push(
"gpgsig", "gpgsig",
signature signature
@ -270,13 +272,13 @@ where
.map_err(signatures::error::Signatures::from)? .map_err(signatures::error::Signatures::from)?
.as_str(), .as_str(),
); );
let author = commit::Author::try_from(&author)?; let author = Author::try_from(&author)?;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
let (author, timestamp) = if let Ok(s) = std::env::var(crate::git::RAD_COMMIT_TIME) { let (author, timestamp) = if let Ok(s) = std::env::var(crate::git::RAD_COMMIT_TIME) {
let timestamp = s.trim().parse::<i64>().unwrap(); let timestamp = s.trim().parse::<i64>().unwrap();
let author = commit::Author { let author = Author {
time: git_commit::author::Time::new(timestamp, 0), time: author::Time::new(timestamp, 0),
..author ..author
}; };
(author, timestamp) (author, timestamp)

View File

@ -2,8 +2,8 @@
use std::{convert::TryFrom as _, fmt, ops::Deref, str::FromStr}; use std::{convert::TryFrom as _, fmt, ops::Deref, str::FromStr};
use git_ext::ref_format::{Component, RefString};
use git_ext::Oid; use git_ext::Oid;
use git_ref_format::{Component, RefString};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error; use thiserror::Error;

View File

@ -82,9 +82,9 @@ impl CollaborativeObject {
/// * The parsing of the [`TypeName`] fails /// * The parsing of the [`TypeName`] fails
pub fn parse_refstr<R>(name: &R) -> Option<(TypeName, ObjectId)> pub fn parse_refstr<R>(name: &R) -> Option<(TypeName, ObjectId)>
where where
R: AsRef<git_ref_format::RefStr>, R: AsRef<git_ext::ref_format::RefStr>,
{ {
use git_ref_format::Qualified; use git_ext::ref_format::Qualified;
let name = name.as_ref(); let name = name.as_ref();
let refs_cobs = match name.to_namespaced() { let refs_cobs = match name.to_namespaced() {
None => Qualified::from_refstr(name)?, None => Qualified::from_refstr(name)?,

View File

@ -2,8 +2,8 @@
use std::{collections::HashMap, error::Error}; use std::{collections::HashMap, error::Error};
use git_ext::ref_format::RefString;
use git_ext::Oid; use git_ext::Oid;
use git_ref_format::RefString;
use crate::change::Change; use crate::change::Change;
use crate::{ObjectId, TypeName}; use crate::{ObjectId, TypeName};
@ -95,7 +95,7 @@ pub trait Storage {
pub mod convert { pub mod convert {
use std::str; use std::str;
use git_ref_format::RefString; use git_ext::ref_format::RefString;
use thiserror::Error; use thiserror::Error;
use super::{Commit, Reference}; use super::{Commit, Reference};
@ -109,7 +109,7 @@ pub mod convert {
err: git2::Error, err: git2::Error,
}, },
#[error(transparent)] #[error(transparent)]
Ref(#[from] git_ref_format::Error), Ref(#[from] git_ext::ref_format::Error),
#[error(transparent)] #[error(transparent)]
Utf8(#[from] str::Utf8Error), Utf8(#[from] str::Utf8Error),
} }

View File

@ -8,9 +8,9 @@ use std::{
}; };
use crypto::{ssh, PublicKey}; use crypto::{ssh, PublicKey};
use git_commit::{ use git_ext::commit::{
headers::Signature::{Pgp, Ssh},
Commit, Commit,
Signature::{Pgp, Ssh},
}; };
pub use ssh::ExtendedSignature; pub use ssh::ExtendedSignature;

View File

@ -30,7 +30,7 @@ pub mod error {
#[error(transparent)] #[error(transparent)]
Git(#[from] git2::Error), Git(#[from] git2::Error),
#[error(transparent)] #[error(transparent)]
Format(#[from] git_ref_format::Error), Format(#[from] git_ext::ref_format::Error),
} }
} }

View File

@ -1,7 +1,7 @@
use std::ops::ControlFlow; use std::ops::ControlFlow;
use crypto::test::signer::MockSigner; use crypto::test::signer::MockSigner;
use git_ref_format::{refname, Component, RefString}; use git_ext::ref_format::{refname, Component, RefString};
use nonempty::nonempty; use nonempty::nonempty;
use qcheck::Arbitrary; use qcheck::Arbitrary;
use radicle_crypto::Signer; use radicle_crypto::Signer;

View File

@ -1,7 +1,7 @@
// Copyright © 2019-2020 The Radicle Foundation <hello@radicle.foundation> // Copyright © 2019-2020 The Radicle Foundation <hello@radicle.foundation>
use git_trailers::{OwnedTrailer, Token, Trailer}; use git_ext::commit::trailers::{OwnedTrailer, Token, Trailer};
use radicle_git_ext as ext; use std::ops::Deref as _;
pub mod error { pub mod error {
use thiserror::Error; use thiserror::Error;
@ -30,11 +30,10 @@ impl ResourceCommitTrailer {
impl TryFrom<&Trailer<'_>> for ResourceCommitTrailer { impl TryFrom<&Trailer<'_>> for ResourceCommitTrailer {
type Error = error::InvalidResourceTrailer; type Error = error::InvalidResourceTrailer;
fn try_from(Trailer { values, token }: &Trailer<'_>) -> Result<Self, Self::Error> { fn try_from(Trailer { value, token }: &Trailer<'_>) -> Result<Self, Self::Error> {
let val = values.first().ok_or(Self::Error::NoValue)?;
let ext_oid = let ext_oid =
radicle_git_ext::Oid::try_from(val.as_ref()).map_err(|_| Self::Error::InvalidOid)?; git_ext::Oid::try_from(value.as_ref()).map_err(|_| Self::Error::InvalidOid)?;
if Some(token) == Token::try_from("Rad-Resource").ok().as_ref() { if token.deref() == "Rad-Resource" {
Ok(ResourceCommitTrailer(ext_oid.into())) Ok(ResourceCommitTrailer(ext_oid.into()))
} else { } else {
Err(Self::Error::WrongToken) Err(Self::Error::WrongToken)
@ -60,7 +59,7 @@ impl From<ResourceCommitTrailer> for Trailer<'_> {
fn from(containing: ResourceCommitTrailer) -> Self { fn from(containing: ResourceCommitTrailer) -> Self {
Trailer { Trailer {
token: Token::try_from("Rad-Resource").unwrap(), token: Token::try_from("Rad-Resource").unwrap(),
values: vec![containing.0.to_string().into()], value: containing.0.to_string().into(),
} }
} }
} }
@ -71,8 +70,8 @@ impl From<ResourceCommitTrailer> for OwnedTrailer {
} }
} }
impl From<ext::Oid> for ResourceCommitTrailer { impl From<git_ext::Oid> for ResourceCommitTrailer {
fn from(oid: ext::Oid) -> Self { fn from(oid: git_ext::Oid) -> Self {
Self(oid.into()) Self(oid.into())
} }
} }

View File

@ -2,7 +2,7 @@
use std::{fmt, str::FromStr}; use std::{fmt, str::FromStr};
use git_ref_format::{Component, RefString}; use git_ext::ref_format::{Component, RefString};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error; use thiserror::Error;

View File

@ -27,8 +27,8 @@ version = "1.8.0"
default-features = false default-features = false
optional = true optional = true
[dependencies.git-ref-format] [dependencies.radicle-git-ext]
version = "0.2" version = "0.4"
optional = true optional = true
[dependencies.ssh-key] [dependencies.ssh-key]

View File

@ -360,13 +360,16 @@ impl PublicKey {
multibase::encode(multibase::Base::Base58Btc, buf) multibase::encode(multibase::Base::Base58Btc, buf)
} }
#[cfg(feature = "git-ref-format")] #[cfg(feature = "radicle-git-ext")]
pub fn to_namespace(&self) -> git_ref_format::RefString { pub fn to_namespace(&self) -> radicle_git_ext::ref_format::RefString {
git_ref_format::refname!("refs/namespaces").join(git_ref_format::Component::from(self)) use radicle_git_ext::ref_format::{refname, Component};
refname!("refs/namespaces").join(Component::from(self))
} }
#[cfg(feature = "git-ref-format")] #[cfg(feature = "radicle-git-ext")]
pub fn from_namespaced(refstr: &git_ref_format::Namespaced) -> Result<Self, PublicKeyError> { pub fn from_namespaced(
refstr: &radicle_git_ext::ref_format::Namespaced,
) -> Result<Self, PublicKeyError> {
let name = refstr.namespace().into_inner(); let name = refstr.namespace().into_inner();
Self::from_str(name.deref().as_str()) Self::from_str(name.deref().as_str())
@ -405,10 +408,10 @@ impl Deref for PublicKey {
} }
} }
#[cfg(feature = "git-ref-format")] #[cfg(feature = "radicle-git-ext")]
impl<'a> From<&PublicKey> for git_ref_format::Component<'a> { impl<'a> From<&PublicKey> for radicle_git_ext::ref_format::Component<'a> {
fn from(id: &PublicKey) -> Self { fn from(id: &PublicKey) -> Self {
use git_ref_format::{Component, RefString}; use radicle_git_ext::ref_format::{Component, RefString};
let refstr = let refstr =
RefString::try_from(id.to_string()).expect("encoded public keys are valid ref strings"); RefString::try_from(id.to_string()).expect("encoded public keys are valid ref strings");
Component::from_refstr(refstr).expect("encoded public keys are valid refname components") Component::from_refstr(refstr).expect("encoded public keys are valid refname components")

View File

@ -25,7 +25,7 @@ flate2 = { version = "1" }
hyper = { version = "0.14.17", default-features = false } hyper = { version = "0.14.17", default-features = false }
lexopt = { version = "0.2.1" } lexopt = { version = "0.2.1" }
nonempty = { version = "0.8.1", features = ["serialize"] } nonempty = { version = "0.8.1", features = ["serialize"] }
radicle-surf = { version = "0.9.0", default-features = false, features = ["serde"] } radicle-surf = { version = "0.10.3", default-features = false, features = ["serde"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["preserve_order"] } serde_json = { version = "1", features = ["preserve_order"] }
thiserror = { version = "1" } thiserror = { version = "1" }

View File

@ -19,7 +19,6 @@ colored = { version = "1.9.0" }
crossbeam-channel = { version = "0.5.6" } crossbeam-channel = { version = "0.5.6" }
cyphernet = { version = "0.2.0", features = ["tor", "dns", "ed25519", "p2p-ed25519"] } cyphernet = { version = "0.2.0", features = ["tor", "dns", "ed25519", "p2p-ed25519"] }
fastrand = { version = "1.9.0" } fastrand = { version = "1.9.0" }
git-ref-format = { version = "0.2", features = ["serde", "macro"] }
io-reactor = { version = "0.1.2", features = ["popol"] } io-reactor = { version = "0.1.2", features = ["popol"] }
lexopt = { version = "0.2.1" } lexopt = { version = "0.2.1" }
libc = { version = "0.2.137" } libc = { version = "0.2.137" }
@ -28,6 +27,9 @@ localtime = { version = "1.2.0" }
netservices = { version = "0.2.2", features = ["io-reactor", "socket2"] } netservices = { version = "0.2.2", features = ["io-reactor", "socket2"] }
nonempty = { version = "0.8.1", features = ["serialize"] } nonempty = { version = "0.8.1", features = ["serialize"] }
qcheck = { version = "1", default-features = false, optional = true } qcheck = { version = "1", default-features = false, optional = true }
# N.b. this is required to use macros, even though it's re-exported
# through radicle
radicle-git-ext = { version = "0", features = ["serde"] }
sqlite = { version = "0.30.3" } sqlite = { version = "0.30.3" }
sqlite3-src = { version = "0.4.0", features = ["bundled"] } # Ensures static linking sqlite3-src = { version = "0.4.0", features = ["bundled"] } # Ensures static linking
scrypt = { version = "0.10.0", default-features = false } scrypt = { version = "0.10.0", default-features = false }

View File

@ -478,7 +478,7 @@ impl Worker {
channels: &mut Channels, channels: &mut Channels,
) -> Result<(), FetchError> ) -> Result<(), FetchError>
where where
S: fetch::AsRefspecs, S: IntoIterator<Item = fetch::Refspec>,
{ {
let mut tunnel = Tunnel::with(channels, stream, self.nid, remote, self.handle.clone())?; let mut tunnel = Tunnel::with(channels, stream, self.nid, remote, self.handle.clone())?;
let tunnel_addr = tunnel.local_addr(); let tunnel_addr = tunnel.local_addr();
@ -498,7 +498,6 @@ impl Worker {
let namespace = self.nid.to_namespace(); let namespace = self.nid.to_namespace();
let mut fetchspecs = specs let mut fetchspecs = specs
.into_refspecs()
.into_iter() .into_iter()
// Filter out our own refs, if we aren't cloning. // Filter out our own refs, if we aren't cloning.
.filter(|fs| is_cloning || !fs.dst.starts_with(namespace.as_str())) .filter(|fs| is_cloning || !fs.dst.starts_with(namespace.as_str()))

View File

@ -1,5 +1,5 @@
mod refspecs; mod refspecs;
pub use refspecs::{AsRefspecs, Refspec, SpecialRefs}; pub use refspecs::SpecialRefs;
pub mod error; pub mod error;
@ -7,6 +7,7 @@ use std::collections::{BTreeMap, BTreeSet, HashSet};
use std::ops::Deref; use std::ops::Deref;
use radicle::crypto::{PublicKey, Unverified, Verified}; use radicle::crypto::{PublicKey, Unverified, Verified};
use radicle::git::refspec;
use radicle::git::{url, Namespaced}; use radicle::git::{url, Namespaced};
use radicle::prelude::{Doc, Id, NodeId}; use radicle::prelude::{Doc, Id, NodeId};
use radicle::storage::git::Repository; use radicle::storage::git::Repository;
@ -15,6 +16,8 @@ use radicle::storage::{Namespaces, RefUpdate, Remote, RemoteId};
use radicle::storage::{ReadRepository, ReadStorage, WriteRepository, WriteStorage}; use radicle::storage::{ReadRepository, ReadStorage, WriteRepository, WriteStorage};
use radicle::{git, Storage}; use radicle::{git, Storage};
pub type Refspec = refspec::Refspec<git::PatternString, git::PatternString>;
/// The initial phase of staging a fetch from a remote. /// The initial phase of staging a fetch from a remote.
/// ///
/// The [`StagingPhaseInitial::refpsecs`] generated are to fetch the /// The [`StagingPhaseInitial::refpsecs`] generated are to fetch the
@ -146,15 +149,14 @@ impl<'a> StagingPhaseInitial<'a> {
/// Return the fetch refspecs for fetching the necessary `rad` /// Return the fetch refspecs for fetching the necessary `rad`
/// references. /// references.
pub fn refspecs(&self) -> Vec<Refspec<git::PatternString, git::PatternString>> { pub fn refspecs(&self) -> Vec<Refspec> {
let id = git::PatternString::from(IDENTITY_BRANCH.clone().into_refstring()); let id = git::PatternString::from(IDENTITY_BRANCH.clone().into_refstring());
match self.repo { match self.repo {
StagedRepository::Cloning(_) => Refspec { StagedRepository::Cloning(_) => vec![Refspec {
src: id.clone(), src: id.clone(),
dst: id, dst: id,
force: false, force: false,
} }],
.into_refspecs(),
StagedRepository::Fetching(_) => SpecialRefs(self.namespaces.clone()).into_refspecs(), StagedRepository::Fetching(_) => SpecialRefs(self.namespaces.clone()).into_refspecs(),
} }
} }
@ -229,7 +231,7 @@ impl<'a> StagingPhaseInitial<'a> {
// don't need to be re-fetched from the remote. // don't need to be re-fetched from the remote.
let mut remote = copy.remote_anonymous(&url)?; let mut remote = copy.remote_anonymous(&url)?;
let refspecs: Vec<_> = Namespaces::All let refspecs: Vec<_> = Namespaces::All
.into_refspecs() .to_refspecs()
.into_iter() .into_iter()
.map(|s| s.to_string()) .map(|s| s.to_string())
.collect(); .collect();
@ -256,12 +258,19 @@ impl<'a> StagingPhaseInitial<'a> {
impl<'a> StagingPhaseFinal<'a> { impl<'a> StagingPhaseFinal<'a> {
/// Return the fetch refspecs for fetching the necessary /// Return the fetch refspecs for fetching the necessary
/// references. /// references.
pub fn refspecs(&self) -> Vec<Refspec<git::PatternString, git::PatternString>> { pub fn refspecs(&self) -> Vec<Refspec> {
match &self.repo { match &self.repo {
FinalStagedRepository::Cloning { trusted, .. } => { FinalStagedRepository::Cloning { trusted, .. } => {
Namespaces::Trusted(trusted.clone()).as_refspecs() Namespaces::Trusted(trusted.clone()).to_refspecs()
} }
FinalStagedRepository::Fetching { refs, .. } => refs.as_refspecs(), FinalStagedRepository::Fetching { refs, .. } => refs
.iter()
.map(|r| Refspec {
src: r.clone().to_ref_string().into(),
dst: r.clone().to_ref_string().into(),
force: true,
})
.collect(),
} }
} }
@ -358,8 +367,8 @@ impl<'a> StagingPhaseFinal<'a> {
refspecs.push(( refspecs.push((
remote.id, remote.id,
Refspec { Refspec {
src: id.clone(), src: id.clone().into(),
dst: id, dst: id.into(),
// Nb. The identity branch is allowed to be force-updated. // Nb. The identity branch is allowed to be force-updated.
force: true, force: true,
} }
@ -368,8 +377,8 @@ impl<'a> StagingPhaseFinal<'a> {
refspecs.push(( refspecs.push((
remote.id, remote.id,
Refspec { Refspec {
src: sigrefs.clone(), src: sigrefs.clone().into(),
dst: sigrefs, dst: sigrefs.into(),
// Nb. Sigrefs are never force-updated. // Nb. Sigrefs are never force-updated.
force: false, force: false,
} }

View File

@ -1,43 +1,16 @@
use std::collections::BTreeSet;
use std::fmt;
use std::fmt::Write as _;
use radicle::crypto::PublicKey; use radicle::crypto::PublicKey;
use radicle::git; use radicle::git;
use radicle::git::refs::storage::{IDENTITY_BRANCH, SIGREFS_BRANCH}; use radicle::git::refs::storage::{IDENTITY_BRANCH, SIGREFS_BRANCH};
use radicle::storage;
use radicle::storage::git::NAMESPACES_GLOB; use radicle::storage::git::NAMESPACES_GLOB;
use radicle::storage::{Namespaces, Remote}; use radicle::storage::Namespaces;
/// A Git [refspec]. use super::Refspec;
///
/// [refspec]: https://git-scm.com/book/en/v2/Git-Internals-The-Refspec
// TODO(finto): this should go into radicle-git-ext/git-ref-format
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Refspec<T, U> {
pub src: T,
pub dst: U,
pub force: bool,
}
impl<T, U> fmt::Display for Refspec<T, U>
where
T: fmt::Display,
U: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.force {
f.write_char('+')?;
}
write!(f, "{}:{}", self.src, self.dst)
}
}
/// Radicle special refs, i.e. `refs/rad/*`. /// Radicle special refs, i.e. `refs/rad/*`.
pub struct SpecialRefs(pub(super) Namespaces); pub struct SpecialRefs(pub(super) Namespaces);
impl AsRefspecs for SpecialRefs { impl SpecialRefs {
fn as_refspecs(&self) -> Vec<Refspec<git::PatternString, git::PatternString>> { pub fn into_refspecs(self) -> Vec<Refspec> {
match &self.0 { match &self.0 {
Namespaces::All => { Namespaces::All => {
let id = NAMESPACES_GLOB.join(&*IDENTITY_BRANCH); let id = NAMESPACES_GLOB.join(&*IDENTITY_BRANCH);
@ -55,13 +28,9 @@ impl AsRefspecs for SpecialRefs {
Namespaces::Trusted(pks) => pks.iter().flat_map(rad_refs).collect(), Namespaces::Trusted(pks) => pks.iter().flat_map(rad_refs).collect(),
} }
} }
fn into_refspecs(self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
self.as_refspecs()
}
} }
fn rad_refs(pk: &PublicKey) -> Vec<Refspec<git::PatternString, git::PatternString>> { fn rad_refs(pk: &PublicKey) -> Vec<Refspec> {
let ns = pk.to_namespace(); let ns = pk.to_namespace();
let id = git::PatternString::from(ns.join(&*IDENTITY_BRANCH)); let id = git::PatternString::from(ns.join(&*IDENTITY_BRANCH));
let id = Refspec { let id = Refspec {
@ -77,95 +46,3 @@ fn rad_refs(pk: &PublicKey) -> Vec<Refspec<git::PatternString, git::PatternStrin
}; };
vec![id, sigrefs] vec![id, sigrefs]
} }
/// A conversion trait for producing a set of Git [`Refspec`]s.
pub trait AsRefspecs
where
Self: Sized,
{
/// Convert the borrowed data into a set of [`Refspec`]s.
fn as_refspecs(&self) -> Vec<Refspec<git::PatternString, git::PatternString>>;
/// Convert the owned data into a set of [`Refspec`]s.
///
/// Nb. The default implementation uses
/// [`AsRefspecs::as_refspecs`], which may clone data.
fn into_refspecs(self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
self.as_refspecs()
}
}
impl AsRefspecs for Namespaces {
fn as_refspecs(&self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
match self {
Namespaces::All => vec![Refspec {
src: (*storage::git::NAMESPACES_GLOB).clone(),
dst: (*storage::git::NAMESPACES_GLOB).clone(),
force: true,
}],
Namespaces::Trusted(pks) => pks
.iter()
.map(|pk| {
let ns = pk.to_namespace().with_pattern(git::refspec::STAR);
Refspec {
src: ns.clone(),
dst: ns,
force: true,
}
})
.collect(),
}
}
}
impl AsRefspecs for Refspec<git::PatternString, git::PatternString> {
fn as_refspecs(&self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
vec![self.clone()]
}
fn into_refspecs(self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
vec![self]
}
}
impl<T: AsRefspecs> AsRefspecs for Vec<T> {
fn as_refspecs(&self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
self.iter().flat_map(AsRefspecs::as_refspecs).collect()
}
fn into_refspecs(self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
self.into_iter()
.flat_map(AsRefspecs::into_refspecs)
.collect()
}
}
impl AsRefspecs for Remote {
fn as_refspecs(&self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
let ns = self.id.to_namespace();
// Nb. the references in Refs are expected to be Qualified
self.refs
.iter()
.map(|(name, _)| {
let name = git::PatternString::from(ns.join(name));
Refspec {
src: name.clone(),
dst: name,
force: true,
}
})
.collect()
}
}
impl<'a> AsRefspecs for BTreeSet<git::Namespaced<'a>> {
fn as_refspecs(&self) -> Vec<Refspec<git::PatternString, git::PatternString>> {
self.iter()
.map(|r| Refspec {
src: r.clone().to_ref_string().into(),
dst: r.clone().to_ref_string().into(),
force: true,
})
.collect()
}
}

View File

@ -7,7 +7,9 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = { version = "1" } anyhow = { version = "1" }
git-ref-format = { version = "0", features = ["serde", "macro"] } # N.b. this is required to use macros, even though it's re-exported
# through radicle
radicle-git-ext = { version = "0", features = ["serde"] }
[dependencies.radicle] [dependencies.radicle]
version = "0" version = "0"

View File

@ -14,7 +14,6 @@ amplify = { version = "4.0.0-beta.7", default-features = false, features = ["std
crossbeam-channel = { version = "0.5.6" } crossbeam-channel = { version = "0.5.6" }
cyphernet = { version = "0.2.0", features = ["tor", "dns", "ed25519"] } cyphernet = { version = "0.2.0", features = ["tor", "dns", "ed25519"] }
fastrand = { version = "1.9.0" } fastrand = { version = "1.9.0" }
git-ref-format = { version = "0", features = ["serde", "macro"] }
multibase = { version = "0.9.1" } multibase = { version = "0.9.1" }
localtime = { version = "1.2.0" } localtime = { version = "1.2.0" }
log = { version = "0.4.17", features = ["std"] } log = { version = "0.4.17", features = ["std"] }
@ -30,7 +29,7 @@ thiserror = { version = "1" }
unicode-normalization = { version = "0.1" } unicode-normalization = { version = "0.1" }
[dependencies.git2] [dependencies.git2]
version = "0.16.1" version = "0.17.0"
default-features = false default-features = false
features = ["vendored-libgit2"] features = ["vendored-libgit2"]
@ -45,7 +44,7 @@ version = "0"
[dependencies.radicle-crypto] [dependencies.radicle-crypto]
path = "../radicle-crypto" path = "../radicle-crypto"
version = "0" version = "0"
features = ["git-ref-format", "ssh", "sqlite", "cyphernet"] features = ["radicle-git-ext", "ssh", "sqlite", "cyphernet"]
[dependencies.radicle-ssh] [dependencies.radicle-ssh]
path = "../radicle-ssh" path = "../radicle-ssh"

View File

@ -59,6 +59,8 @@ pub enum Action {
revision: RevisionId, revision: RevisionId,
}, },
Revision { Revision {
// N.b. the `Oid` is a blob identifier and not a commit, so we
// do not need to propagate it via HistoryAction.
current: Oid, current: Oid,
proposed: Doc<Verified>, proposed: Doc<Verified>,
}, },
@ -68,16 +70,7 @@ pub enum Action {
}, },
} }
impl HistoryAction for Action { impl HistoryAction for Action {}
fn parents(&self) -> Vec<Oid> {
match self {
Self::Revision { current, .. } => {
vec![*current]
}
_ => vec![],
}
}
}
/// Error applying an operation onto a state. /// Error applying an operation onto a state.
#[derive(Error, Debug)] #[derive(Error, Debug)]

View File

@ -575,7 +575,9 @@ mod test {
#[test] #[test]
fn test_issue_create_and_assign() { fn test_issue_create_and_assign() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let test::setup::Context {
signer, project, ..
} = test::setup::Context::new(&tmp);
let mut issues = Issues::open(&project).unwrap(); let mut issues = Issues::open(&project).unwrap();
let assignee: ActorId = arbitrary::gen(1); let assignee: ActorId = arbitrary::gen(1);
@ -612,7 +614,9 @@ mod test {
#[test] #[test]
fn test_issue_create_and_reassign() { fn test_issue_create_and_reassign() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let test::setup::Context {
signer, project, ..
} = test::setup::Context::new(&tmp);
let mut issues = Issues::open(&project).unwrap(); let mut issues = Issues::open(&project).unwrap();
let assignee: ActorId = arbitrary::gen(1); let assignee: ActorId = arbitrary::gen(1);
@ -642,7 +646,9 @@ mod test {
#[test] #[test]
fn test_issue_create_and_get() { fn test_issue_create_and_get() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let test::setup::Context {
signer, project, ..
} = test::setup::Context::new(&tmp);
let mut issues = Issues::open(&project).unwrap(); let mut issues = Issues::open(&project).unwrap();
let created = issues let created = issues
.create("My first issue", "Blah blah blah.", &[], &[], &signer) .create("My first issue", "Blah blah blah.", &[], &[], &signer)
@ -664,7 +670,9 @@ mod test {
#[test] #[test]
fn test_issue_create_and_change_state() { fn test_issue_create_and_change_state() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let test::setup::Context {
signer, project, ..
} = test::setup::Context::new(&tmp);
let mut issues = Issues::open(&project).unwrap(); let mut issues = Issues::open(&project).unwrap();
let mut issue = issues let mut issue = issues
.create("My first issue", "Blah blah blah.", &[], &[], &signer) .create("My first issue", "Blah blah blah.", &[], &[], &signer)
@ -698,7 +706,9 @@ mod test {
#[test] #[test]
fn test_issue_create_and_unassign() { fn test_issue_create_and_unassign() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let test::setup::Context {
signer, project, ..
} = test::setup::Context::new(&tmp);
let mut issues = Issues::open(&project).unwrap(); let mut issues = Issues::open(&project).unwrap();
let assignee: ActorId = arbitrary::gen(1); let assignee: ActorId = arbitrary::gen(1);
@ -726,7 +736,9 @@ mod test {
#[test] #[test]
fn test_issue_edit_title() { fn test_issue_edit_title() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let test::setup::Context {
signer, project, ..
} = test::setup::Context::new(&tmp);
let mut issues = Issues::open(&project).unwrap(); let mut issues = Issues::open(&project).unwrap();
let mut issue = issues let mut issue = issues
.create("My first issue", "Blah blah blah.", &[], &[], &signer) .create("My first issue", "Blah blah blah.", &[], &[], &signer)
@ -744,7 +756,9 @@ mod test {
#[test] #[test]
fn test_issue_react() { fn test_issue_react() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let test::setup::Context {
signer, project, ..
} = test::setup::Context::new(&tmp);
let mut issues = Issues::open(&project).unwrap(); let mut issues = Issues::open(&project).unwrap();
let mut issue = issues let mut issue = issues
.create("My first issue", "Blah blah blah.", &[], &[], &signer) .create("My first issue", "Blah blah blah.", &[], &[], &signer)
@ -767,7 +781,9 @@ mod test {
#[test] #[test]
fn test_issue_reply() { fn test_issue_reply() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let test::setup::Context {
signer, project, ..
} = test::setup::Context::new(&tmp);
let mut issues = Issues::open(&project).unwrap(); let mut issues = Issues::open(&project).unwrap();
let mut issue = issues let mut issue = issues
.create("My first issue", "Blah blah blah.", &[], &[], &signer) .create("My first issue", "Blah blah blah.", &[], &[], &signer)
@ -805,7 +821,9 @@ mod test {
#[test] #[test]
fn test_issue_tag() { fn test_issue_tag() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let test::setup::Context {
signer, project, ..
} = test::setup::Context::new(&tmp);
let mut issues = Issues::open(&project).unwrap(); let mut issues = Issues::open(&project).unwrap();
let bug_tag = Tag::new("bug").unwrap(); let bug_tag = Tag::new("bug").unwrap();
let ux_tag = Tag::new("ux").unwrap(); let ux_tag = Tag::new("ux").unwrap();
@ -835,7 +853,9 @@ mod test {
#[test] #[test]
fn test_issue_comment() { fn test_issue_comment() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let test::setup::Context {
signer, project, ..
} = test::setup::Context::new(&tmp);
let author = *signer.public_key(); let author = *signer.public_key();
let mut issues = Issues::open(&project).unwrap(); let mut issues = Issues::open(&project).unwrap();
let mut issue = issues let mut issue = issues
@ -886,7 +906,9 @@ mod test {
#[test] #[test]
fn test_issue_all() { fn test_issue_all() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let test::setup::Context {
signer, project, ..
} = test::setup::Context::new(&tmp);
let mut issues = Issues::open(&project).unwrap(); let mut issues = Issues::open(&project).unwrap();
issues.create("First", "Blah", &[], &[], &signer).unwrap(); issues.create("First", "Blah", &[], &[], &signer).unwrap();
@ -910,7 +932,9 @@ mod test {
#[test] #[test]
fn test_issue_multilines() { fn test_issue_multilines() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let test::setup::Context {
signer, project, ..
} = test::setup::Context::new(&tmp);
let mut issues = Issues::open(&project).unwrap(); let mut issues = Issues::open(&project).unwrap();
let created = issues let created = issues
.create( .create(

View File

@ -1391,21 +1391,21 @@ mod test {
#[test] #[test]
fn test_patch_create_and_get() { fn test_patch_create_and_get() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let ctx = test::setup::Context::new(&tmp);
let mut patches = Patches::open(&project).unwrap(); let signer = &ctx.signer;
let pr = ctx.branch_with(test::setup::initial_blobs());
let mut patches = Patches::open(&ctx.project).unwrap();
let author: Did = signer.public_key().into(); let author: Did = signer.public_key().into();
let target = MergeTarget::Delegates; let target = MergeTarget::Delegates;
let oid = git::Oid::from_str("e2a85016a458cd809c0ecee81f8c99613b0b0945").unwrap();
let base = git::Oid::from_str("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap();
let patch = patches let patch = patches
.create( .create(
"My first patch", "My first patch",
"Blah blah blah.", "Blah blah blah.",
target, target,
base, pr.base,
oid, pr.oid,
&[], &[],
&signer, signer,
) )
.unwrap(); .unwrap();
@ -1426,8 +1426,8 @@ mod test {
assert_eq!(revision.author.id(), &author); assert_eq!(revision.author.id(), &author);
assert_eq!(revision.description(), ""); assert_eq!(revision.description(), "");
assert_eq!(revision.discussion.len(), 0); assert_eq!(revision.discussion.len(), 0);
assert_eq!(revision.oid, oid); assert_eq!(revision.oid, pr.oid);
assert_eq!(revision.base, base); assert_eq!(revision.base, pr.base);
let (id, _, _) = patches.find_by_revision(rev_id).unwrap().unwrap(); let (id, _, _) = patches.find_by_revision(rev_id).unwrap().unwrap();
assert_eq!(id, patch_id); assert_eq!(id, patch_id);
@ -1436,17 +1436,19 @@ mod test {
#[test] #[test]
fn test_patch_discussion() { fn test_patch_discussion() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let ctx = test::setup::Context::new(&tmp);
let mut patches = Patches::open(&project).unwrap(); let signer = &ctx.signer;
let pr = ctx.branch_with(test::setup::initial_blobs());
let mut patches = Patches::open(&ctx.project).unwrap();
let patch = patches let patch = patches
.create( .create(
"My first patch", "My first patch",
"Blah blah blah.", "Blah blah blah.",
MergeTarget::Delegates, MergeTarget::Delegates,
git::Oid::try_from("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(), pr.base,
git::Oid::try_from("e2a85016a458cd809c0ecee81f8c99613b0b0945").unwrap(), pr.oid,
&[], &[],
&signer, signer,
) )
.unwrap(); .unwrap();
@ -1455,7 +1457,7 @@ mod test {
let (revision_id, _) = patch.revisions().last().unwrap(); let (revision_id, _) = patch.revisions().last().unwrap();
assert!( assert!(
patch patch
.comment(*revision_id, "patch comment", None, &signer) .comment(*revision_id, "patch comment", None, signer)
.is_ok(), .is_ok(),
"can comment on patch" "can comment on patch"
); );
@ -1468,25 +1470,25 @@ mod test {
#[test] #[test]
fn test_patch_merge() { fn test_patch_merge() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let ctx = test::setup::Context::new(&tmp);
let oid = git::Oid::from_str("e2a85016a458cd809c0ecee81f8c99613b0b0945").unwrap(); let signer = &ctx.signer;
let base = git::Oid::from_str("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(); let pr = ctx.branch_with(test::setup::initial_blobs());
let mut patches = Patches::open(&project).unwrap(); let mut patches = Patches::open(&ctx.project).unwrap();
let mut patch = patches let mut patch = patches
.create( .create(
"My first patch", "My first patch",
"Blah blah blah.", "Blah blah blah.",
MergeTarget::Delegates, MergeTarget::Delegates,
base, pr.base,
oid, pr.oid,
&[], &[],
&signer, signer,
) )
.unwrap(); .unwrap();
let id = patch.id; let id = patch.id;
let (rid, _) = patch.revisions().next().unwrap(); let (rid, _) = patch.revisions().next().unwrap();
let _merge = patch.merge(*rid, base, &signer).unwrap(); let _merge = patch.merge(*rid, pr.base, signer).unwrap();
let patch = patches.get(&id).unwrap().unwrap(); let patch = patches.get(&id).unwrap().unwrap();
@ -1496,25 +1498,25 @@ mod test {
let merge = merges.first().unwrap(); let merge = merges.first().unwrap();
assert_eq!(merge.node, *signer.public_key()); assert_eq!(merge.node, *signer.public_key());
assert_eq!(merge.commit, base); assert_eq!(merge.commit, pr.base);
} }
#[test] #[test]
fn test_patch_review() { fn test_patch_review() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let ctx = test::setup::Context::new(&tmp);
let base = git::Oid::from_str("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(); let signer = &ctx.signer;
let oid = git::Oid::from_str("518d5069f94c03427f694bb494ac1cd7d1339380").unwrap(); let pr = ctx.branch_with(test::setup::initial_blobs());
let mut patches = Patches::open(&project).unwrap(); let mut patches = Patches::open(&ctx.project).unwrap();
let mut patch = patches let mut patch = patches
.create( .create(
"My first patch", "My first patch",
"Blah blah blah.", "Blah blah blah.",
MergeTarget::Delegates, MergeTarget::Delegates,
base, pr.base,
oid, pr.oid,
&[], &[],
&signer, signer,
) )
.unwrap(); .unwrap();
@ -1525,7 +1527,7 @@ mod test {
Some(Verdict::Accept), Some(Verdict::Accept),
Some("LGTM".to_owned()), Some("LGTM".to_owned()),
vec![], vec![],
&signer, signer,
) )
.unwrap(); .unwrap();
@ -1626,20 +1628,20 @@ mod test {
#[test] #[test]
fn test_patch_review_edit() { fn test_patch_review_edit() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let ctx = test::setup::Context::new(&tmp);
let base = git::Oid::from_str("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(); let signer = &ctx.signer;
let oid = git::Oid::from_str("518d5069f94c03427f694bb494ac1cd7d1339380").unwrap(); let pr = ctx.branch_with(test::setup::initial_blobs());
let blob = git::Oid::from_str("518d5069f94c03427f694bb494ac1cd7d133999").unwrap(); let blob = git::Oid::from_str("518d5069f94c03427f694bb494ac1cd7d133999").unwrap();
let mut patches = Patches::open(&project).unwrap(); let mut patches = Patches::open(&ctx.project).unwrap();
let mut patch = patches let mut patch = patches
.create( .create(
"My first patch", "My first patch",
"Blah blah blah.", "Blah blah blah.",
MergeTarget::Delegates, MergeTarget::Delegates,
base, pr.base,
oid, pr.oid,
&[], &[],
&signer, signer,
) )
.unwrap(); .unwrap();
@ -1650,7 +1652,7 @@ mod test {
location: CodeLocation { location: CodeLocation {
blob, blob,
path: Path::new("file.rs").to_path_buf(), path: Path::new("file.rs").to_path_buf(),
commit: oid, commit: pr.oid,
lines: 1..3, lines: 1..3,
}, },
comment: "Nice!".to_owned(), comment: "Nice!".to_owned(),
@ -1662,7 +1664,7 @@ mod test {
Some(Verdict::Accept), Some(Verdict::Accept),
Some("LGTM".to_owned()), Some("LGTM".to_owned()),
inline.clone(), inline.clone(),
&signer, signer,
) )
.unwrap(); .unwrap();
patch patch
@ -1671,7 +1673,7 @@ mod test {
Some(Verdict::Reject), Some(Verdict::Reject),
Some("LGTM".to_owned()), Some("LGTM".to_owned()),
vec![], vec![],
&signer, signer,
) )
.unwrap(); // Overwrite the verdict. .unwrap(); // Overwrite the verdict.
@ -1691,7 +1693,7 @@ mod test {
Some(Verdict::Reject), Some(Verdict::Reject),
Some("Whoops!".to_owned()), Some("Whoops!".to_owned()),
vec![], vec![],
&signer, signer,
) )
.unwrap(); // Overwrite the comment. .unwrap(); // Overwrite the comment.
let (_, revision) = patch.latest().unwrap(); let (_, revision) = patch.latest().unwrap();
@ -1704,19 +1706,19 @@ mod test {
#[test] #[test]
fn test_patch_reject_to_accept() { fn test_patch_reject_to_accept() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let ctx = test::setup::Context::new(&tmp);
let base = git::Oid::from_str("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(); let signer = &ctx.signer;
let oid = git::Oid::from_str("518d5069f94c03427f694bb494ac1cd7d1339380").unwrap(); let pr = ctx.branch_with(test::setup::initial_blobs());
let mut patches = Patches::open(&project).unwrap(); let mut patches = Patches::open(&ctx.project).unwrap();
let mut patch = patches let mut patch = patches
.create( .create(
"My first patch", "My first patch",
"Blah blah blah.", "Blah blah blah.",
MergeTarget::Delegates, MergeTarget::Delegates,
base, pr.base,
oid, pr.oid,
&[], &[],
&signer, signer,
) )
.unwrap(); .unwrap();
@ -1729,7 +1731,7 @@ mod test {
Some(Verdict::Reject), Some(Verdict::Reject),
Some("Nah".to_owned()), Some("Nah".to_owned()),
vec![], vec![],
&signer, signer,
) )
.unwrap(); .unwrap();
patch patch
@ -1738,7 +1740,7 @@ mod test {
Some(Verdict::Accept), Some(Verdict::Accept),
Some("LGTM".to_owned()), Some("LGTM".to_owned()),
vec![], vec![],
&signer, signer,
) )
.unwrap(); .unwrap();
@ -1755,19 +1757,19 @@ mod test {
#[test] #[test]
fn test_patch_review_remove_fields() { fn test_patch_review_remove_fields() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let ctx = test::setup::Context::new(&tmp);
let base = git::Oid::from_str("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(); let signer = &ctx.signer;
let oid = git::Oid::from_str("518d5069f94c03427f694bb494ac1cd7d1339380").unwrap(); let pr = ctx.branch_with(test::setup::initial_blobs());
let mut patches = Patches::open(&project).unwrap(); let mut patches = Patches::open(&ctx.project).unwrap();
let mut patch = patches let mut patch = patches
.create( .create(
"My first patch", "My first patch",
"Blah blah blah.", "Blah blah blah.",
MergeTarget::Delegates, MergeTarget::Delegates,
base, pr.base,
oid, pr.oid,
&[], &[],
&signer, signer,
) )
.unwrap(); .unwrap();
@ -1780,10 +1782,10 @@ mod test {
Some(Verdict::Reject), Some(Verdict::Reject),
Some("Nah".to_owned()), Some("Nah".to_owned()),
vec![], vec![],
&signer, signer,
) )
.unwrap(); .unwrap();
patch.review(rid, None, None, vec![], &signer).unwrap(); patch.review(rid, None, None, vec![], signer).unwrap();
let id = patch.id; let id = patch.id;
let patch = patches.get_mut(&id).unwrap(); let patch = patches.get_mut(&id).unwrap();
@ -1797,20 +1799,19 @@ mod test {
#[test] #[test]
fn test_patch_update() { fn test_patch_update() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, project) = test::setup::context(&tmp); let ctx = test::setup::Context::new(&tmp);
let base = git::Oid::from_str("af08e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(); let signer = &ctx.signer;
let rev0_oid = git::Oid::from_str("518d5069f94c03427f694bb494ac1cd7d1339380").unwrap(); let pr = ctx.branch_with(test::setup::initial_blobs());
let rev1_oid = git::Oid::from_str("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(); let mut patches = Patches::open(&ctx.project).unwrap();
let mut patches = Patches::open(&project).unwrap();
let mut patch = patches let mut patch = patches
.create( .create(
"My first patch", "My first patch",
"Blah blah blah.", "Blah blah blah.",
MergeTarget::Delegates, MergeTarget::Delegates,
base, pr.base,
rev0_oid, pr.oid,
&[], &[],
&signer, signer,
) )
.unwrap(); .unwrap();
@ -1818,8 +1819,9 @@ mod test {
assert_eq!(patch.description(), "Blah blah blah."); assert_eq!(patch.description(), "Blah blah blah.");
assert_eq!(patch.version(), 0); assert_eq!(patch.version(), 0);
let update = ctx.branch_with(test::setup::update_blobs());
let _ = patch let _ = patch
.update("I've made changes.", base, rev1_oid, &signer) .update("I've made changes.", pr.base, update.oid, signer)
.unwrap(); .unwrap();
assert_eq!(patch.clock.get(), 2); assert_eq!(patch.clock.get(), 2);
@ -1837,7 +1839,7 @@ mod test {
let (_, revision) = patch.latest().unwrap(); let (_, revision) = patch.latest().unwrap();
assert_eq!(patch.version(), 1); assert_eq!(patch.version(), 1);
assert_eq!(revision.oid, rev1_oid); assert_eq!(revision.oid, update.oid);
assert_eq!(revision.description(), "I've made changes."); assert_eq!(revision.description(), "I've made changes.");
} }
} }

View File

@ -497,7 +497,7 @@ mod tests {
#[test] #[test]
fn test_redact_comment() { fn test_redact_comment() {
let tmp = tempfile::tempdir().unwrap(); let tmp = tempfile::tempdir().unwrap();
let (_, signer, _) = radicle::test::setup::context(&tmp); let radicle::test::setup::Context { signer, .. } = radicle::test::setup::Context::new(&tmp);
let repo = gen::<MockRepository>(1); let repo = gen::<MockRepository>(1);
let mut alice = Actor::new(signer); let mut alice = Actor::new(signer);
let mut thread = Thread::default(); let mut thread = Thread::default();

View File

@ -3,7 +3,7 @@ use std::path::Path;
use std::process::Command; use std::process::Command;
use std::str::FromStr; use std::str::FromStr;
use git_ref_format as format; use git_ext::ref_format as format;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use crate::collections::HashMap; use crate::collections::HashMap;
@ -17,8 +17,8 @@ pub use ext::Error;
pub use ext::NotFound; pub use ext::NotFound;
pub use ext::Oid; pub use ext::Oid;
pub use git2 as raw; pub use git2 as raw;
pub use git_ref_format as fmt; pub use git_ext::ref_format as fmt;
pub use git_ref_format::{ pub use git_ext::ref_format::{
component, lit, name, qualified, refname, refspec, component, lit, name, qualified, refname, refspec,
refspec::{PatternStr, PatternString}, refspec::{PatternStr, PatternString},
Component, Namespaced, Qualified, RefStr, RefString, Component, Namespaced, Qualified, RefStr, RefString,
@ -152,6 +152,7 @@ pub mod refs {
pub mod storage { pub mod storage {
use format::{ use format::{
lit,
name::component, name::component,
refspec::{self, PatternString}, refspec::{self, PatternString},
}; };
@ -181,7 +182,7 @@ pub mod refs {
/// `refs/namespaces/<remote>/refs/heads/<branch>` /// `refs/namespaces/<remote>/refs/heads/<branch>`
/// ///
pub fn branch<'a>(remote: &RemoteId, branch: &RefStr) -> Namespaced<'a> { pub fn branch<'a>(remote: &RemoteId, branch: &RefStr) -> Namespaced<'a> {
Qualified::from(git_ref_format::lit::refs_heads(branch)).with_namespace(remote.into()) Qualified::from(lit::refs_heads(branch)).with_namespace(remote.into())
} }
/// Get the branch where the project's identity document is stored. /// Get the branch where the project's identity document is stored.

View File

@ -1,7 +1,7 @@
use std::ops::Deref; use std::ops::Deref;
use std::{ffi::OsString, fmt, str::FromStr}; use std::{ffi::OsString, fmt, str::FromStr};
use git_ref_format::{Component, RefString}; use git_ext::ref_format::{Component, RefString};
use thiserror::Error; use thiserror::Error;
use crate::crypto; use crate::crypto;

View File

@ -7,6 +7,7 @@ pub extern crate radicle_crypto as crypto;
#[macro_use] #[macro_use]
extern crate amplify; extern crate amplify;
extern crate radicle_git_ext as git_ext;
mod canonical; mod canonical;
pub mod cob; pub mod cob;

View File

@ -16,11 +16,12 @@ pub use radicle_git_ext::Oid;
use crate::collections::HashMap; use crate::collections::HashMap;
use crate::git::ext as git_ext; use crate::git::ext as git_ext;
use crate::git::{Qualified, RefError, RefString}; use crate::git::{refspec::Refspec, PatternString, Qualified, RefError, RefString};
use crate::identity; use crate::identity;
use crate::identity::doc::DocError; use crate::identity::doc::DocError;
use crate::identity::Did; use crate::identity::Did;
use crate::identity::{Id, IdentityError}; use crate::identity::{Id, IdentityError};
use crate::storage::git::NAMESPACES_GLOB;
use crate::storage::refs::Refs; use crate::storage::refs::Refs;
use self::refs::SignedRefs; use self::refs::SignedRefs;
@ -38,6 +39,29 @@ pub enum Namespaces {
Trusted(HashSet<PublicKey>), Trusted(HashSet<PublicKey>),
} }
impl Namespaces {
pub fn to_refspecs(&self) -> Vec<Refspec<PatternString, PatternString>> {
match self {
Namespaces::All => vec![Refspec {
src: (*NAMESPACES_GLOB).clone(),
dst: (*NAMESPACES_GLOB).clone(),
force: true,
}],
Namespaces::Trusted(pks) => pks
.iter()
.map(|pk| {
let ns = pk.to_namespace().with_pattern(git::refspec::STAR);
Refspec {
src: ns.clone(),
dst: ns,
force: true,
}
})
.collect(),
}
}
}
impl FromIterator<PublicKey> for Namespaces { impl FromIterator<PublicKey> for Namespaces {
fn from_iter<T: IntoIterator<Item = PublicKey>>(iter: T) -> Self { fn from_iter<T: IntoIterator<Item = PublicKey>>(iter: T) -> Self {
Self::Trusted(iter.into_iter().collect()) Self::Trusted(iter.into_iter().collect())
@ -236,6 +260,22 @@ impl Remote<Verified> {
refs: self.refs.unverified(), refs: self.refs.unverified(),
} }
} }
pub fn to_refspecs(&self) -> Vec<Refspec<PatternString, PatternString>> {
let ns = self.id.to_namespace();
// Nb. the references in Refs are expected to be Qualified
self.refs
.iter()
.map(|(name, _)| {
let name = PatternString::from(ns.join(name));
Refspec {
src: name.clone(),
dst: name,
force: true,
}
})
.collect()
}
} }
impl<V> Deref for Remote<V> { impl<V> Deref for Remote<V> {

View File

@ -6,7 +6,6 @@ use std::path::{Path, PathBuf};
use std::{fs, io}; use std::{fs, io};
use crypto::{Signer, Unverified, Verified}; use crypto::{Signer, Unverified, Verified};
use git_ref_format::refspec;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use crate::git; use crate::git;
@ -25,10 +24,10 @@ pub use crate::storage::Error;
use super::RemoteId; use super::RemoteId;
pub static NAMESPACES_GLOB: Lazy<refspec::PatternString> = pub static NAMESPACES_GLOB: Lazy<git::refspec::PatternString> =
Lazy::new(|| refspec::pattern!("refs/namespaces/*")); Lazy::new(|| git::refspec::pattern!("refs/namespaces/*"));
pub static SIGREFS_GLOB: Lazy<refspec::PatternString> = pub static SIGREFS_GLOB: Lazy<refspec::PatternString> =
Lazy::new(|| refspec::pattern!("refs/namespaces/*/rad/sigrefs")); Lazy::new(|| git::refspec::pattern!("refs/namespaces/*/rad/sigrefs"));
pub static CANONICAL_IDENTITY: Lazy<git::Qualified> = Lazy::new(|| { pub static CANONICAL_IDENTITY: Lazy<git::Qualified> = Lazy::new(|| {
git::Qualified::from_components( git::Qualified::from_components(
git::name::component!("rad"), git::name::component!("rad"),

View File

@ -31,7 +31,7 @@ pub enum TypesError {
#[error(transparent)] #[error(transparent)]
ParseObjectId(#[from] cob::object::ParseObjectId), ParseObjectId(#[from] cob::object::ParseObjectId),
#[error(transparent)] #[error(transparent)]
RefFormat(#[from] git_ref_format::Error), RefFormat(#[from] git::fmt::Error),
} }
impl cob::Store for Repository {} impl cob::Store for Repository {}

View File

@ -361,7 +361,7 @@ pub mod canonical {
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
#[error(transparent)] #[error(transparent)]
InvalidRef(#[from] git_ref_format::Error), InvalidRef(#[from] git::fmt::Error),
#[error("invalid canonical format")] #[error("invalid canonical format")]
InvalidFormat, InvalidFormat,
#[error(transparent)] #[error(transparent)]

View File

@ -10,20 +10,108 @@ pub mod setup {
use crate::crypto::test::signer::MockSigner; use crate::crypto::test::signer::MockSigner;
use crate::prelude::*; use crate::prelude::*;
use crate::{ use crate::{
git,
profile::Home, profile::Home,
rad::REMOTE_NAME,
test::{fixtures, storage::git::Repository}, test::{fixtures, storage::git::Repository},
Storage, Storage,
}; };
pub fn context(tmp: &TempDir) -> (Storage, MockSigner, Repository) { #[derive(Debug)]
pub struct BranchWith {
pub base: git::Oid,
pub oid: git::Oid,
}
pub struct Context {
pub storage: Storage,
pub signer: MockSigner,
pub project: Repository,
pub working: git2::Repository,
}
impl Context {
pub fn new(tmp: &TempDir) -> Self {
let mut rng = fastrand::Rng::new(); let mut rng = fastrand::Rng::new();
let signer = MockSigner::new(&mut rng); let signer = MockSigner::new(&mut rng);
let home = tmp.path().join("home"); let home = tmp.path().join("home");
let paths = Home::new(home.as_path()).unwrap(); let paths = Home::new(home.as_path()).unwrap();
let storage = Storage::open(paths.storage()).unwrap(); let storage = Storage::open(paths.storage()).unwrap();
let (id, _, _, _) = fixtures::project(tmp.path().join("copy"), &storage, &signer).unwrap(); let (id, _, working, _) =
fixtures::project(tmp.path().join("copy"), &storage, &signer).unwrap();
let project = storage.repository(id).unwrap(); let project = storage.repository(id).unwrap();
(storage, signer, project) Self {
storage,
signer,
project,
working,
}
}
pub fn branch_with(
&self,
blobs: impl IntoIterator<Item = (String, Vec<u8>)>,
) -> BranchWith {
let refname = git::Qualified::from(git::lit::refs_heads(git::refname!("master")));
let base = self.working.refname_to_id(refname.as_str()).unwrap();
let parent = self.working.find_commit(base).unwrap();
let oid = commit(&self.working, &refname, blobs, &[&parent]);
git::push(&self.working, &REMOTE_NAME, [(&refname, &refname)]).unwrap();
BranchWith {
base: base.into(),
oid,
}
}
}
pub fn initial_blobs() -> Vec<(String, Vec<u8>)> {
vec![
("README.md".to_string(), b"Hello, World!".to_vec()),
(
"CONTRIBUTING".to_string(),
b"Please follow the rules".to_vec(),
),
]
}
pub fn update_blobs() -> Vec<(String, Vec<u8>)> {
vec![
("README.md".to_string(), b"Hello, Radicle!".to_vec()),
(
"CONTRIBUTING".to_string(),
b"Please follow the rules".to_vec(),
),
]
}
pub fn commit(
repo: &git2::Repository,
refname: &git::Qualified,
blobs: impl IntoIterator<Item = (String, Vec<u8>)>,
parents: &[&git2::Commit<'_>],
) -> git::Oid {
let tree = {
let mut tb = repo.treebuilder(None).unwrap();
for (name, blob) in blobs.into_iter() {
let oid = repo.blob(&blob).unwrap();
tb.insert(name, oid, git2::FileMode::Blob.into()).unwrap();
}
tb.write().unwrap()
};
let tree = repo.find_tree(tree).unwrap();
let author = git2::Signature::now("anonymous", "anonymous@example.com").unwrap();
repo.commit(
Some(refname.as_str()),
&author,
&author,
"test commit",
&tree,
parents,
)
.unwrap()
.into()
} }
} }

View File

@ -2,8 +2,7 @@ use std::collections::HashMap;
use std::io; use std::io;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use git_ref_format as fmt; use git_ext::ref_format as fmt;
use radicle_git_ext as git_ext;
use crate::crypto::{Signer, Verified}; use crate::crypto::{Signer, Verified};
use crate::identity::doc::{Doc, DocError, Id}; use crate::identity::doc::{Doc, DocError, Id};