diff --git a/Cargo.lock b/Cargo.lock index e8905855..633df985 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -912,22 +912,11 @@ dependencies = [ "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]] name = "git-ref-format" -version = "0.2.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c0eb2e3e4837359b843ab8cc5ad482bb84d4aa528153dc882dbbba44a2f4f47" +checksum = "dee7296543085890905b49ee720902b25133a8d878b7221488d0450bbd965741" dependencies = [ "git-ref-format-core", "git-ref-format-macro", @@ -935,19 +924,20 @@ dependencies = [ [[package]] name = "git-ref-format-core" -version = "0.2.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cbf4ccf1d4cd0ac87a1461d6e59be34aec975bc2097850d7c3e164e3ff66436" +checksum = "54d2d2c6b5f0b38d20c51883abe9ee5879fb50e56a8255bb35ac0b0c679504c5" dependencies = [ + "minicbor", "serde", "thiserror", ] [[package]] name = "git-ref-format-macro" -version = "0.2.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3058716dac23a95ef68bd44c855c8e8368ee36feefc875bd8bdcb42ae582d98e" +checksum = "f6b7367435d968adc55ba8cd0bb3c12a3cb517270f110b5300df9c6f0726c762" dependencies = [ "git-ref-format-core", "proc-macro-error", @@ -955,21 +945,11 @@ dependencies = [ "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]] name = "git2" -version = "0.16.1" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf7f68c2995f392c49fffb4f95ae2c873297830eb25c6bc4c114ce8f4562acc" +checksum = "8b7905cdfe33d31a88bb2e8419ddd054451f5432d1da9eaf2ac7804ee1ea12d5" dependencies = [ "bitflags", "libc", @@ -1345,9 +1325,9 @@ checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] 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" -checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" +checksum = "fb4577bde8cdfc7d6a2a4bcb7b049598597de33ffd337276e9c7db6cd4a2cee7" dependencies = [ "cc", "libc", @@ -1437,10 +1417,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] -name = "minimal-lexical" -version = "0.2.1" +name = "minicbor" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "124d887cb82f0b1469bdac3d1b65764a381eed1a54fdab0070e5772b13114521" [[package]] name = "miniz_oxide" @@ -1508,16 +1488,6 @@ dependencies = [ "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]] name = "nonempty" version = "0.5.0" @@ -1872,7 +1842,6 @@ dependencies = [ "crossbeam-channel", "cyphernet", "fastrand", - "git-ref-format", "git2", "localtime", "log", @@ -1912,6 +1881,7 @@ dependencies = [ "radicle-cli-test", "radicle-cob", "radicle-crypto", + "radicle-git-ext", "radicle-node", "radicle-term", "serde", @@ -1941,9 +1911,6 @@ name = "radicle-cob" version = "0.1.0" dependencies = [ "fastrand", - "git-commit", - "git-ref-format", - "git-trailers", "git2", "log", "nonempty 0.8.1", @@ -1980,10 +1947,10 @@ dependencies = [ "cyphernet", "ec25519", "fastrand", - "git-ref-format", "multibase", "qcheck", "qcheck-macros", + "radicle-git-ext", "radicle-ssh", "serde", "sqlite", @@ -2002,9 +1969,9 @@ dependencies = [ [[package]] name = "radicle-git-ext" -version = "0.2.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c68edd5604b96b0ede289513ce523b9cef82a9c4ab07f9e059a24036fc77a2da" +checksum = "69d38277eb376789c3ed50a673c99b7fead98c172dd976060b029b527e605148" dependencies = [ "git-ref-format", "git2", @@ -2060,7 +2027,6 @@ dependencies = [ "crossbeam-channel", "cyphernet", "fastrand", - "git-ref-format", "io-reactor", "lexopt", "libc", @@ -2072,6 +2038,7 @@ dependencies = [ "qcheck-macros", "radicle", "radicle-crypto", + "radicle-git-ext", "radicle-term", "scrypt", "serde", @@ -2110,14 +2077,13 @@ checksum = "db20136bbc9ae63f3fec8e5a6c369f4902fac2244501b5dfc6d668e43475aaa4" [[package]] name = "radicle-surf" -version = "0.9.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10168a9ee60ee17511fe0062cc2fa3115654f0c1f2d1692d8109a3f254c855de" +checksum = "7607a6c93d4cc399479cc0da0d259a42a0ed0b7ae2b051ed80ea8ed35f1246b1" dependencies = [ "anyhow", "base64", "flate2", - "git-ref-format", "git2", "log", "nonempty 0.5.0", @@ -2149,9 +2115,9 @@ name = "radicle-tools" version = "0.2.0" dependencies = [ "anyhow", - "git-ref-format", "radicle", "radicle-cli", + "radicle-git-ext", ] [[package]] diff --git a/radicle-cli/Cargo.toml b/radicle-cli/Cargo.toml index 9e0878af..db5d1713 100644 --- a/radicle-cli/Cargo.toml +++ b/radicle-cli/Cargo.toml @@ -18,6 +18,9 @@ json-color = { version = "0.7" } lexopt = { version = "0.2" } log = { version = "0.4", features = ["std"] } 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_json = { version = "1" } serde_yaml = { version = "0.8" } diff --git a/radicle-cli/examples/rad-id-rebase.md b/radicle-cli/examples/rad-id-rebase.md index 182c7e34..4647d197 100644 --- a/radicle-cli/examples/rad-id-rebase.md +++ b/radicle-cli/examples/rad-id-rebase.md @@ -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 -✓ Identity proposal 'f4579d0a9a068453cb24da1ad048ca1bfdcb6d98' created +✓ Identity proposal '04603c0d3ea4d137487024a51c9360adfc511114' created title: Add Alice description: Add Alice as a delegate 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 -✓ Identity proposal '5d6c0faeeb5ed5e0d66c72d853a6131a35228d3f' created +✓ Identity proposal '3f6ae4f8645c8b0cbcd35ea924df7b13aca52774' created title: Add Bob description: Add Bob as a delegate 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. ``` -$ rad id accept f4579d0a9a068453cb24da1ad048ca1bfdcb6d98 --no-confirm +$ rad id accept 04603c0d3ea4d137487024a51c9360adfc511114 --no-confirm ✓ Accepted proposal ✓ title: Add Alice 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' title: Add Alice description: Add Alice as a delegate @@ -183,7 +183,7 @@ Quorum Reached 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: d96f425412c9f8ad5d9a9a05c9831d0728e2338d =/= 475cdfbc8662853dd132ec564e4f5eb0f152dd7f 👉 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: ``` -$ rad id commit 5d6c0faeeb5ed5e0d66c72d853a6131a35228d3f --no-confirm +$ rad id commit 3f6ae4f8645c8b0cbcd35ea924df7b13aca52774 --no-confirm ! Warning: Revision is out of date ! Warning: d96f425412c9f8ad5d9a9a05c9831d0728e2338d =/= 475cdfbc8662853dd132ec564e4f5eb0f152dd7f 👉 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: ``` -$ rad id rebase 5d6c0faeeb5ed5e0d66c72d853a6131a35228d3f --no-confirm -✓ Identity proposal '5d6c0faeeb5ed5e0d66c72d853a6131a35228d3f' rebased -✓ Revision '45477572abd02ea6fc3d3e710e034bb60c74038b' +$ rad id rebase 3f6ae4f8645c8b0cbcd35ea924df7b13aca52774 --no-confirm +✓ Identity proposal '3f6ae4f8645c8b0cbcd35ea924df7b13aca52774' rebased +✓ Revision '42b9428df59ad349f706b1397750b75ea3b42574' title: Add Bob description: Add Bob as a delegate status: ❲open❳ @@ -293,9 +293,9 @@ Quorum Reached 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 -✓ Identity proposal '5d6c0faeeb5ed5e0d66c72d853a6131a35228d3f' updated -✓ Revision '66f68597d031f9678e704204bea5ce7f020c98e7' +$ rad id update 3f6ae4f8645c8b0cbcd35ea924df7b13aca52774 --rev 42b9428df59ad349f706b1397750b75ea3b42574 --delegates did:key:z6MkedTZGJGqgQ2py2b8kGecfxdt2yRdHWF6JpaZC47fovFn --no-confirm +✓ Identity proposal '3f6ae4f8645c8b0cbcd35ea924df7b13aca52774' updated +✓ Revision '1b4ded759249e4f76d19c3e580b4736bf2a2d1c4' title: Add Bob description: Add Bob as a delegate status: ❲open❳ @@ -338,10 +338,10 @@ Quorum Reached Finally, we can accept and commit this proposal, creating the final 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 ✓ title: Add Bob 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' title: Add Bob description: Add Bob as a delegate diff --git a/radicle-cli/examples/rad-id.md b/radicle-cli/examples/rad-id.md index 6dc3c063..8c73e649 100644 --- a/radicle-cli/examples/rad-id.md +++ b/radicle-cli/examples/rad-id.md @@ -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 -✓ Identity proposal 'c2fe23ff387d5e040d0ed8eb2651942f5d0c2aa5' created +✓ Identity proposal '0d396a83a5e1dda2b8929f7dc401d19dd1a79fb8' created title: Add Bob description: Add Bob as a delegate 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: ``` -$ rad id reject c2fe23ff387d5e040d0ed8eb2651942f5d0c2aa5 --no-confirm +$ rad id reject 0d396a --no-confirm ✓ Rejected proposal 👎 title: Add Bob description: Add Bob as a delegate @@ -145,7 +145,7 @@ increased to `1`. Instead, let's accept the proposal: ``` -$ rad id accept c2fe23f --no-confirm +$ rad id accept 0d396a --no-confirm ✓ Accepted proposal ✓ title: Add Bob 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: ``` -$ rad id commit c2fe23ff387d5e040d0ed8eb2651942f5d0c2aa5 --no-confirm +$ rad id commit 0d396a --no-confirm ✓ Committed new identity 'c96e764965aaeff1c6ea3e5b97e2b9828773c8b0' title: Add Bob 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 -✓ Identity proposal '27864167e392d63e77df2a7413c988857bd32839' created +✓ Identity proposal 'f435d6e89c8f922ede691287c0d8b7f82afa591e' created title: Update threshold description: Update to safer threshold status: ❲open❳ @@ -298,8 +298,8 @@ Quorum Reached But we change our minds and decide to close the proposal instead: ``` -$ rad id close 27864167e392d63e77df2a7413c988857bd32839 --no-confirm -✓ Closed identity proposal '27864167e392d63e77df2a7413c988857bd32839' +$ rad id close f435d6 --no-confirm +✓ Closed identity proposal 'f435d6e89c8f922ede691287c0d8b7f82afa591e' title: Update threshold description: Update to safer threshold status: ❲closed❳ @@ -348,15 +348,15 @@ Radicle identity, then we can use the list command: ``` $ rad id list -27864167e392d63e77df2a7413c988857bd32839 "Update threshold" ❲closed❳ -c2fe23ff387d5e040d0ed8eb2651942f5d0c2aa5 "Add Bob" ❲committed❳ +0d396a83a5e1dda2b8929f7dc401d19dd1a79fb8 "Add Bob" ❲committed❳ +f435d6e89c8f922ede691287c0d8b7f82afa591e "Update threshold" ❲closed❳ ``` And if we want to view the latest state of any proposal we can use the show command: ``` -$ rad id show 27864167e392d63e77df2a7413c988857bd32839 +$ rad id show f435d6 title: Update threshold description: Update to safer threshold status: ❲closed❳ diff --git a/radicle-cli/tests/commands.rs b/radicle-cli/tests/commands.rs index 46156639..33e3ae8f 100644 --- a/radicle-cli/tests/commands.rs +++ b/radicle-cli/tests/commands.rs @@ -513,6 +513,8 @@ fn test_clone_without_seeds() { #[test] fn test_cob_replication() { + logger::init(log::Level::Debug); + let mut environment = Environment::new(); let working = tempfile::tempdir().unwrap(); let mut alice = environment.node("alice"); diff --git a/radicle-cob/Cargo.toml b/radicle-cob/Cargo.toml index b038c03e..ce106ed9 100644 --- a/radicle-cob/Cargo.toml +++ b/radicle-cob/Cargo.toml @@ -14,17 +14,14 @@ keywords = ["radicle", "collaborative objects", "cob", "cobs"] [dependencies] 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" } nonempty = { version = "0.8.1", features = ["serialize"] } -radicle-git-ext = { version = "0" } +radicle-git-ext = { version = "0", features = ["serde"] } serde_json = { version = "1.0" } thiserror = { version = "1.0" } [dependencies.git2] -version = "0.16.1" +version = "0.17.0" default-features = false features = ["vendored-libgit2"] @@ -43,7 +40,6 @@ features = ["derive"] [dev-dependencies] fastrand = { version = "1.9.0", default-features = false } -git-ref-format = { version = "0.2", features = ["macro"] } tempfile = { version = "3" } qcheck = { version = "1", default-features = false } qcheck-macros = { version = "1", default-features = false } diff --git a/radicle-cob/src/backend/git/change.rs b/radicle-cob/src/backend/git/change.rs index 9d4c6a8b..2f4e601f 100644 --- a/radicle-cob/src/backend/git/change.rs +++ b/radicle-cob/src/backend/git/change.rs @@ -3,10 +3,12 @@ use std::collections::BTreeMap; 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_trailers::OwnedTrailer; use nonempty::NonEmpty; +use radicle_git_ext::commit::trailers::OwnedTrailer; use crate::history::entry::Timestamp; use crate::signatures; @@ -23,14 +25,16 @@ pub mod error { use std::str::Utf8Error; use std::string::FromUtf8Error; + use git_ext::commit; use git_ext::Oid; - use git_trailers::Error as TrailerError; use thiserror::Error; use crate::signatures::error::Signatures; #[derive(Debug, Error)] pub enum Create { + #[error(transparent)] + WriteCommit(#[from] commit::error::Write), #[error(transparent)] FromUtf8(#[from] FromUtf8Error), #[error(transparent)] @@ -46,7 +50,7 @@ pub mod error { #[derive(Debug, Error)] pub enum Load { #[error(transparent)] - Read(#[from] git_commit::error::Read), + Read(#[from] commit::error::Read), #[error(transparent)] Signatures(#[from] Signatures), #[error(transparent)] @@ -73,8 +77,6 @@ pub mod error { ResourceTrailer(#[from] super::trailers::error::InvalidResourceTrailer), #[error("non utf-8 characters in commit message")] Utf8(#[from] FromUtf8Error), - #[error(transparent)] - Trailer(#[from] TrailerError), } } @@ -262,7 +264,7 @@ where let author = repo.signature()?; let timestamp = author.when().seconds(); - let mut headers = commit::Headers::new(); + let mut headers = Headers::new(); headers.push( "gpgsig", signature @@ -270,13 +272,13 @@ where .map_err(signatures::error::Signatures::from)? .as_str(), ); - let author = commit::Author::try_from(&author)?; + let author = Author::try_from(&author)?; #[cfg(debug_assertions)] let (author, timestamp) = if let Ok(s) = std::env::var(crate::git::RAD_COMMIT_TIME) { let timestamp = s.trim().parse::().unwrap(); - let author = commit::Author { - time: git_commit::author::Time::new(timestamp, 0), + let author = Author { + time: author::Time::new(timestamp, 0), ..author }; (author, timestamp) diff --git a/radicle-cob/src/object.rs b/radicle-cob/src/object.rs index 25382f8e..da03f038 100644 --- a/radicle-cob/src/object.rs +++ b/radicle-cob/src/object.rs @@ -2,8 +2,8 @@ use std::{convert::TryFrom as _, fmt, ops::Deref, str::FromStr}; +use git_ext::ref_format::{Component, RefString}; use git_ext::Oid; -use git_ref_format::{Component, RefString}; use serde::{Deserialize, Serialize}; use thiserror::Error; diff --git a/radicle-cob/src/object/collaboration.rs b/radicle-cob/src/object/collaboration.rs index 149aded3..4f30e816 100644 --- a/radicle-cob/src/object/collaboration.rs +++ b/radicle-cob/src/object/collaboration.rs @@ -82,9 +82,9 @@ impl CollaborativeObject { /// * The parsing of the [`TypeName`] fails pub fn parse_refstr(name: &R) -> Option<(TypeName, ObjectId)> where - R: AsRef, + R: AsRef, { - use git_ref_format::Qualified; + use git_ext::ref_format::Qualified; let name = name.as_ref(); let refs_cobs = match name.to_namespaced() { None => Qualified::from_refstr(name)?, diff --git a/radicle-cob/src/object/storage.rs b/radicle-cob/src/object/storage.rs index 6b44375b..73b03c3d 100644 --- a/radicle-cob/src/object/storage.rs +++ b/radicle-cob/src/object/storage.rs @@ -2,8 +2,8 @@ use std::{collections::HashMap, error::Error}; +use git_ext::ref_format::RefString; use git_ext::Oid; -use git_ref_format::RefString; use crate::change::Change; use crate::{ObjectId, TypeName}; @@ -95,7 +95,7 @@ pub trait Storage { pub mod convert { use std::str; - use git_ref_format::RefString; + use git_ext::ref_format::RefString; use thiserror::Error; use super::{Commit, Reference}; @@ -109,7 +109,7 @@ pub mod convert { err: git2::Error, }, #[error(transparent)] - Ref(#[from] git_ref_format::Error), + Ref(#[from] git_ext::ref_format::Error), #[error(transparent)] Utf8(#[from] str::Utf8Error), } diff --git a/radicle-cob/src/signatures.rs b/radicle-cob/src/signatures.rs index 12cb77a1..967eb1e7 100644 --- a/radicle-cob/src/signatures.rs +++ b/radicle-cob/src/signatures.rs @@ -8,9 +8,9 @@ use std::{ }; use crypto::{ssh, PublicKey}; -use git_commit::{ +use git_ext::commit::{ + headers::Signature::{Pgp, Ssh}, Commit, - Signature::{Pgp, Ssh}, }; pub use ssh::ExtendedSignature; diff --git a/radicle-cob/src/test/storage.rs b/radicle-cob/src/test/storage.rs index 2fe83cd4..3160ad56 100644 --- a/radicle-cob/src/test/storage.rs +++ b/radicle-cob/src/test/storage.rs @@ -30,7 +30,7 @@ pub mod error { #[error(transparent)] Git(#[from] git2::Error), #[error(transparent)] - Format(#[from] git_ref_format::Error), + Format(#[from] git_ext::ref_format::Error), } } diff --git a/radicle-cob/src/tests.rs b/radicle-cob/src/tests.rs index 3af920dc..0fe68ebf 100644 --- a/radicle-cob/src/tests.rs +++ b/radicle-cob/src/tests.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; use crypto::test::signer::MockSigner; -use git_ref_format::{refname, Component, RefString}; +use git_ext::ref_format::{refname, Component, RefString}; use nonempty::nonempty; use qcheck::Arbitrary; use radicle_crypto::Signer; diff --git a/radicle-cob/src/trailers.rs b/radicle-cob/src/trailers.rs index 215da098..8ea84b3f 100644 --- a/radicle-cob/src/trailers.rs +++ b/radicle-cob/src/trailers.rs @@ -1,7 +1,7 @@ // Copyright © 2019-2020 The Radicle Foundation -use git_trailers::{OwnedTrailer, Token, Trailer}; -use radicle_git_ext as ext; +use git_ext::commit::trailers::{OwnedTrailer, Token, Trailer}; +use std::ops::Deref as _; pub mod error { use thiserror::Error; @@ -30,11 +30,10 @@ impl ResourceCommitTrailer { impl TryFrom<&Trailer<'_>> for ResourceCommitTrailer { type Error = error::InvalidResourceTrailer; - fn try_from(Trailer { values, token }: &Trailer<'_>) -> Result { - let val = values.first().ok_or(Self::Error::NoValue)?; + fn try_from(Trailer { value, token }: &Trailer<'_>) -> Result { let ext_oid = - radicle_git_ext::Oid::try_from(val.as_ref()).map_err(|_| Self::Error::InvalidOid)?; - if Some(token) == Token::try_from("Rad-Resource").ok().as_ref() { + git_ext::Oid::try_from(value.as_ref()).map_err(|_| Self::Error::InvalidOid)?; + if token.deref() == "Rad-Resource" { Ok(ResourceCommitTrailer(ext_oid.into())) } else { Err(Self::Error::WrongToken) @@ -60,7 +59,7 @@ impl From for Trailer<'_> { fn from(containing: ResourceCommitTrailer) -> Self { Trailer { 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 for OwnedTrailer { } } -impl From for ResourceCommitTrailer { - fn from(oid: ext::Oid) -> Self { +impl From for ResourceCommitTrailer { + fn from(oid: git_ext::Oid) -> Self { Self(oid.into()) } } diff --git a/radicle-cob/src/type_name.rs b/radicle-cob/src/type_name.rs index b6d9f3ec..20a50314 100644 --- a/radicle-cob/src/type_name.rs +++ b/radicle-cob/src/type_name.rs @@ -2,7 +2,7 @@ use std::{fmt, str::FromStr}; -use git_ref_format::{Component, RefString}; +use git_ext::ref_format::{Component, RefString}; use serde::{Deserialize, Serialize}; use thiserror::Error; diff --git a/radicle-crypto/Cargo.toml b/radicle-crypto/Cargo.toml index 97b84c31..4b72e5dd 100644 --- a/radicle-crypto/Cargo.toml +++ b/radicle-crypto/Cargo.toml @@ -27,8 +27,8 @@ version = "1.8.0" default-features = false optional = true -[dependencies.git-ref-format] -version = "0.2" +[dependencies.radicle-git-ext] +version = "0.4" optional = true [dependencies.ssh-key] diff --git a/radicle-crypto/src/lib.rs b/radicle-crypto/src/lib.rs index e41a5646..21580c49 100644 --- a/radicle-crypto/src/lib.rs +++ b/radicle-crypto/src/lib.rs @@ -360,13 +360,16 @@ impl PublicKey { multibase::encode(multibase::Base::Base58Btc, buf) } - #[cfg(feature = "git-ref-format")] - pub fn to_namespace(&self) -> git_ref_format::RefString { - git_ref_format::refname!("refs/namespaces").join(git_ref_format::Component::from(self)) + #[cfg(feature = "radicle-git-ext")] + pub fn to_namespace(&self) -> radicle_git_ext::ref_format::RefString { + use radicle_git_ext::ref_format::{refname, Component}; + refname!("refs/namespaces").join(Component::from(self)) } - #[cfg(feature = "git-ref-format")] - pub fn from_namespaced(refstr: &git_ref_format::Namespaced) -> Result { + #[cfg(feature = "radicle-git-ext")] + pub fn from_namespaced( + refstr: &radicle_git_ext::ref_format::Namespaced, + ) -> Result { let name = refstr.namespace().into_inner(); Self::from_str(name.deref().as_str()) @@ -405,10 +408,10 @@ impl Deref for PublicKey { } } -#[cfg(feature = "git-ref-format")] -impl<'a> From<&PublicKey> for git_ref_format::Component<'a> { +#[cfg(feature = "radicle-git-ext")] +impl<'a> From<&PublicKey> for radicle_git_ext::ref_format::Component<'a> { fn from(id: &PublicKey) -> Self { - use git_ref_format::{Component, RefString}; + use radicle_git_ext::ref_format::{Component, RefString}; let refstr = 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") diff --git a/radicle-httpd/Cargo.toml b/radicle-httpd/Cargo.toml index bf9a73b8..95d693ab 100644 --- a/radicle-httpd/Cargo.toml +++ b/radicle-httpd/Cargo.toml @@ -25,7 +25,7 @@ flate2 = { version = "1" } hyper = { version = "0.14.17", default-features = false } lexopt = { version = "0.2.1" } 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_json = { version = "1", features = ["preserve_order"] } thiserror = { version = "1" } diff --git a/radicle-node/Cargo.toml b/radicle-node/Cargo.toml index e3419422..519095b2 100644 --- a/radicle-node/Cargo.toml +++ b/radicle-node/Cargo.toml @@ -19,7 +19,6 @@ colored = { version = "1.9.0" } crossbeam-channel = { version = "0.5.6" } cyphernet = { version = "0.2.0", features = ["tor", "dns", "ed25519", "p2p-ed25519"] } fastrand = { version = "1.9.0" } -git-ref-format = { version = "0.2", features = ["serde", "macro"] } io-reactor = { version = "0.1.2", features = ["popol"] } lexopt = { version = "0.2.1" } libc = { version = "0.2.137" } @@ -28,6 +27,9 @@ localtime = { version = "1.2.0" } netservices = { version = "0.2.2", features = ["io-reactor", "socket2"] } nonempty = { version = "0.8.1", features = ["serialize"] } 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" } sqlite3-src = { version = "0.4.0", features = ["bundled"] } # Ensures static linking scrypt = { version = "0.10.0", default-features = false } diff --git a/radicle-node/src/worker.rs b/radicle-node/src/worker.rs index 706e24e1..31dfe627 100644 --- a/radicle-node/src/worker.rs +++ b/radicle-node/src/worker.rs @@ -478,7 +478,7 @@ impl Worker { channels: &mut Channels, ) -> Result<(), FetchError> where - S: fetch::AsRefspecs, + S: IntoIterator, { let mut tunnel = Tunnel::with(channels, stream, self.nid, remote, self.handle.clone())?; let tunnel_addr = tunnel.local_addr(); @@ -498,7 +498,6 @@ impl Worker { let namespace = self.nid.to_namespace(); let mut fetchspecs = specs - .into_refspecs() .into_iter() // Filter out our own refs, if we aren't cloning. .filter(|fs| is_cloning || !fs.dst.starts_with(namespace.as_str())) diff --git a/radicle-node/src/worker/fetch.rs b/radicle-node/src/worker/fetch.rs index 37cd3ab9..d99f8a94 100644 --- a/radicle-node/src/worker/fetch.rs +++ b/radicle-node/src/worker/fetch.rs @@ -1,5 +1,5 @@ mod refspecs; -pub use refspecs::{AsRefspecs, Refspec, SpecialRefs}; +pub use refspecs::SpecialRefs; pub mod error; @@ -7,6 +7,7 @@ use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::ops::Deref; use radicle::crypto::{PublicKey, Unverified, Verified}; +use radicle::git::refspec; use radicle::git::{url, Namespaced}; use radicle::prelude::{Doc, Id, NodeId}; 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::{git, Storage}; +pub type Refspec = refspec::Refspec; + /// The initial phase of staging a fetch from a remote. /// /// 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` /// references. - pub fn refspecs(&self) -> Vec> { + pub fn refspecs(&self) -> Vec { let id = git::PatternString::from(IDENTITY_BRANCH.clone().into_refstring()); match self.repo { - StagedRepository::Cloning(_) => Refspec { + StagedRepository::Cloning(_) => vec![Refspec { src: id.clone(), dst: id, force: false, - } - .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. let mut remote = copy.remote_anonymous(&url)?; let refspecs: Vec<_> = Namespaces::All - .into_refspecs() + .to_refspecs() .into_iter() .map(|s| s.to_string()) .collect(); @@ -256,12 +258,19 @@ impl<'a> StagingPhaseInitial<'a> { impl<'a> StagingPhaseFinal<'a> { /// Return the fetch refspecs for fetching the necessary /// references. - pub fn refspecs(&self) -> Vec> { + pub fn refspecs(&self) -> Vec { match &self.repo { 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(( remote.id, Refspec { - src: id.clone(), - dst: id, + src: id.clone().into(), + dst: id.into(), // Nb. The identity branch is allowed to be force-updated. force: true, } @@ -368,8 +377,8 @@ impl<'a> StagingPhaseFinal<'a> { refspecs.push(( remote.id, Refspec { - src: sigrefs.clone(), - dst: sigrefs, + src: sigrefs.clone().into(), + dst: sigrefs.into(), // Nb. Sigrefs are never force-updated. force: false, } diff --git a/radicle-node/src/worker/fetch/refspecs.rs b/radicle-node/src/worker/fetch/refspecs.rs index 6e68fd56..b4afbcdc 100644 --- a/radicle-node/src/worker/fetch/refspecs.rs +++ b/radicle-node/src/worker/fetch/refspecs.rs @@ -1,43 +1,16 @@ -use std::collections::BTreeSet; -use std::fmt; -use std::fmt::Write as _; - use radicle::crypto::PublicKey; use radicle::git; use radicle::git::refs::storage::{IDENTITY_BRANCH, SIGREFS_BRANCH}; -use radicle::storage; use radicle::storage::git::NAMESPACES_GLOB; -use radicle::storage::{Namespaces, Remote}; +use radicle::storage::Namespaces; -/// A Git [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 { - pub src: T, - pub dst: U, - pub force: bool, -} - -impl fmt::Display for Refspec -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) - } -} +use super::Refspec; /// Radicle special refs, i.e. `refs/rad/*`. pub struct SpecialRefs(pub(super) Namespaces); -impl AsRefspecs for SpecialRefs { - fn as_refspecs(&self) -> Vec> { +impl SpecialRefs { + pub fn into_refspecs(self) -> Vec { match &self.0 { Namespaces::All => { 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(), } } - - fn into_refspecs(self) -> Vec> { - self.as_refspecs() - } } -fn rad_refs(pk: &PublicKey) -> Vec> { +fn rad_refs(pk: &PublicKey) -> Vec { let ns = pk.to_namespace(); let id = git::PatternString::from(ns.join(&*IDENTITY_BRANCH)); let id = Refspec { @@ -77,95 +46,3 @@ fn rad_refs(pk: &PublicKey) -> Vec Vec>; - - /// 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> { - self.as_refspecs() - } -} - -impl AsRefspecs for Namespaces { - fn as_refspecs(&self) -> Vec> { - 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 { - fn as_refspecs(&self) -> Vec> { - vec![self.clone()] - } - - fn into_refspecs(self) -> Vec> { - vec![self] - } -} - -impl AsRefspecs for Vec { - fn as_refspecs(&self) -> Vec> { - self.iter().flat_map(AsRefspecs::as_refspecs).collect() - } - - fn into_refspecs(self) -> Vec> { - self.into_iter() - .flat_map(AsRefspecs::into_refspecs) - .collect() - } -} - -impl AsRefspecs for Remote { - fn as_refspecs(&self) -> Vec> { - 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> { - fn as_refspecs(&self) -> Vec> { - self.iter() - .map(|r| Refspec { - src: r.clone().to_ref_string().into(), - dst: r.clone().to_ref_string().into(), - force: true, - }) - .collect() - } -} diff --git a/radicle-tools/Cargo.toml b/radicle-tools/Cargo.toml index f35b9916..7f77c091 100644 --- a/radicle-tools/Cargo.toml +++ b/radicle-tools/Cargo.toml @@ -7,7 +7,9 @@ edition = "2021" [dependencies] 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] version = "0" diff --git a/radicle/Cargo.toml b/radicle/Cargo.toml index 93a30298..29c1e840 100644 --- a/radicle/Cargo.toml +++ b/radicle/Cargo.toml @@ -14,7 +14,6 @@ amplify = { version = "4.0.0-beta.7", default-features = false, features = ["std crossbeam-channel = { version = "0.5.6" } cyphernet = { version = "0.2.0", features = ["tor", "dns", "ed25519"] } fastrand = { version = "1.9.0" } -git-ref-format = { version = "0", features = ["serde", "macro"] } multibase = { version = "0.9.1" } localtime = { version = "1.2.0" } log = { version = "0.4.17", features = ["std"] } @@ -30,7 +29,7 @@ thiserror = { version = "1" } unicode-normalization = { version = "0.1" } [dependencies.git2] -version = "0.16.1" +version = "0.17.0" default-features = false features = ["vendored-libgit2"] @@ -45,7 +44,7 @@ version = "0" [dependencies.radicle-crypto] path = "../radicle-crypto" version = "0" -features = ["git-ref-format", "ssh", "sqlite", "cyphernet"] +features = ["radicle-git-ext", "ssh", "sqlite", "cyphernet"] [dependencies.radicle-ssh] path = "../radicle-ssh" diff --git a/radicle/src/cob/identity.rs b/radicle/src/cob/identity.rs index 72e1daf5..9e0a4791 100644 --- a/radicle/src/cob/identity.rs +++ b/radicle/src/cob/identity.rs @@ -59,6 +59,8 @@ pub enum Action { revision: RevisionId, }, 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, proposed: Doc, }, @@ -68,16 +70,7 @@ pub enum Action { }, } -impl HistoryAction for Action { - fn parents(&self) -> Vec { - match self { - Self::Revision { current, .. } => { - vec![*current] - } - _ => vec![], - } - } -} +impl HistoryAction for Action {} /// Error applying an operation onto a state. #[derive(Error, Debug)] diff --git a/radicle/src/cob/issue.rs b/radicle/src/cob/issue.rs index acafcfc3..887a2691 100644 --- a/radicle/src/cob/issue.rs +++ b/radicle/src/cob/issue.rs @@ -575,7 +575,9 @@ mod test { #[test] fn test_issue_create_and_assign() { 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 assignee: ActorId = arbitrary::gen(1); @@ -612,7 +614,9 @@ mod test { #[test] fn test_issue_create_and_reassign() { 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 assignee: ActorId = arbitrary::gen(1); @@ -642,7 +646,9 @@ mod test { #[test] fn test_issue_create_and_get() { 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 created = issues .create("My first issue", "Blah blah blah.", &[], &[], &signer) @@ -664,7 +670,9 @@ mod test { #[test] fn test_issue_create_and_change_state() { 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 issue = issues .create("My first issue", "Blah blah blah.", &[], &[], &signer) @@ -698,7 +706,9 @@ mod test { #[test] fn test_issue_create_and_unassign() { 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 assignee: ActorId = arbitrary::gen(1); @@ -726,7 +736,9 @@ mod test { #[test] fn test_issue_edit_title() { 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 issue = issues .create("My first issue", "Blah blah blah.", &[], &[], &signer) @@ -744,7 +756,9 @@ mod test { #[test] fn test_issue_react() { 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 issue = issues .create("My first issue", "Blah blah blah.", &[], &[], &signer) @@ -767,7 +781,9 @@ mod test { #[test] fn test_issue_reply() { 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 issue = issues .create("My first issue", "Blah blah blah.", &[], &[], &signer) @@ -805,7 +821,9 @@ mod test { #[test] fn test_issue_tag() { 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 bug_tag = Tag::new("bug").unwrap(); let ux_tag = Tag::new("ux").unwrap(); @@ -835,7 +853,9 @@ mod test { #[test] fn test_issue_comment() { 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 mut issues = Issues::open(&project).unwrap(); let mut issue = issues @@ -886,7 +906,9 @@ mod test { #[test] fn test_issue_all() { 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(); issues.create("First", "Blah", &[], &[], &signer).unwrap(); @@ -910,7 +932,9 @@ mod test { #[test] fn test_issue_multilines() { 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 created = issues .create( diff --git a/radicle/src/cob/patch.rs b/radicle/src/cob/patch.rs index c5d1826e..4c574602 100644 --- a/radicle/src/cob/patch.rs +++ b/radicle/src/cob/patch.rs @@ -1391,21 +1391,21 @@ mod test { #[test] fn test_patch_create_and_get() { let tmp = tempfile::tempdir().unwrap(); - let (_, signer, project) = test::setup::context(&tmp); - let mut patches = Patches::open(&project).unwrap(); + let ctx = test::setup::Context::new(&tmp); + 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 target = MergeTarget::Delegates; - let oid = git::Oid::from_str("e2a85016a458cd809c0ecee81f8c99613b0b0945").unwrap(); - let base = git::Oid::from_str("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(); let patch = patches .create( "My first patch", "Blah blah blah.", target, - base, - oid, + pr.base, + pr.oid, &[], - &signer, + signer, ) .unwrap(); @@ -1426,8 +1426,8 @@ mod test { assert_eq!(revision.author.id(), &author); assert_eq!(revision.description(), ""); assert_eq!(revision.discussion.len(), 0); - assert_eq!(revision.oid, oid); - assert_eq!(revision.base, base); + assert_eq!(revision.oid, pr.oid); + assert_eq!(revision.base, pr.base); let (id, _, _) = patches.find_by_revision(rev_id).unwrap().unwrap(); assert_eq!(id, patch_id); @@ -1436,17 +1436,19 @@ mod test { #[test] fn test_patch_discussion() { let tmp = tempfile::tempdir().unwrap(); - let (_, signer, project) = test::setup::context(&tmp); - let mut patches = Patches::open(&project).unwrap(); + let ctx = test::setup::Context::new(&tmp); + let signer = &ctx.signer; + let pr = ctx.branch_with(test::setup::initial_blobs()); + let mut patches = Patches::open(&ctx.project).unwrap(); let patch = patches .create( "My first patch", "Blah blah blah.", MergeTarget::Delegates, - git::Oid::try_from("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(), - git::Oid::try_from("e2a85016a458cd809c0ecee81f8c99613b0b0945").unwrap(), + pr.base, + pr.oid, &[], - &signer, + signer, ) .unwrap(); @@ -1455,7 +1457,7 @@ mod test { let (revision_id, _) = patch.revisions().last().unwrap(); assert!( patch - .comment(*revision_id, "patch comment", None, &signer) + .comment(*revision_id, "patch comment", None, signer) .is_ok(), "can comment on patch" ); @@ -1468,25 +1470,25 @@ mod test { #[test] fn test_patch_merge() { let tmp = tempfile::tempdir().unwrap(); - let (_, signer, project) = test::setup::context(&tmp); - let oid = git::Oid::from_str("e2a85016a458cd809c0ecee81f8c99613b0b0945").unwrap(); - let base = git::Oid::from_str("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(); - let mut patches = Patches::open(&project).unwrap(); + let ctx = test::setup::Context::new(&tmp); + let signer = &ctx.signer; + let pr = ctx.branch_with(test::setup::initial_blobs()); + let mut patches = Patches::open(&ctx.project).unwrap(); let mut patch = patches .create( "My first patch", "Blah blah blah.", MergeTarget::Delegates, - base, - oid, + pr.base, + pr.oid, &[], - &signer, + signer, ) .unwrap(); let id = patch.id; 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(); @@ -1496,25 +1498,25 @@ mod test { let merge = merges.first().unwrap(); assert_eq!(merge.node, *signer.public_key()); - assert_eq!(merge.commit, base); + assert_eq!(merge.commit, pr.base); } #[test] fn test_patch_review() { let tmp = tempfile::tempdir().unwrap(); - let (_, signer, project) = test::setup::context(&tmp); - let base = git::Oid::from_str("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(); - let oid = git::Oid::from_str("518d5069f94c03427f694bb494ac1cd7d1339380").unwrap(); - let mut patches = Patches::open(&project).unwrap(); + let ctx = test::setup::Context::new(&tmp); + let signer = &ctx.signer; + let pr = ctx.branch_with(test::setup::initial_blobs()); + let mut patches = Patches::open(&ctx.project).unwrap(); let mut patch = patches .create( "My first patch", "Blah blah blah.", MergeTarget::Delegates, - base, - oid, + pr.base, + pr.oid, &[], - &signer, + signer, ) .unwrap(); @@ -1525,7 +1527,7 @@ mod test { Some(Verdict::Accept), Some("LGTM".to_owned()), vec![], - &signer, + signer, ) .unwrap(); @@ -1626,20 +1628,20 @@ mod test { #[test] fn test_patch_review_edit() { let tmp = tempfile::tempdir().unwrap(); - let (_, signer, project) = test::setup::context(&tmp); - let base = git::Oid::from_str("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(); - let oid = git::Oid::from_str("518d5069f94c03427f694bb494ac1cd7d1339380").unwrap(); + let ctx = test::setup::Context::new(&tmp); + let signer = &ctx.signer; + let pr = ctx.branch_with(test::setup::initial_blobs()); 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 .create( "My first patch", "Blah blah blah.", MergeTarget::Delegates, - base, - oid, + pr.base, + pr.oid, &[], - &signer, + signer, ) .unwrap(); @@ -1650,7 +1652,7 @@ mod test { location: CodeLocation { blob, path: Path::new("file.rs").to_path_buf(), - commit: oid, + commit: pr.oid, lines: 1..3, }, comment: "Nice!".to_owned(), @@ -1662,7 +1664,7 @@ mod test { Some(Verdict::Accept), Some("LGTM".to_owned()), inline.clone(), - &signer, + signer, ) .unwrap(); patch @@ -1671,7 +1673,7 @@ mod test { Some(Verdict::Reject), Some("LGTM".to_owned()), vec![], - &signer, + signer, ) .unwrap(); // Overwrite the verdict. @@ -1691,7 +1693,7 @@ mod test { Some(Verdict::Reject), Some("Whoops!".to_owned()), vec![], - &signer, + signer, ) .unwrap(); // Overwrite the comment. let (_, revision) = patch.latest().unwrap(); @@ -1704,19 +1706,19 @@ mod test { #[test] fn test_patch_reject_to_accept() { let tmp = tempfile::tempdir().unwrap(); - let (_, signer, project) = test::setup::context(&tmp); - let base = git::Oid::from_str("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(); - let oid = git::Oid::from_str("518d5069f94c03427f694bb494ac1cd7d1339380").unwrap(); - let mut patches = Patches::open(&project).unwrap(); + let ctx = test::setup::Context::new(&tmp); + let signer = &ctx.signer; + let pr = ctx.branch_with(test::setup::initial_blobs()); + let mut patches = Patches::open(&ctx.project).unwrap(); let mut patch = patches .create( "My first patch", "Blah blah blah.", MergeTarget::Delegates, - base, - oid, + pr.base, + pr.oid, &[], - &signer, + signer, ) .unwrap(); @@ -1729,7 +1731,7 @@ mod test { Some(Verdict::Reject), Some("Nah".to_owned()), vec![], - &signer, + signer, ) .unwrap(); patch @@ -1738,7 +1740,7 @@ mod test { Some(Verdict::Accept), Some("LGTM".to_owned()), vec![], - &signer, + signer, ) .unwrap(); @@ -1755,19 +1757,19 @@ mod test { #[test] fn test_patch_review_remove_fields() { let tmp = tempfile::tempdir().unwrap(); - let (_, signer, project) = test::setup::context(&tmp); - let base = git::Oid::from_str("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(); - let oid = git::Oid::from_str("518d5069f94c03427f694bb494ac1cd7d1339380").unwrap(); - let mut patches = Patches::open(&project).unwrap(); + let ctx = test::setup::Context::new(&tmp); + let signer = &ctx.signer; + let pr = ctx.branch_with(test::setup::initial_blobs()); + let mut patches = Patches::open(&ctx.project).unwrap(); let mut patch = patches .create( "My first patch", "Blah blah blah.", MergeTarget::Delegates, - base, - oid, + pr.base, + pr.oid, &[], - &signer, + signer, ) .unwrap(); @@ -1780,10 +1782,10 @@ mod test { Some(Verdict::Reject), Some("Nah".to_owned()), vec![], - &signer, + signer, ) .unwrap(); - patch.review(rid, None, None, vec![], &signer).unwrap(); + patch.review(rid, None, None, vec![], signer).unwrap(); let id = patch.id; let patch = patches.get_mut(&id).unwrap(); @@ -1797,20 +1799,19 @@ mod test { #[test] fn test_patch_update() { let tmp = tempfile::tempdir().unwrap(); - let (_, signer, project) = test::setup::context(&tmp); - let base = git::Oid::from_str("af08e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(); - let rev0_oid = git::Oid::from_str("518d5069f94c03427f694bb494ac1cd7d1339380").unwrap(); - let rev1_oid = git::Oid::from_str("cb18e95ada2bb38aadd8e6cef0963ce37a87add3").unwrap(); - let mut patches = Patches::open(&project).unwrap(); + let ctx = test::setup::Context::new(&tmp); + let signer = &ctx.signer; + let pr = ctx.branch_with(test::setup::initial_blobs()); + let mut patches = Patches::open(&ctx.project).unwrap(); let mut patch = patches .create( "My first patch", "Blah blah blah.", MergeTarget::Delegates, - base, - rev0_oid, + pr.base, + pr.oid, &[], - &signer, + signer, ) .unwrap(); @@ -1818,8 +1819,9 @@ mod test { assert_eq!(patch.description(), "Blah blah blah."); assert_eq!(patch.version(), 0); + let update = ctx.branch_with(test::setup::update_blobs()); let _ = patch - .update("I've made changes.", base, rev1_oid, &signer) + .update("I've made changes.", pr.base, update.oid, signer) .unwrap(); assert_eq!(patch.clock.get(), 2); @@ -1837,7 +1839,7 @@ mod test { let (_, revision) = patch.latest().unwrap(); 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."); } } diff --git a/radicle/src/cob/thread.rs b/radicle/src/cob/thread.rs index cb2461c0..b896fd92 100644 --- a/radicle/src/cob/thread.rs +++ b/radicle/src/cob/thread.rs @@ -497,7 +497,7 @@ mod tests { #[test] fn test_redact_comment() { 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::(1); let mut alice = Actor::new(signer); let mut thread = Thread::default(); diff --git a/radicle/src/git.rs b/radicle/src/git.rs index 2f9358af..30b91650 100644 --- a/radicle/src/git.rs +++ b/radicle/src/git.rs @@ -3,7 +3,7 @@ use std::path::Path; use std::process::Command; use std::str::FromStr; -use git_ref_format as format; +use git_ext::ref_format as format; use once_cell::sync::Lazy; use crate::collections::HashMap; @@ -17,8 +17,8 @@ pub use ext::Error; pub use ext::NotFound; pub use ext::Oid; pub use git2 as raw; -pub use git_ref_format as fmt; -pub use git_ref_format::{ +pub use git_ext::ref_format as fmt; +pub use git_ext::ref_format::{ component, lit, name, qualified, refname, refspec, refspec::{PatternStr, PatternString}, Component, Namespaced, Qualified, RefStr, RefString, @@ -152,6 +152,7 @@ pub mod refs { pub mod storage { use format::{ + lit, name::component, refspec::{self, PatternString}, }; @@ -181,7 +182,7 @@ pub mod refs { /// `refs/namespaces//refs/heads/` /// 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. diff --git a/radicle/src/identity/doc/id.rs b/radicle/src/identity/doc/id.rs index bfe0c7c2..83623630 100644 --- a/radicle/src/identity/doc/id.rs +++ b/radicle/src/identity/doc/id.rs @@ -1,7 +1,7 @@ use std::ops::Deref; use std::{ffi::OsString, fmt, str::FromStr}; -use git_ref_format::{Component, RefString}; +use git_ext::ref_format::{Component, RefString}; use thiserror::Error; use crate::crypto; diff --git a/radicle/src/lib.rs b/radicle/src/lib.rs index a355b73a..ea6bc609 100644 --- a/radicle/src/lib.rs +++ b/radicle/src/lib.rs @@ -7,6 +7,7 @@ pub extern crate radicle_crypto as crypto; #[macro_use] extern crate amplify; +extern crate radicle_git_ext as git_ext; mod canonical; pub mod cob; diff --git a/radicle/src/storage.rs b/radicle/src/storage.rs index f68c3800..00c34d57 100644 --- a/radicle/src/storage.rs +++ b/radicle/src/storage.rs @@ -16,11 +16,12 @@ pub use radicle_git_ext::Oid; use crate::collections::HashMap; 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::doc::DocError; use crate::identity::Did; use crate::identity::{Id, IdentityError}; +use crate::storage::git::NAMESPACES_GLOB; use crate::storage::refs::Refs; use self::refs::SignedRefs; @@ -38,6 +39,29 @@ pub enum Namespaces { Trusted(HashSet), } +impl Namespaces { + pub fn to_refspecs(&self) -> Vec> { + 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 for Namespaces { fn from_iter>(iter: T) -> Self { Self::Trusted(iter.into_iter().collect()) @@ -236,6 +260,22 @@ impl Remote { refs: self.refs.unverified(), } } + + pub fn to_refspecs(&self) -> Vec> { + 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 Deref for Remote { diff --git a/radicle/src/storage/git.rs b/radicle/src/storage/git.rs index 2d2976b9..6820e05c 100644 --- a/radicle/src/storage/git.rs +++ b/radicle/src/storage/git.rs @@ -6,7 +6,6 @@ use std::path::{Path, PathBuf}; use std::{fs, io}; use crypto::{Signer, Unverified, Verified}; -use git_ref_format::refspec; use once_cell::sync::Lazy; use crate::git; @@ -25,10 +24,10 @@ pub use crate::storage::Error; use super::RemoteId; -pub static NAMESPACES_GLOB: Lazy = - Lazy::new(|| refspec::pattern!("refs/namespaces/*")); +pub static NAMESPACES_GLOB: Lazy = + Lazy::new(|| git::refspec::pattern!("refs/namespaces/*")); pub static SIGREFS_GLOB: Lazy = - Lazy::new(|| refspec::pattern!("refs/namespaces/*/rad/sigrefs")); + Lazy::new(|| git::refspec::pattern!("refs/namespaces/*/rad/sigrefs")); pub static CANONICAL_IDENTITY: Lazy = Lazy::new(|| { git::Qualified::from_components( git::name::component!("rad"), diff --git a/radicle/src/storage/git/cob.rs b/radicle/src/storage/git/cob.rs index e2b24063..320aabd9 100644 --- a/radicle/src/storage/git/cob.rs +++ b/radicle/src/storage/git/cob.rs @@ -31,7 +31,7 @@ pub enum TypesError { #[error(transparent)] ParseObjectId(#[from] cob::object::ParseObjectId), #[error(transparent)] - RefFormat(#[from] git_ref_format::Error), + RefFormat(#[from] git::fmt::Error), } impl cob::Store for Repository {} diff --git a/radicle/src/storage/refs.rs b/radicle/src/storage/refs.rs index 3d9d9f09..5a4b82c7 100644 --- a/radicle/src/storage/refs.rs +++ b/radicle/src/storage/refs.rs @@ -361,7 +361,7 @@ pub mod canonical { #[derive(Debug, thiserror::Error)] pub enum Error { #[error(transparent)] - InvalidRef(#[from] git_ref_format::Error), + InvalidRef(#[from] git::fmt::Error), #[error("invalid canonical format")] InvalidFormat, #[error(transparent)] diff --git a/radicle/src/test.rs b/radicle/src/test.rs index 5e96a722..1911dc65 100644 --- a/radicle/src/test.rs +++ b/radicle/src/test.rs @@ -10,20 +10,108 @@ pub mod setup { use crate::crypto::test::signer::MockSigner; use crate::prelude::*; use crate::{ + git, profile::Home, + rad::REMOTE_NAME, test::{fixtures, storage::git::Repository}, Storage, }; - pub fn context(tmp: &TempDir) -> (Storage, MockSigner, Repository) { - let mut rng = fastrand::Rng::new(); - let signer = MockSigner::new(&mut rng); - let home = tmp.path().join("home"); - let paths = Home::new(home.as_path()).unwrap(); - let storage = Storage::open(paths.storage()).unwrap(); - let (id, _, _, _) = fixtures::project(tmp.path().join("copy"), &storage, &signer).unwrap(); - let project = storage.repository(id).unwrap(); + #[derive(Debug)] + pub struct BranchWith { + pub base: git::Oid, + pub oid: git::Oid, + } - (storage, signer, project) + 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 signer = MockSigner::new(&mut rng); + let home = tmp.path().join("home"); + let paths = Home::new(home.as_path()).unwrap(); + let storage = Storage::open(paths.storage()).unwrap(); + let (id, _, working, _) = + fixtures::project(tmp.path().join("copy"), &storage, &signer).unwrap(); + let project = storage.repository(id).unwrap(); + + Self { + storage, + signer, + project, + working, + } + } + + pub fn branch_with( + &self, + blobs: impl IntoIterator)>, + ) -> 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)> { + 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)> { + 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)>, + 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() } } diff --git a/radicle/src/test/storage.rs b/radicle/src/test/storage.rs index ca4eb38d..a56c9c79 100644 --- a/radicle/src/test/storage.rs +++ b/radicle/src/test/storage.rs @@ -2,8 +2,7 @@ use std::collections::HashMap; use std::io; use std::path::{Path, PathBuf}; -use git_ref_format as fmt; -use radicle_git_ext as git_ext; +use git_ext::ref_format as fmt; use crate::crypto::{Signer, Verified}; use crate::identity::doc::{Doc, DocError, Id};