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:
parent
293a76948f
commit
8508eab8fa
|
|
@ -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]]
|
||||||
|
|
|
||||||
|
|
@ -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" }
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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❳
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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 }
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)?,
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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" }
|
||||||
|
|
|
||||||
|
|
@ -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 }
|
||||||
|
|
|
||||||
|
|
@ -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()))
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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> {
|
||||||
|
|
|
||||||
|
|
@ -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"),
|
||||||
|
|
|
||||||
|
|
@ -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 {}
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue