workspace: A Little Less `git2`
Make `git2` an *optional* dependency of `radicle-cob`, and refactor
`radicle` to depend on crates that in turn do not depend on `git2`
*non-optionally*
The main offending dependency of `radicle-cob` is `radicle-git-ext`
from the `radicle-git` workspace in repository
(rad:z6cFWeWpnZNHh9rUW8phgA3b5yGt) which *non-optionally* depends on
`git2`.
So, to achieve removal of this dependency:
1. The crate is refactored to depend on the new crates
`radicle-git-ref-format` `radicle-git-metadata`, and
`radicle-oid` introduced in the previous commits, instead of
`radicle-git-ext`.
2. Some code from the `radicle-git-ext` crate in the `radicle-git`
workspace in repository (rad:z6cFWeWpnZNHh9rUW8phgA3b5yGt) is
copied. See `crates/radicle-cob/src/backend/git/commit.rs`.
This cascades to `radicle` and its dependents.
Firstly, the there is an
`impl Deref<Target=git2::Oid> for radicle_git_ext::Oid`. This made
it very convenient to just deref to obtain the wrapped `git2::Oid`,
so there are many expressions of the shape `*oid` in `radicle` and
its dependents. However `radicle-oid` does not provide
`impl Deref<Target=git2::Oid> for radicle_oid::Oid`, as notably,
`Target` is an associated type, and not a type parameter, so an
implementation of `Deref` would tie the new `Oid` too tightly to a
particular implementation.
Instead, work with `impl From<radicle_oid::Oid> for git2::Oid`
(which can be enabled using the feature flag `radicle-oid/git2`).
This explains the changes from `*oid` to `oid.into()` at every
transition to "`git2` land".
Secondly, `radicle` and its dependents are refactored to also depend
on `radicle-git-ref-format` and (much less prominently)
`radicle-git-metadata` instead of `radicle-git-ext`
This is to avoid pulling in `git2` via these dependencies.
Thirdly, as the re-exports in `crates/radicle/src/git.rs` change,
they are at the same time also cleaned up. Notably, the types from
`radicle-git-ref-format` are re-exported under `fmt` only, not "twice".
While overall this obviously is very much a breaking change, these
changes should mostly amount to changing from `Deref` to `Into`, i.e.,
`*oid` → `oid.into()` and rewriting imports for `radicle:
:fmt`.
This is indicated by the mostly mechanical nature of the changes to
`crates/radicle-{cli,node,remote-helper}`.
This commit is contained in:
parent
f4a8908727
commit
b7cfcfff75
|
|
@ -239,12 +239,6 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
|
|
@ -1119,9 +1113,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
|||
|
||||
[[package]]
|
||||
name = "git-ref-format"
|
||||
version = "0.3.1"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7428e0d6e549a9a613d6f019b839a0f5142c331295b79e119ca8f4faac145da1"
|
||||
checksum = "76314f6eb43910ebc5eb89a1f0728724a6b9144dabd18bca4e7cc7c01c3804e3"
|
||||
dependencies = [
|
||||
"git-ref-format-core",
|
||||
"git-ref-format-macro",
|
||||
|
|
@ -1129,9 +1123,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "git-ref-format-core"
|
||||
version = "0.3.1"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaeb9672a55e9e32cb6d3ef781e7526b25ab97d499fae71615649340b143424"
|
||||
checksum = "ae6d9ee666ca7d4ad49cbf7174f785f299a18b37565694f665e8c7df24999cdd"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"serde",
|
||||
|
|
@ -1140,12 +1134,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "git-ref-format-macro"
|
||||
version = "0.3.1"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b6ca5353accc201f6324dff744ba4660099546d4daf187ba868f07562e36ca4"
|
||||
checksum = "2dc2ded12a6ea2b6e63afaf09415e4c15bf4baa74e530acd9daed9ff47e7dd41"
|
||||
dependencies = [
|
||||
"git-ref-format-core",
|
||||
"proc-macro-error",
|
||||
"proc-macro-error2",
|
||||
"quote",
|
||||
"syn 2.0.89",
|
||||
]
|
||||
|
|
@ -2238,12 +2232,6 @@ dependencies = [
|
|||
"cyphergraphy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nonempty"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ff7ac1e5ea23db6d61ad103e91864675049644bf47c35912336352fa4e9c109"
|
||||
|
||||
[[package]]
|
||||
name = "nonempty"
|
||||
version = "0.9.0"
|
||||
|
|
@ -2643,27 +2631,25 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
"syn 2.0.89",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2751,13 +2737,15 @@ dependencies = [
|
|||
"localtime",
|
||||
"log",
|
||||
"multibase",
|
||||
"nonempty 0.9.0",
|
||||
"nonempty",
|
||||
"pretty_assertions",
|
||||
"qcheck",
|
||||
"qcheck-macros",
|
||||
"radicle-cob",
|
||||
"radicle-crypto",
|
||||
"radicle-git-ext",
|
||||
"radicle-git-metadata",
|
||||
"radicle-git-ref-format",
|
||||
"radicle-oid",
|
||||
"radicle-ssh",
|
||||
"schemars",
|
||||
"serde",
|
||||
|
|
@ -2779,19 +2767,18 @@ dependencies = [
|
|||
"chrono",
|
||||
"clap",
|
||||
"dunce",
|
||||
"git-ref-format",
|
||||
"human-panic",
|
||||
"itertools",
|
||||
"lexopt",
|
||||
"localtime",
|
||||
"log",
|
||||
"nonempty 0.9.0",
|
||||
"nonempty",
|
||||
"pretty_assertions",
|
||||
"radicle",
|
||||
"radicle-cli-test",
|
||||
"radicle-cob",
|
||||
"radicle-crypto",
|
||||
"radicle-git-ext",
|
||||
"radicle-git-ref-format",
|
||||
"radicle-node",
|
||||
"radicle-surf",
|
||||
"radicle-term",
|
||||
|
|
@ -2837,14 +2824,17 @@ name = "radicle-cob"
|
|||
version = "0.17.0"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"git-ref-format-core",
|
||||
"git2",
|
||||
"log",
|
||||
"nonempty 0.9.0",
|
||||
"nonempty",
|
||||
"qcheck",
|
||||
"qcheck-macros",
|
||||
"radicle-crypto",
|
||||
"radicle-dag",
|
||||
"radicle-git-ext",
|
||||
"radicle-git-metadata",
|
||||
"radicle-git-ref-format",
|
||||
"radicle-oid",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"signature 2.2.0",
|
||||
|
|
@ -2894,17 +2884,18 @@ dependencies = [
|
|||
"gix-protocol",
|
||||
"gix-transport",
|
||||
"log",
|
||||
"nonempty 0.9.0",
|
||||
"nonempty",
|
||||
"radicle",
|
||||
"radicle-git-ext",
|
||||
"radicle-git-ref-format",
|
||||
"radicle-oid",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radicle-git-ext"
|
||||
version = "0.8.1"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b78c26e67d1712ad5a0c602ae3b236609461372ac04e200bda359fe4a1c6650"
|
||||
checksum = "fb3de6999a8ff570e0dd92a04ba3132853dd08a3bcfc2f7faf56de7b1bd36053"
|
||||
dependencies = [
|
||||
"git-ref-format",
|
||||
"git2",
|
||||
|
|
@ -2944,13 +2935,12 @@ dependencies = [
|
|||
"localtime",
|
||||
"log",
|
||||
"mio 1.0.4",
|
||||
"nonempty 0.9.0",
|
||||
"nonempty",
|
||||
"qcheck",
|
||||
"qcheck-macros",
|
||||
"radicle",
|
||||
"radicle-crypto",
|
||||
"radicle-fetch",
|
||||
"radicle-git-ext",
|
||||
"radicle-protocol",
|
||||
"radicle-signals",
|
||||
"radicle-systemd",
|
||||
|
|
@ -2991,14 +2981,13 @@ dependencies = [
|
|||
"fastrand",
|
||||
"localtime",
|
||||
"log",
|
||||
"nonempty 0.9.0",
|
||||
"nonempty",
|
||||
"paste",
|
||||
"qcheck",
|
||||
"qcheck-macros",
|
||||
"radicle",
|
||||
"radicle-crypto",
|
||||
"radicle-fetch",
|
||||
"radicle-git-ext",
|
||||
"scrypt",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
@ -3015,7 +3004,6 @@ dependencies = [
|
|||
"radicle",
|
||||
"radicle-cli",
|
||||
"radicle-crypto",
|
||||
"radicle-git-ext",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
|
|
@ -3049,22 +3037,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "radicle-std-ext"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db20136bbc9ae63f3fec8e5a6c369f4902fac2244501b5dfc6d668e43475aaa4"
|
||||
checksum = "5310e7a04506b6ce92dc9c47b26bd24c1c680937a3dcd13cd20847f89dbda32a"
|
||||
|
||||
[[package]]
|
||||
name = "radicle-surf"
|
||||
version = "0.22.0"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb308c3989087f71e43d8c7a2737273fdc7fbcd3e6628af81a42f601ae64f314"
|
||||
checksum = "2f08adc954d4a49287d86bd1ce14ca7e34a11a3acd904044db30551ca8f4e46a"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.13.1",
|
||||
"base64 0.21.7",
|
||||
"flate2",
|
||||
"git2",
|
||||
"log",
|
||||
"nonempty 0.5.0",
|
||||
"nonempty",
|
||||
"radicle-git-ext",
|
||||
"radicle-std-ext",
|
||||
"tar",
|
||||
|
|
|
|||
11
Cargo.toml
11
Cargo.toml
|
|
@ -28,8 +28,6 @@ crossbeam-channel = "0.5.6"
|
|||
cyphernet = "0.5.2"
|
||||
dunce = "1.0.5"
|
||||
fastrand = { version = "2.0.0", default-features = false }
|
||||
git-ref-format-core = { version = "0.3.0", default-features = false }
|
||||
git-ref-format = { version = "0.3.0", default-features = false }
|
||||
git2 = { version = "0.19.0", default-features = false }
|
||||
human-panic = "2"
|
||||
itertools = "0.14"
|
||||
|
|
@ -49,7 +47,7 @@ radicle-cob = { version = "0.17", path = "crates/radicle-cob" }
|
|||
radicle-crypto = { version = "0.14", path = "crates/radicle-crypto" }
|
||||
radicle-dag = { version = "0.10", path = "crates/radicle-dag" }
|
||||
radicle-fetch = { version = "0.16", path = "crates/radicle-fetch" }
|
||||
radicle-git-ext = { version = "0.8", default-features = false }
|
||||
radicle-git-metadata = { version = "0.1.0", path = "crates/radicle-git-metadata", default-features = false }
|
||||
radicle-git-ref-format = { version = "0.1.0", path = "crates/radicle-git-ref-format", default-features = false }
|
||||
radicle-node = { version = "0.16", path = "crates/radicle-node" }
|
||||
radicle-oid = { version = "0.1.0", path = "crates/radicle-oid", default-features = false }
|
||||
|
|
@ -70,6 +68,13 @@ thiserror = "1.0"
|
|||
winpipe = "0.1.1"
|
||||
zeroize = "1.5.7"
|
||||
|
||||
# Crates from the "radicle-git" workspace. These should be synced manually.
|
||||
# When updating, start from `radicle-surf`:
|
||||
# `radicle-surf` → `radicle-git-ext` → `git-ref-format` → `git-ref-format-core`
|
||||
# Also note that `radicle-surf → git2` so try to also sync with `git2`.
|
||||
git-ref-format-core = { version = "0.5.0", default-features = false }
|
||||
radicle-surf = "0.25.0"
|
||||
|
||||
[workspace.lints]
|
||||
clippy.type_complexity = "allow"
|
||||
clippy.enum_variant_names = "allow"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ anyhow = "1"
|
|||
chrono = { workspace = true, features = ["clock", "std"] }
|
||||
clap = { version = "4.5.44", features = ["derive"] }
|
||||
dunce = { workspace = true }
|
||||
git-ref-format = { version = "0.3.0", features = ["macro"] }
|
||||
human-panic.workspace = true
|
||||
itertools.workspace = true
|
||||
lexopt = { workspace = true }
|
||||
|
|
@ -28,10 +27,8 @@ nonempty = { workspace = true }
|
|||
radicle = { workspace = true, features = ["logger", "schemars"] }
|
||||
radicle-cob = { workspace = true }
|
||||
radicle-crypto = { workspace = true }
|
||||
# N.b. this is required to use macros, even though it's re-exported
|
||||
# through radicle
|
||||
radicle-git-ext = { workspace = true, features = ["serde"] }
|
||||
radicle-surf = "0.22.0"
|
||||
radicle-git-ref-format = { workspace = true, features = ["macro"] }
|
||||
radicle-surf = { workspace = true }
|
||||
radicle-term = { workspace = true }
|
||||
schemars = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -156,9 +156,9 @@ pub fn setup_remotes(
|
|||
pub fn setup_remote(
|
||||
setup: &project::SetupRemote,
|
||||
remote_id: &NodeId,
|
||||
remote_name: Option<git::RefString>,
|
||||
remote_name: Option<git::fmt::RefString>,
|
||||
aliases: &impl AliasStore,
|
||||
) -> anyhow::Result<git::RefString> {
|
||||
) -> anyhow::Result<git::fmt::RefString> {
|
||||
let remote_name = if let Some(name) = remote_name {
|
||||
name
|
||||
} else {
|
||||
|
|
@ -167,7 +167,7 @@ pub fn setup_remote(
|
|||
} else {
|
||||
remote_id.to_human()
|
||||
};
|
||||
git::RefString::try_from(name.as_str())
|
||||
git::fmt::RefString::try_from(name.as_str())
|
||||
.map_err(|_| anyhow!("invalid remote name: '{name}'"))?
|
||||
};
|
||||
let (remote, branch) = setup.run(&remote_name, *remote_id)?;
|
||||
|
|
|
|||
|
|
@ -4,16 +4,17 @@ use std::process;
|
|||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use git_ref_format::Qualified;
|
||||
use localtime::LocalTime;
|
||||
use radicle::cob::TypedId;
|
||||
use radicle::git::fmt::Qualified;
|
||||
use radicle::git::BranchName;
|
||||
use radicle::identity::Identity;
|
||||
use radicle::issue::cache::Issues as _;
|
||||
use radicle::node::notifications;
|
||||
use radicle::node::notifications::*;
|
||||
use radicle::patch::cache::Patches as _;
|
||||
use radicle::prelude::{NodeId, Profile, RepoId};
|
||||
use radicle::storage::{BranchName, ReadRepository, ReadStorage};
|
||||
use radicle::storage::{ReadRepository, ReadStorage};
|
||||
use radicle::{cob, git, Storage};
|
||||
|
||||
use term::Element as _;
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ use serde_json as json;
|
|||
|
||||
use radicle::crypto::ssh;
|
||||
use radicle::explorer::ExplorerUrl;
|
||||
use radicle::git::fmt::RefString;
|
||||
use radicle::git::raw;
|
||||
use radicle::git::raw::ErrorExt as _;
|
||||
use radicle::git::RefString;
|
||||
use radicle::identity::project::ProjectName;
|
||||
use radicle::identity::{Doc, RepoId, Visibility};
|
||||
use radicle::node::events::UploadPack;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use anyhow::anyhow;
|
|||
|
||||
use radicle::cob::patch::PatchId;
|
||||
use radicle::cob::{patch, Label, Reaction};
|
||||
use radicle::git::RefString;
|
||||
use radicle::git::fmt::RefString;
|
||||
use radicle::patch::cache::Patches as _;
|
||||
use radicle::storage::git::transport;
|
||||
use radicle::{prelude::*, Node};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use anyhow::anyhow;
|
||||
|
||||
use git_ref_format::Qualified;
|
||||
use radicle::cob::patch;
|
||||
use radicle::cob::patch::RevisionId;
|
||||
use radicle::git::fmt::Qualified;
|
||||
use radicle::git::fmt::RefString;
|
||||
use radicle::git::raw::ErrorExt as _;
|
||||
use radicle::git::RefString;
|
||||
use radicle::patch::cache::Patches as _;
|
||||
use radicle::patch::PatchId;
|
||||
use radicle::storage::git::Repository;
|
||||
|
|
@ -25,7 +25,7 @@ impl Options {
|
|||
Some(refname) => Ok(Qualified::from_refstr(refname)
|
||||
.map_or_else(|| refname.clone(), |q| q.to_ref_string())),
|
||||
// SAFETY: Patch IDs are valid refstrings.
|
||||
None => Ok(git::refname!("patch")
|
||||
None => Ok(git::fmt::refname!("patch")
|
||||
.join(RefString::try_from(term::format::cob(id).item).unwrap())),
|
||||
}
|
||||
}
|
||||
|
|
@ -57,8 +57,7 @@ pub fn run(
|
|||
let mut spinner = term::spinner("Performing checkout...");
|
||||
let patch_branch = opts.branch(patch_id)?;
|
||||
|
||||
let commit =
|
||||
match working.find_branch(patch_branch.as_str(), radicle::git::raw::BranchType::Local) {
|
||||
let commit = match working.find_branch(patch_branch.as_str(), git::raw::BranchType::Local) {
|
||||
Ok(branch) if opts.force => {
|
||||
let commit = find_patch_commit(revision, stored, working)?;
|
||||
let mut r = branch.into_reference();
|
||||
|
|
@ -67,7 +66,7 @@ pub fn run(
|
|||
}
|
||||
Ok(branch) => {
|
||||
let head = branch.get().peel_to_commit()?;
|
||||
if head.id() != *revision.head() {
|
||||
if revision.head() != head.id() {
|
||||
anyhow::bail!(
|
||||
"branch '{patch_branch}' already exists (use `--force` to overwrite)"
|
||||
);
|
||||
|
|
@ -84,7 +83,7 @@ pub fn run(
|
|||
};
|
||||
|
||||
if opts.force {
|
||||
let mut builder = radicle::git::raw::build::CheckoutBuilder::new();
|
||||
let mut builder = git::raw::build::CheckoutBuilder::new();
|
||||
builder.force();
|
||||
working.checkout_tree(commit.as_object(), Some(&mut builder))?;
|
||||
} else {
|
||||
|
|
@ -125,7 +124,7 @@ fn find_patch_commit<'a>(
|
|||
stored: &Repository,
|
||||
working: &'a git::raw::Repository,
|
||||
) -> anyhow::Result<git::raw::Commit<'a>> {
|
||||
let head = *revision.head();
|
||||
let head = revision.head().into();
|
||||
|
||||
match working.find_commit(head) {
|
||||
Ok(commit) => Ok(commit),
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ use radicle::cob::patch::{PatchId, Revision, Verdict};
|
|||
use radicle::cob::{CodeLocation, CodeRange};
|
||||
use radicle::crypto;
|
||||
use radicle::git;
|
||||
use radicle::git::Oid;
|
||||
use radicle::node::device::Device;
|
||||
use radicle::prelude::*;
|
||||
use radicle::storage::git::{cob::DraftStore, Repository};
|
||||
use radicle_git_ext::Oid;
|
||||
use radicle_surf::diff::*;
|
||||
use radicle_term::{Element, VStack};
|
||||
|
||||
|
|
@ -196,25 +196,28 @@ impl ReviewItem {
|
|||
|
||||
fn paths(&self) -> (Option<(&Path, Oid)>, Option<(&Path, Oid)>) {
|
||||
match self {
|
||||
Self::FileAdded { path, new, .. } => (None, Some((path, new.oid))),
|
||||
Self::FileDeleted { path, old, .. } => (Some((path, old.oid)), None),
|
||||
Self::FileAdded { path, new, .. } => (None, Some((path, Oid::from(*new.oid)))),
|
||||
Self::FileDeleted { path, old, .. } => (Some((path, Oid::from(*old.oid))), None),
|
||||
Self::FileMoved { moved } => (
|
||||
Some((&moved.old_path, moved.old.oid)),
|
||||
Some((&moved.new_path, moved.new.oid)),
|
||||
Some((&moved.old_path, Oid::from(*moved.old.oid))),
|
||||
Some((&moved.new_path, Oid::from(*moved.new.oid))),
|
||||
),
|
||||
Self::FileCopied { copied } => (
|
||||
Some((&copied.old_path, copied.old.oid)),
|
||||
Some((&copied.new_path, copied.new.oid)),
|
||||
Some((&copied.old_path, Oid::from(*copied.old.oid))),
|
||||
Some((&copied.new_path, Oid::from(*copied.new.oid))),
|
||||
),
|
||||
Self::FileModified { path, old, new, .. } => (
|
||||
Some((path, Oid::from(*old.oid))),
|
||||
Some((path, Oid::from(*new.oid))),
|
||||
),
|
||||
Self::FileEofChanged { path, old, new, .. } => (
|
||||
Some((path, Oid::from(*old.oid))),
|
||||
Some((path, Oid::from(*new.oid))),
|
||||
),
|
||||
Self::FileModeChanged { path, old, new, .. } => (
|
||||
Some((path, Oid::from(*old.oid))),
|
||||
Some((path, Oid::from(*new.oid))),
|
||||
),
|
||||
Self::FileModified { path, old, new, .. } => {
|
||||
(Some((path, old.oid)), Some((path, new.oid)))
|
||||
}
|
||||
Self::FileEofChanged { path, old, new, .. } => {
|
||||
(Some((path, old.oid)), Some((path, new.oid)))
|
||||
}
|
||||
Self::FileModeChanged { path, old, new, .. } => {
|
||||
(Some((path, old.oid)), Some((path, new.oid)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -479,7 +482,7 @@ impl FileReviewBuilder {
|
|||
/// of changes introduced by a patch.
|
||||
pub struct Brain<'a> {
|
||||
/// Where the review draft is being stored.
|
||||
refname: git::Namespaced<'a>,
|
||||
refname: git::fmt::Namespaced<'a>,
|
||||
/// The commit pointed to by the ref.
|
||||
head: git::raw::Commit<'a>,
|
||||
/// The tree of accepted changes pointed to by the head commit.
|
||||
|
|
@ -565,7 +568,7 @@ impl<'a> Brain<'a> {
|
|||
}
|
||||
|
||||
/// Get the brain's refname given the patch and remote.
|
||||
fn refname(patch: &PatchId, remote: &NodeId) -> git::Namespaced<'a> {
|
||||
fn refname(patch: &PatchId, remote: &NodeId) -> git::fmt::Namespaced<'a> {
|
||||
git::refs::storage::draft::review(remote, patch)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use radicle::cob::patch;
|
||||
use radicle::git;
|
||||
use radicle::git::Oid;
|
||||
use radicle::prelude::*;
|
||||
use radicle::storage::git::Repository;
|
||||
|
||||
|
|
@ -9,7 +10,7 @@ use crate::terminal::patch::*;
|
|||
/// Run patch update.
|
||||
pub fn run(
|
||||
patch_id: patch::PatchId,
|
||||
base_id: Option<git::raw::Oid>,
|
||||
base_id: Option<Oid>,
|
||||
message: term::patch::Message,
|
||||
profile: &Profile,
|
||||
repository: &Repository,
|
||||
|
|
@ -27,22 +28,25 @@ pub fn run(
|
|||
let head_oid = branch_oid(&head_branch)?;
|
||||
let base_oid = match base_id {
|
||||
Some(oid) => oid,
|
||||
None => repository.backend.merge_base(*target_oid, *head_oid)?,
|
||||
None => repository
|
||||
.backend
|
||||
.merge_base(target_oid.into(), head_oid.into())?
|
||||
.into(),
|
||||
};
|
||||
|
||||
// N.b. we don't update if both the head and base are the same as
|
||||
// any previous revision
|
||||
if patch
|
||||
.revisions()
|
||||
.any(|(_, revision)| revision.head() == head_oid && **revision.base() == base_oid)
|
||||
.any(|(_, revision)| revision.head() == head_oid && *revision.base() == base_oid)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (_, revision) = patch.latest();
|
||||
let message = term::patch::get_update_message(message, workdir, revision, &head_oid)?;
|
||||
let message = term::patch::get_update_message(message, workdir, revision, &head_oid.into())?;
|
||||
let signer = term::signer(profile)?;
|
||||
let revision = patch.update(message, base_oid, *head_oid, &signer)?;
|
||||
let revision = patch.update(message, base_oid, head_oid, &signer)?;
|
||||
|
||||
term::print(revision);
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use std::ffi::OsString;
|
|||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use radicle::git::RefString;
|
||||
use radicle::git::fmt::RefString;
|
||||
use radicle::prelude::NodeId;
|
||||
use radicle::storage::ReadStorage;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use radicle::git;
|
||||
use radicle::git::RefString;
|
||||
use radicle::git::fmt::RefString;
|
||||
use radicle::prelude::*;
|
||||
use radicle::Profile;
|
||||
use radicle_crypto::PublicKey;
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ pub fn run(_args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
let remote = remote?;
|
||||
let sigrefs = repo.reference_oid(&remote, &git::refs::storage::SIGREFS_BRANCH)?;
|
||||
let mut walk = repo.raw().revwalk()?;
|
||||
walk.push(*sigrefs)?;
|
||||
walk.push(sigrefs.into())?;
|
||||
|
||||
stats.local.pushes += walk.count();
|
||||
stats.local.forks += 1;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ Options
|
|||
|
||||
pub struct Options {
|
||||
rid: Option<RepoId>,
|
||||
refstr: git::RefString,
|
||||
refstr: git::fmt::RefString,
|
||||
target: Option<git::Oid>,
|
||||
nid: Option<NodeId>,
|
||||
interval: time::Duration,
|
||||
|
|
@ -56,7 +56,7 @@ impl Args for Options {
|
|||
let mut rid = None;
|
||||
let mut nid: Option<NodeId> = None;
|
||||
let mut target: Option<git::Oid> = None;
|
||||
let mut refstr: Option<git::RefString> = None;
|
||||
let mut refstr: Option<git::fmt::RefString> = None;
|
||||
let mut interval: Option<time::Duration> = None;
|
||||
let mut timeout: time::Duration = time::Duration::MAX;
|
||||
|
||||
|
|
@ -166,7 +166,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
fn reference<R: ReadRepository>(
|
||||
repo: &R,
|
||||
nid: &NodeId,
|
||||
qual: &git::Qualified,
|
||||
qual: &git::fmt::Qualified,
|
||||
) -> Result<Option<git::Oid>, git::raw::Error> {
|
||||
match repo.reference_oid(nid, qual) {
|
||||
Ok(oid) => Ok(Some(oid)),
|
||||
|
|
|
|||
|
|
@ -20,14 +20,15 @@ use thiserror::Error;
|
|||
|
||||
use radicle::crypto::ssh;
|
||||
use radicle::git;
|
||||
use radicle::git::raw as git2;
|
||||
use radicle::git::{Version, VERSION_REQUIRED};
|
||||
use radicle::prelude::{NodeId, RepoId};
|
||||
use radicle::storage::git::transport;
|
||||
|
||||
pub use radicle::git::Oid;
|
||||
|
||||
pub use radicle::git::raw::{
|
||||
build::CheckoutBuilder, AnnotatedCommit, Commit, Direction, ErrorCode, MergeAnalysis,
|
||||
MergeOptions, Oid, Reference, Repository, Signature,
|
||||
build::CheckoutBuilder, AnnotatedCommit, Commit, Direction, ErrorCode, ErrorExt as _,
|
||||
MergeAnalysis, MergeOptions, Reference, Repository, Signature,
|
||||
};
|
||||
|
||||
pub const CONFIG_COMMIT_GPG_SIGN: &str = "commit.gpgsign";
|
||||
|
|
@ -46,10 +47,10 @@ impl Rev {
|
|||
&self.0
|
||||
}
|
||||
|
||||
/// Resolve the revision to an [`From<git2::Oid>`].
|
||||
pub fn resolve<T>(&self, repo: &git2::Repository) -> Result<T, git2::Error>
|
||||
/// Resolve the revision to an [`From<git::raw::Oid>`].
|
||||
pub fn resolve<T>(&self, repo: &Repository) -> Result<T, git::raw::Error>
|
||||
where
|
||||
T: From<git2::Oid>,
|
||||
T: From<git::raw::Oid>,
|
||||
{
|
||||
let object = repo.revparse_single(self.as_str())?;
|
||||
Ok(object.id().into())
|
||||
|
|
@ -84,13 +85,13 @@ pub struct Remote<'a> {
|
|||
pub url: radicle::git::Url,
|
||||
pub pushurl: Option<radicle::git::Url>,
|
||||
|
||||
inner: git2::Remote<'a>,
|
||||
inner: git::raw::Remote<'a>,
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<git2::Remote<'a>> for Remote<'a> {
|
||||
impl<'a> TryFrom<git::raw::Remote<'a>> for Remote<'a> {
|
||||
type Error = RemoteError;
|
||||
|
||||
fn try_from(value: git2::Remote<'a>) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: git::raw::Remote<'a>) -> Result<Self, Self::Error> {
|
||||
let url = value.url().map_or(Err(RemoteError::MissingUrl), |url| {
|
||||
Ok(radicle::git::Url::from_str(url)?)
|
||||
})?;
|
||||
|
|
@ -110,7 +111,7 @@ impl<'a> TryFrom<git2::Remote<'a>> for Remote<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Deref for Remote<'a> {
|
||||
type Target = git2::Remote<'a>;
|
||||
type Target = git::raw::Remote<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
|
|
@ -250,7 +251,7 @@ pub fn is_signing_configured(repo: &Path) -> Result<bool, anyhow::Error> {
|
|||
}
|
||||
|
||||
/// Return the list of radicle remotes for the given repository.
|
||||
pub fn rad_remotes(repo: &git2::Repository) -> anyhow::Result<Vec<Remote>> {
|
||||
pub fn rad_remotes(repo: &Repository) -> anyhow::Result<Vec<Remote>> {
|
||||
let remotes: Vec<_> = repo
|
||||
.remotes()?
|
||||
.iter()
|
||||
|
|
@ -263,16 +264,16 @@ pub fn rad_remotes(repo: &git2::Repository) -> anyhow::Result<Vec<Remote>> {
|
|||
}
|
||||
|
||||
/// Check if the git remote is configured for the `Repository`.
|
||||
pub fn is_remote(repo: &git2::Repository, alias: &str) -> anyhow::Result<bool> {
|
||||
pub fn is_remote(repo: &Repository, alias: &str) -> anyhow::Result<bool> {
|
||||
match repo.find_remote(alias) {
|
||||
Ok(_) => Ok(true),
|
||||
Err(err) if err.code() == git2::ErrorCode::NotFound => Ok(false),
|
||||
Err(err) if err.is_not_found() => Ok(false),
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the repository's "rad" remote.
|
||||
pub fn rad_remote(repo: &Repository) -> anyhow::Result<(git2::Remote, RepoId)> {
|
||||
pub fn rad_remote(repo: &Repository) -> anyhow::Result<(git::raw::Remote, RepoId)> {
|
||||
match radicle::rad::remote(repo) {
|
||||
Ok((remote, id)) => Ok((remote, id)),
|
||||
Err(radicle::rad::RemoteError::NotFound(_)) => Err(anyhow!(
|
||||
|
|
@ -352,12 +353,12 @@ pub fn parse_remote(refspec: &str) -> Option<(NodeId, &str)> {
|
|||
}
|
||||
|
||||
pub fn add_tag(
|
||||
repo: &git2::Repository,
|
||||
repo: &Repository,
|
||||
message: &str,
|
||||
patch_tag_name: &str,
|
||||
) -> anyhow::Result<git2::Oid> {
|
||||
) -> anyhow::Result<git::raw::Oid> {
|
||||
let head = repo.head()?;
|
||||
let commit = head.peel(git2::ObjectType::Commit).unwrap();
|
||||
let commit = head.peel(git::raw::ObjectType::Commit).unwrap();
|
||||
let oid = repo.tag(patch_tag_name, &commit, &repo.signature()?, message, false)?;
|
||||
|
||||
Ok(oid)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::fs;
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use radicle::git;
|
||||
use radicle_git_ext::Oid;
|
||||
use radicle::git::Oid;
|
||||
use radicle_surf::diff;
|
||||
use radicle_surf::diff::{Added, Copied, Deleted, FileStats, Hunks, Modified, Moved};
|
||||
use radicle_surf::diff::{Diff, DiffContent, FileDiff, Hunk, Modification};
|
||||
|
|
@ -33,7 +33,7 @@ pub trait Repo {
|
|||
|
||||
impl Repo for git::raw::Repository {
|
||||
fn blob(&self, oid: git::Oid) -> Result<Blob, git::raw::Error> {
|
||||
let blob = self.find_blob(*oid)?;
|
||||
let blob = self.find_blob(oid.into())?;
|
||||
|
||||
if blob.is_binary() {
|
||||
Ok(Blob::Binary)
|
||||
|
|
@ -338,7 +338,7 @@ impl ToPretty for Added {
|
|||
repo: &R,
|
||||
) -> Self::Output {
|
||||
let old = None;
|
||||
let new = Some((self.path.as_path(), self.new.oid));
|
||||
let new = Some((self.path.as_path(), Oid::from(*self.new.oid)));
|
||||
|
||||
pretty_modification(header, &self.diff, old, new, repo, hi)
|
||||
}
|
||||
|
|
@ -354,7 +354,7 @@ impl ToPretty for Deleted {
|
|||
header: &Self::Context,
|
||||
repo: &R,
|
||||
) -> Self::Output {
|
||||
let old = Some((self.path.as_path(), self.old.oid));
|
||||
let old = Some((self.path.as_path(), Oid::from(*self.old.oid)));
|
||||
let new = None;
|
||||
|
||||
pretty_modification(header, &self.diff, old, new, repo, hi)
|
||||
|
|
@ -371,8 +371,8 @@ impl ToPretty for Modified {
|
|||
header: &Self::Context,
|
||||
repo: &R,
|
||||
) -> Self::Output {
|
||||
let old = Some((self.path.as_path(), self.old.oid));
|
||||
let new = Some((self.path.as_path(), self.new.oid));
|
||||
let old = Some((self.path.as_path(), Oid::from(*self.old.oid)));
|
||||
let new = Some((self.path.as_path(), Oid::from(*self.new.oid)));
|
||||
|
||||
pretty_modification(header, &self.diff, old, new, repo, hi)
|
||||
}
|
||||
|
|
@ -595,8 +595,8 @@ mod test {
|
|||
use term::Element;
|
||||
|
||||
use super::*;
|
||||
use radicle::git::raw::RepositoryOpenFlags;
|
||||
use radicle::git::raw::{Oid, Repository};
|
||||
use git::raw::RepositoryOpenFlags;
|
||||
use git::raw::{Oid, Repository};
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ use radicle_surf::diff::FileStats;
|
|||
use thiserror::Error;
|
||||
|
||||
use radicle::git;
|
||||
use radicle::git::raw::Oid;
|
||||
use radicle_surf::diff;
|
||||
use radicle_surf::diff::{Diff, DiffContent, DiffFile, FileDiff, Hunk, Hunks, Line, Modification};
|
||||
|
||||
|
|
@ -307,8 +306,8 @@ impl Encode for FileHeader {
|
|||
if old.mode == new.mode {
|
||||
w.meta(format!(
|
||||
"index {}..{} {:o}",
|
||||
term::format::oid(old.oid),
|
||||
term::format::oid(new.oid),
|
||||
term::format::oid(*old.oid),
|
||||
term::format::oid(*new.oid),
|
||||
u32::from(old.mode.clone()),
|
||||
))?;
|
||||
} else {
|
||||
|
|
@ -316,8 +315,8 @@ impl Encode for FileHeader {
|
|||
w.meta(format!("new mode {:o}", u32::from(new.mode.clone())))?;
|
||||
w.meta(format!(
|
||||
"index {}..{}",
|
||||
term::format::oid(old.oid),
|
||||
term::format::oid(new.oid)
|
||||
term::format::oid(*old.oid),
|
||||
term::format::oid(*new.oid)
|
||||
))?;
|
||||
}
|
||||
|
||||
|
|
@ -334,8 +333,8 @@ impl Encode for FileHeader {
|
|||
w.meta(format!("new file mode {:o}", u32::from(new.mode.clone())))?;
|
||||
w.meta(format!(
|
||||
"index {}..{}",
|
||||
term::format::oid(Oid::zero()),
|
||||
term::format::oid(new.oid),
|
||||
term::format::oid(git::Oid::sha1_zero()),
|
||||
term::format::oid(*new.oid),
|
||||
))?;
|
||||
|
||||
w.meta("--- /dev/null")?;
|
||||
|
|
@ -355,8 +354,8 @@ impl Encode for FileHeader {
|
|||
))?;
|
||||
w.meta(format!(
|
||||
"index {}..{}",
|
||||
term::format::oid(old.oid),
|
||||
term::format::oid(Oid::zero())
|
||||
term::format::oid(*old.oid),
|
||||
term::format::oid(git::Oid::sha1_zero())
|
||||
))?;
|
||||
|
||||
w.meta(format!("--- a/{}", path.display()))?;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use radicle::prelude::*;
|
||||
|
||||
use crate::git;
|
||||
use radicle::git::RefStr;
|
||||
use radicle::git::fmt::RefStr;
|
||||
use radicle::node::NodeId;
|
||||
|
||||
/// Setup a repository remote and tracking branch.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use anyhow::anyhow;
|
|||
|
||||
use radicle::cob::{self, issue, patch};
|
||||
use radicle::crypto;
|
||||
use radicle::git::{Oid, RefString};
|
||||
use radicle::git::{fmt::RefString, Oid};
|
||||
use radicle::node::{Address, Alias};
|
||||
use radicle::prelude::{Did, NodeId, RepoId};
|
||||
|
||||
|
|
|
|||
|
|
@ -188,8 +188,8 @@ fn message_from_commits(name: &str, commits: Vec<git::raw::Commit>) -> Result<St
|
|||
/// Return commits between the merge base and a head.
|
||||
pub fn patch_commits<'a>(
|
||||
repo: &'a git::raw::Repository,
|
||||
base: &git::Oid,
|
||||
head: &git::Oid,
|
||||
base: &git::raw::Oid,
|
||||
head: &git::raw::Oid,
|
||||
) -> Result<Vec<git::raw::Commit<'a>>, git::raw::Error> {
|
||||
let mut commits = Vec::new();
|
||||
let mut revwalk = repo.revwalk()?;
|
||||
|
|
@ -205,8 +205,8 @@ pub fn patch_commits<'a>(
|
|||
/// The message shown in the editor when creating a `Patch`.
|
||||
fn create_display_message(
|
||||
repo: &git::raw::Repository,
|
||||
base: &git::Oid,
|
||||
head: &git::Oid,
|
||||
base: &git::raw::Oid,
|
||||
head: &git::raw::Oid,
|
||||
) -> Result<String, Error> {
|
||||
let commits = patch_commits(repo, base, head)?;
|
||||
if commits.is_empty() {
|
||||
|
|
@ -226,8 +226,8 @@ fn create_display_message(
|
|||
pub fn get_create_message(
|
||||
message: term::patch::Message,
|
||||
repo: &git::raw::Repository,
|
||||
base: &git::Oid,
|
||||
head: &git::Oid,
|
||||
base: &git::raw::Oid,
|
||||
head: &git::raw::Oid,
|
||||
) -> Result<(Title, String), Error> {
|
||||
let display_msg = create_display_message(repo, base, head)?;
|
||||
let message = message.get(&display_msg)?;
|
||||
|
|
@ -279,10 +279,10 @@ pub fn get_edit_message(
|
|||
/// The message shown in the editor when updating a `Patch`.
|
||||
fn update_display_message(
|
||||
repo: &git::raw::Repository,
|
||||
last_rev_head: &git::Oid,
|
||||
head: &git::Oid,
|
||||
last_rev_head: &git::raw::Oid,
|
||||
head: &git::raw::Oid,
|
||||
) -> Result<String, Error> {
|
||||
if !repo.graph_descendant_of(**head, **last_rev_head)? {
|
||||
if !repo.graph_descendant_of(*head, *last_rev_head)? {
|
||||
return Ok(REVISION_MSG.trim_start().to_string());
|
||||
}
|
||||
|
||||
|
|
@ -302,9 +302,9 @@ pub fn get_update_message(
|
|||
message: term::patch::Message,
|
||||
repo: &git::raw::Repository,
|
||||
latest: &patch::Revision,
|
||||
head: &git::Oid,
|
||||
head: &git::raw::Oid,
|
||||
) -> Result<String, Error> {
|
||||
let display_msg = update_display_message(repo, &latest.head(), head)?;
|
||||
let display_msg = update_display_message(repo, &latest.head().into(), head)?;
|
||||
let message = message.get(&display_msg)?;
|
||||
let message = message.trim();
|
||||
|
||||
|
|
@ -366,11 +366,8 @@ pub fn show(
|
|||
} else {
|
||||
vec![]
|
||||
};
|
||||
let ahead_behind = common::ahead_behind(
|
||||
stored.raw(),
|
||||
*revision.head(),
|
||||
*patch.target().head(stored)?,
|
||||
)?;
|
||||
let ahead_behind =
|
||||
common::ahead_behind(stored.raw(), revision.head(), patch.target().head(stored)?)?;
|
||||
let author = patch.author();
|
||||
let author = term::format::Author::new(author.id(), profile, verbose);
|
||||
let labels = patch.labels().map(|l| l.to_string()).collect::<Vec<_>>();
|
||||
|
|
@ -468,7 +465,7 @@ fn patch_commit_lines(
|
|||
let (from, to) = patch.range()?;
|
||||
let mut lines = Vec::new();
|
||||
|
||||
for commit in patch_commits(stored.raw(), &from, &to)? {
|
||||
for commit in patch_commits(stored.raw(), &from.into(), &to.into())? {
|
||||
lines.push(term::Line::spaced([
|
||||
term::label(term::format::secondary::<String>(
|
||||
term::format::oid(commit.id()).into(),
|
||||
|
|
@ -484,37 +481,36 @@ fn patch_commit_lines(
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use radicle::git::refname;
|
||||
use radicle::git::fmt::refname;
|
||||
use radicle::test::fixtures;
|
||||
use std::path;
|
||||
|
||||
fn commit(
|
||||
repo: &git::raw::Repository,
|
||||
branch: &git::RefStr,
|
||||
parent: &git::Oid,
|
||||
branch: &git::fmt::RefStr,
|
||||
parent: &git::raw::Oid,
|
||||
msg: &str,
|
||||
) -> git::Oid {
|
||||
) -> git::raw::Oid {
|
||||
let sig = git::raw::Signature::new(
|
||||
"anonymous",
|
||||
"anonymous@radicle.example.com",
|
||||
&git::raw::Time::new(0, 0),
|
||||
)
|
||||
.unwrap();
|
||||
let head = repo.find_commit(**parent).unwrap();
|
||||
let head = repo.find_commit(*parent).unwrap();
|
||||
let tree =
|
||||
git::write_tree(path::Path::new("README"), "Hello World!\n".as_bytes(), repo).unwrap();
|
||||
|
||||
let branch = git::refs::branch(branch);
|
||||
let commit = git::commit(repo, &head, &branch, msg, &sig, &tree).unwrap();
|
||||
|
||||
commit.id().into()
|
||||
commit.id()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_display_message() {
|
||||
let tmpdir = tempfile::tempdir().unwrap();
|
||||
let (repo, commit_0) = fixtures::repository(&tmpdir);
|
||||
let commit_0 = commit_0.into();
|
||||
let commit_1 = commit(
|
||||
&repo,
|
||||
&refname!("feature"),
|
||||
|
|
@ -625,7 +621,6 @@ mod test {
|
|||
fn test_update_display_message() {
|
||||
let tmpdir = tempfile::tempdir().unwrap();
|
||||
let (repo, commit_0) = fixtures::repository(&tmpdir);
|
||||
let commit_0 = commit_0.into();
|
||||
|
||||
let commit_1 = commit(&repo, &refname!("feature"), &commit_0, "commit 1\n");
|
||||
let commit_2 = commit(&repo, &refname!("feature"), &commit_1, "commit 2\n");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use anyhow::anyhow;
|
||||
|
||||
use radicle::git;
|
||||
use radicle::git::raw::Oid;
|
||||
use radicle::git::Oid;
|
||||
use radicle::prelude::*;
|
||||
use radicle::storage::git::Repository;
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ use crate::terminal as term;
|
|||
|
||||
/// Give the oid of the branch or an appropriate error.
|
||||
#[inline]
|
||||
pub fn branch_oid(branch: &git::raw::Branch) -> anyhow::Result<git::Oid> {
|
||||
pub fn branch_oid(branch: &git::raw::Branch) -> anyhow::Result<Oid> {
|
||||
let oid = branch
|
||||
.get()
|
||||
.target()
|
||||
|
|
@ -18,7 +18,7 @@ pub fn branch_oid(branch: &git::raw::Branch) -> anyhow::Result<git::Oid> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn get_branch(git_ref: git::Qualified) -> git::RefString {
|
||||
fn get_branch(git_ref: git::fmt::Qualified) -> git::fmt::RefString {
|
||||
let (_, _, head, tail) = git_ref.non_empty_components();
|
||||
std::iter::once(head).chain(tail).collect()
|
||||
}
|
||||
|
|
@ -28,16 +28,18 @@ fn get_branch(git_ref: git::Qualified) -> git::RefString {
|
|||
pub fn get_merge_target(
|
||||
storage: &Repository,
|
||||
head_branch: &git::raw::Branch,
|
||||
) -> anyhow::Result<(git::RefString, git::Oid)> {
|
||||
) -> anyhow::Result<(git::fmt::RefString, git::Oid)> {
|
||||
let (qualified_ref, target_oid) = storage.canonical_head()?;
|
||||
let head_oid = branch_oid(head_branch)?;
|
||||
let merge_base = storage.raw().merge_base(*head_oid, *target_oid)?;
|
||||
let merge_base = storage
|
||||
.raw()
|
||||
.merge_base(head_oid.into(), target_oid.into())?;
|
||||
|
||||
if head_oid == merge_base.into() {
|
||||
if head_oid == merge_base {
|
||||
anyhow::bail!("commits are already included in the target branch; nothing to do");
|
||||
}
|
||||
|
||||
Ok((get_branch(qualified_ref), (*target_oid).into()))
|
||||
Ok((get_branch(qualified_ref), (target_oid)))
|
||||
}
|
||||
|
||||
/// Get the diff stats between two commits.
|
||||
|
|
@ -47,8 +49,8 @@ pub fn diff_stats(
|
|||
old: &Oid,
|
||||
new: &Oid,
|
||||
) -> Result<git::raw::DiffStats, git::raw::Error> {
|
||||
let old = repo.find_commit(*old)?;
|
||||
let new = repo.find_commit(*new)?;
|
||||
let old = repo.find_commit(old.into())?;
|
||||
let new = repo.find_commit(new.into())?;
|
||||
let old_tree = old.tree()?;
|
||||
let new_tree = new.tree()?;
|
||||
let mut diff = repo.diff_tree_to_tree(Some(&old_tree), Some(&new_tree), None)?;
|
||||
|
|
@ -64,7 +66,7 @@ pub fn ahead_behind(
|
|||
revision_oid: Oid,
|
||||
head_oid: Oid,
|
||||
) -> anyhow::Result<term::Line> {
|
||||
let (a, b) = repo.graph_ahead_behind(revision_oid, head_oid)?;
|
||||
let (a, b) = repo.graph_ahead_behind(revision_oid.into(), head_oid.into())?;
|
||||
if a == 0 && b == 0 {
|
||||
return Ok(term::Line::new(term::format::dim("up to date")));
|
||||
}
|
||||
|
|
@ -88,7 +90,7 @@ pub fn branches(target: &Oid, repo: &git::raw::Repository) -> anyhow::Result<Vec
|
|||
continue;
|
||||
}
|
||||
if let (Some(oid), Some(name)) = (&r.target(), &r.shorthand()) {
|
||||
if oid == target {
|
||||
if target == oid {
|
||||
branches.push(name.to_string());
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use localtime::LocalTime;
|
|||
use radicle::cob::cache::COBS_DB_FILE;
|
||||
use radicle::crypto::ssh::{keystore::MemorySigner, Keystore};
|
||||
use radicle::crypto::{KeyPair, Seed};
|
||||
use radicle::git;
|
||||
use radicle::node::policy::store as policy;
|
||||
use radicle::node::{self, UserAgent};
|
||||
use radicle::node::{Alias, Config, POLICIES_DB_FILE};
|
||||
|
|
@ -233,7 +234,7 @@ impl Environment {
|
|||
pub fn repository(
|
||||
&self,
|
||||
has_alias: &impl HasAlias,
|
||||
) -> (radicle_cli::git::Repository, radicle_cli::git::Oid) {
|
||||
) -> (radicle_cli::git::Repository, git::raw::Oid) {
|
||||
radicle::test::fixtures::repository(self.work(has_alias).as_path())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,15 +17,18 @@ rust-version.workspace = true
|
|||
default = []
|
||||
# Only used for testing. Ensures that commit ids are stable.
|
||||
stable-commit-ids = []
|
||||
test = []
|
||||
|
||||
[dependencies]
|
||||
fastrand = { workspace = true }
|
||||
git2 = { workspace = true, features = ["vendored-libgit2"] }
|
||||
git-ref-format-core = { workspace = true }
|
||||
git2 = { workspace = true, optional = true, features = ["vendored-libgit2"] }
|
||||
log = { workspace = true }
|
||||
nonempty = { workspace = true, features = ["serialize"] }
|
||||
radicle-crypto = { workspace = true, features = ["ssh"] }
|
||||
radicle-dag = { workspace = true }
|
||||
radicle-git-ext = { workspace = true, features = ["serde"] }
|
||||
radicle-git-metadata = { workspace = true }
|
||||
radicle-oid = { workspace = true, features = ["git2", "serde", "std"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
signature = { workspace = true }
|
||||
|
|
@ -36,4 +39,6 @@ fastrand = { workspace = true }
|
|||
qcheck = { workspace = true }
|
||||
qcheck-macros = { workspace = true }
|
||||
radicle-crypto = { workspace = true, features = ["test", "git-ref-format-core"] }
|
||||
radicle-git-ref-format = { workspace = true, features = ["macro"] }
|
||||
radicle-oid = { workspace = true, features = ["qcheck"] }
|
||||
tempfile = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright © 2022 The Radicle Link Contributors
|
||||
|
||||
#[cfg(feature = "git2")]
|
||||
pub mod git;
|
||||
|
||||
#[cfg(feature = "stable-commit-ids")]
|
||||
pub mod stable;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
// Copyright © 2022 The Radicle Team
|
||||
|
||||
pub mod change;
|
||||
mod commit;
|
||||
|
||||
#[cfg(feature = "stable-commit-ids")]
|
||||
pub mod stable;
|
||||
pub mod change;
|
||||
|
||||
/// Environment variable to set to overwrite the commit date for both the author and the committer.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ use std::convert::TryFrom;
|
|||
use std::path::PathBuf;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use git_ext::author::Author;
|
||||
use git_ext::commit::{headers::Headers, Commit};
|
||||
use git_ext::Oid;
|
||||
use metadata::author::Author;
|
||||
use metadata::commit::headers::Headers;
|
||||
use metadata::commit::trailers::OwnedTrailer;
|
||||
use nonempty::NonEmpty;
|
||||
use radicle_git_ext::commit::trailers::OwnedTrailer;
|
||||
use oid::Oid;
|
||||
|
||||
use crate::change::store::Version;
|
||||
use crate::signatures;
|
||||
|
|
@ -21,6 +21,8 @@ use crate::{
|
|||
trailers, Embed,
|
||||
};
|
||||
|
||||
use super::commit::Commit;
|
||||
|
||||
/// Name of the COB manifest file.
|
||||
pub const MANIFEST_BLOB_NAME: &str = "manifest";
|
||||
/// Path under which COB embeds are kept.
|
||||
|
|
@ -30,8 +32,7 @@ pub mod error {
|
|||
use std::str::Utf8Error;
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
use git_ext::commit;
|
||||
use git_ext::Oid;
|
||||
use oid::Oid;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::signatures::error::Signatures;
|
||||
|
|
@ -39,7 +40,7 @@ pub mod error {
|
|||
#[derive(Debug, Error)]
|
||||
pub enum Create {
|
||||
#[error(transparent)]
|
||||
WriteCommit(#[from] commit::error::Write),
|
||||
WriteCommit(#[from] super::super::commit::error::Write),
|
||||
#[error(transparent)]
|
||||
FromUtf8(#[from] FromUtf8Error),
|
||||
#[error(transparent)]
|
||||
|
|
@ -55,7 +56,7 @@ pub mod error {
|
|||
#[derive(Debug, Error)]
|
||||
pub enum Load {
|
||||
#[error(transparent)]
|
||||
Read(#[from] commit::error::Read),
|
||||
Read(#[from] super::super::commit::error::Read),
|
||||
#[error(transparent)]
|
||||
Signatures(#[from] Signatures),
|
||||
#[error(transparent)]
|
||||
|
|
@ -123,7 +124,7 @@ impl change::Storage for git2::Repository {
|
|||
|
||||
let (id, timestamp) = write_commit(
|
||||
self,
|
||||
resource.map(|o| *o),
|
||||
resource.map(|o| o.into()),
|
||||
// Commit to tips, extra parents and resource.
|
||||
tips.iter()
|
||||
.cloned()
|
||||
|
|
@ -134,7 +135,7 @@ impl change::Storage for git2::Repository {
|
|||
signature.clone(),
|
||||
related
|
||||
.iter()
|
||||
.map(|p| trailers::CommitTrailer::Related(**p).into()),
|
||||
.map(|p| trailers::CommitTrailer::Related(*p).into()),
|
||||
tree,
|
||||
)?;
|
||||
|
||||
|
|
@ -153,40 +154,34 @@ impl change::Storage for git2::Repository {
|
|||
|
||||
fn parents_of(&self, id: &Oid) -> Result<Vec<Oid>, Self::LoadError> {
|
||||
Ok(self
|
||||
.find_commit(**id)?
|
||||
.find_commit(id.into())?
|
||||
.parent_ids()
|
||||
.map(Oid::from)
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
fn manifest_of(&self, id: &Oid) -> Result<crate::Manifest, Self::LoadError> {
|
||||
let commit = self.find_commit(**id)?;
|
||||
let commit = self.find_commit(id.into())?;
|
||||
let tree = commit.tree()?;
|
||||
load_manifest(self, &tree)
|
||||
}
|
||||
|
||||
fn load(&self, id: Self::ObjectId) -> Result<Entry, Self::LoadError> {
|
||||
let commit = Commit::read(self, id.into())?;
|
||||
let timestamp = git2::Time::from(commit.committer().time).seconds() as u64;
|
||||
let commit = super::commit::Commit::read(self, id.into())?;
|
||||
let timestamp = commit.committer().time.seconds() as u64;
|
||||
let trailers = parse_trailers(commit.trailers())?;
|
||||
let (resources, related): (Vec<_>, Vec<_>) = trailers.iter().partition(|t| match t {
|
||||
CommitTrailer::Resource(_) => true,
|
||||
CommitTrailer::Related(_) => false,
|
||||
});
|
||||
let mut resources = resources
|
||||
.into_iter()
|
||||
.map(|r| r.oid().into())
|
||||
.collect::<Vec<_>>();
|
||||
let related = related
|
||||
.into_iter()
|
||||
.map(|r| r.oid().into())
|
||||
.collect::<Vec<_>>();
|
||||
let mut resources = resources.into_iter().map(|r| r.oid()).collect::<Vec<_>>();
|
||||
let related = related.into_iter().map(|r| r.oid()).collect::<Vec<_>>();
|
||||
let parents = commit
|
||||
.parents()
|
||||
.map(Oid::from)
|
||||
.filter(|p| !resources.contains(p) && !related.contains(p))
|
||||
.collect();
|
||||
let mut signatures = Signatures::try_from(&commit)?
|
||||
let mut signatures = Signatures::try_from(&*commit)?
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
let Some((key, sig)) = signatures.pop() else {
|
||||
|
|
@ -285,7 +280,7 @@ fn write_commit(
|
|||
) -> Result<(Oid, Timestamp), error::Create> {
|
||||
let trailers: Vec<OwnedTrailer> = trailers
|
||||
.into_iter()
|
||||
.chain(resource.map(|r| trailers::CommitTrailer::Resource(r).into()))
|
||||
.chain(resource.map(|r| trailers::CommitTrailer::Resource(r.into()).into()))
|
||||
.collect();
|
||||
let author = repo.signature()?;
|
||||
#[allow(unused_variables)]
|
||||
|
|
@ -299,29 +294,37 @@ fn write_commit(
|
|||
.map_err(signatures::error::Signatures::from)?
|
||||
.as_str(),
|
||||
);
|
||||
let author = Author::try_from(&author)?;
|
||||
|
||||
let author = Author {
|
||||
name: String::from_utf8(author.name_bytes().to_vec())?,
|
||||
email: String::from_utf8(author.email_bytes().to_vec())?,
|
||||
time: {
|
||||
let when = author.when();
|
||||
metadata::author::Time::new(when.seconds(), when.offset_minutes())
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(feature = "stable-commit-ids")]
|
||||
// Ensures the commit id doesn't change on every run.
|
||||
let (author, timestamp) = {
|
||||
let stable = crate::git::stable::read_timestamp();
|
||||
let stable = crate::stable::read_timestamp();
|
||||
(
|
||||
Author {
|
||||
time: git_ext::author::Time::new(stable, 0),
|
||||
time: metadata::author::Time::new(stable, 0),
|
||||
..author
|
||||
},
|
||||
stable,
|
||||
)
|
||||
};
|
||||
let (author, timestamp) = if let Ok(s) = std::env::var(crate::git::GIT_COMMITTER_DATE) {
|
||||
let (author, timestamp) = if let Ok(s) = std::env::var(super::GIT_COMMITTER_DATE) {
|
||||
let Ok(timestamp) = s.trim().parse::<i64>() else {
|
||||
panic!(
|
||||
"Invalid timestamp value {s:?} for `{}`",
|
||||
crate::git::GIT_COMMITTER_DATE
|
||||
super::GIT_COMMITTER_DATE
|
||||
);
|
||||
};
|
||||
let author = Author {
|
||||
time: git_ext::author::Time::new(timestamp, 0),
|
||||
time: metadata::author::Time::new(timestamp, 0),
|
||||
..author
|
||||
};
|
||||
(author, timestamp)
|
||||
|
|
@ -380,7 +383,7 @@ fn write_manifest(
|
|||
let oid = embed.content;
|
||||
let path = PathBuf::from(embed.name);
|
||||
|
||||
embeds_tree.insert(path, *oid, git2::FileMode::Blob.into())?;
|
||||
embeds_tree.insert(path, oid.into(), git2::FileMode::Blob.into())?;
|
||||
}
|
||||
let oid = embeds_tree.write()?;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,180 @@
|
|||
mod trailers;
|
||||
|
||||
use std::fmt;
|
||||
use std::str::{self, FromStr};
|
||||
|
||||
use git2::{ObjectType, Oid};
|
||||
|
||||
use metadata::author::Author;
|
||||
use metadata::commit::headers::Headers;
|
||||
use metadata::commit::trailers::OwnedTrailer;
|
||||
use metadata::commit::CommitData;
|
||||
|
||||
use trailers::Trailers;
|
||||
|
||||
#[repr(transparent)]
|
||||
pub(super) struct Commit(metadata::commit::CommitData<Oid, Oid>);
|
||||
|
||||
impl Commit {
|
||||
pub fn new<P, I, T>(
|
||||
tree: git2::Oid,
|
||||
parents: P,
|
||||
author: Author,
|
||||
committer: Author,
|
||||
headers: Headers,
|
||||
message: String,
|
||||
trailers: I,
|
||||
) -> Self
|
||||
where
|
||||
P: IntoIterator<Item = Oid>,
|
||||
I: IntoIterator<Item = T>,
|
||||
OwnedTrailer: From<T>,
|
||||
{
|
||||
Self(CommitData::new(
|
||||
tree, parents, author, committer, headers, message, trailers,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Commit {
|
||||
/// Read the [`Commit`] from the `repo` that is expected to be found at
|
||||
/// `oid`.
|
||||
pub fn read(repo: &git2::Repository, oid: Oid) -> Result<Self, error::Read> {
|
||||
let odb = repo.odb()?;
|
||||
let object = odb.read(oid)?;
|
||||
Ok(Commit::try_from(object.data())?)
|
||||
}
|
||||
|
||||
/// Write the given [`Commit`] to the `repo`. The resulting `Oid`
|
||||
/// is the identifier for this commit.
|
||||
pub fn write(&self, repo: &git2::Repository) -> Result<Oid, error::Write> {
|
||||
let odb = repo.odb().map_err(error::Write::Odb)?;
|
||||
self.verify_for_write(&odb)?;
|
||||
Ok(odb.write(ObjectType::Commit, self.to_string().as_bytes())?)
|
||||
}
|
||||
|
||||
fn verify_for_write(&self, odb: &git2::Odb) -> Result<(), error::Write> {
|
||||
for parent in self.0.parents() {
|
||||
verify_object(odb, &parent, ObjectType::Commit)?;
|
||||
}
|
||||
verify_object(odb, self.0.tree(), ObjectType::Tree)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_object(odb: &git2::Odb, oid: &Oid, expected: ObjectType) -> Result<(), error::Write> {
|
||||
use git2::{Error, ErrorClass, ErrorCode};
|
||||
|
||||
let (_, kind) = odb
|
||||
.read_header(*oid)
|
||||
.map_err(|err| error::Write::OdbRead { oid: *oid, err })?;
|
||||
if kind != expected {
|
||||
Err(error::Write::NotCommit {
|
||||
oid: *oid,
|
||||
err: Error::new(
|
||||
ErrorCode::NotFound,
|
||||
ErrorClass::Object,
|
||||
format!("Object '{oid}' is not expected object type {expected}"),
|
||||
),
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub mod error {
|
||||
use std::str;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Write {
|
||||
#[error(transparent)]
|
||||
Git(#[from] git2::Error),
|
||||
#[error("the parent '{oid}' provided is not a commit object")]
|
||||
NotCommit {
|
||||
oid: git2::Oid,
|
||||
#[source]
|
||||
err: git2::Error,
|
||||
},
|
||||
#[error("failed to access git odb")]
|
||||
Odb(#[source] git2::Error),
|
||||
#[error("failed to read '{oid}' from git odb")]
|
||||
OdbRead {
|
||||
oid: git2::Oid,
|
||||
#[source]
|
||||
err: git2::Error,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Read {
|
||||
#[error(transparent)]
|
||||
Git(#[from] git2::Error),
|
||||
#[error(transparent)]
|
||||
Parse(#[from] Parse),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Parse {
|
||||
#[error(transparent)]
|
||||
Git(#[from] git2::Error),
|
||||
#[error(transparent)]
|
||||
Header(#[from] metadata::commit::headers::ParseError),
|
||||
#[error(transparent)]
|
||||
Utf8(#[from] str::Utf8Error),
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Commit {
|
||||
type Error = error::Parse;
|
||||
|
||||
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
|
||||
Commit::from_str(str::from_utf8(data)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Commit {
|
||||
type Err = error::Parse;
|
||||
|
||||
fn from_str(buffer: &str) -> Result<Self, Self::Err> {
|
||||
let (header, message) = buffer
|
||||
.split_once("\n\n")
|
||||
.ok_or(metadata::commit::headers::ParseError::InvalidFormat)?;
|
||||
|
||||
let (tree, parents, author, committer, headers) =
|
||||
metadata::commit::headers::parse_commit_header(header)?;
|
||||
|
||||
let trailers = Trailers::parse(message)?;
|
||||
|
||||
let message = message
|
||||
.strip_suffix(&trailers.to_string(": "))
|
||||
.unwrap_or(message)
|
||||
.to_string();
|
||||
|
||||
Ok(Self(CommitData::new(
|
||||
tree,
|
||||
parents,
|
||||
author,
|
||||
committer,
|
||||
headers,
|
||||
message,
|
||||
trailers.iter(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Commit {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Commit {
|
||||
type Target = CommitData<git2::Oid, git2::Oid>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
use std::{borrow::Cow, fmt, fmt::Write, str::FromStr};
|
||||
|
||||
use git2::{MessageTrailersStrs, MessageTrailersStrsIterator};
|
||||
|
||||
use metadata::commit::trailers::Separator;
|
||||
|
||||
/// A Git commit's set of trailers that are left in the commit's
|
||||
/// message.
|
||||
///
|
||||
/// Trailers are key/value pairs in the last paragraph of a message,
|
||||
/// not including any patches or conflicts that may be present.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// To construct `Trailers`, you can use [`Trailers::parse`] or its
|
||||
/// `FromStr` implementation.
|
||||
///
|
||||
/// To iterate over the trailers, you can use [`Trailers::iter`].
|
||||
///
|
||||
/// To render the trailers to a `String`, you can use
|
||||
/// [`Trailers::to_string`] or its `Display` implementation (note that
|
||||
/// it will default to using `": "` as the separator.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```text
|
||||
/// Add new functionality
|
||||
///
|
||||
/// Making code better with new functionality.
|
||||
///
|
||||
/// X-Signed-Off-By: Alex Sellier
|
||||
/// X-Co-Authored-By: Fintan Halpenny
|
||||
/// ```
|
||||
///
|
||||
/// The trailers in the above example are:
|
||||
///
|
||||
/// ```text
|
||||
/// X-Signed-Off-By: Alex Sellier
|
||||
/// X-Co-Authored-By: Fintan Halpenny
|
||||
/// ```
|
||||
pub struct Trailers {
|
||||
inner: MessageTrailersStrs,
|
||||
}
|
||||
|
||||
impl Trailers {
|
||||
pub fn parse(message: &str) -> Result<Self, git2::Error> {
|
||||
Ok(Self {
|
||||
inner: git2::message_trailers_strs(message)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> Iter<'_> {
|
||||
Iter {
|
||||
inner: self.inner.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_string<'a, S>(&self, sep: S) -> String
|
||||
where
|
||||
S: Separator<'a>,
|
||||
{
|
||||
let mut buf = String::new();
|
||||
for (i, trailer) in self.iter().enumerate() {
|
||||
if i > 0 {
|
||||
writeln!(buf).ok();
|
||||
}
|
||||
|
||||
write!(buf, "{}", trailer.display(sep.sep_for(&trailer.token))).ok();
|
||||
}
|
||||
writeln!(buf).ok();
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Trailers {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(&self.to_string(": "))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Trailers {
|
||||
type Err = git2::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::parse(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Iter<'a> {
|
||||
inner: MessageTrailersStrsIterator<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
type Item = metadata::commit::trailers::Trailer<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let (token, value) = self.inner.next()?;
|
||||
Some(metadata::commit::trailers::Trailer {
|
||||
token: {
|
||||
// This code used to live in the same module with `Token`,
|
||||
// but was separated because it depends on `git2`.
|
||||
// We have no way of directly constructing a `Token`, anymore
|
||||
// but `git2` still guarantees that the trailer is well-formed.
|
||||
metadata::commit::trailers::Token::try_from(token)
|
||||
.expect("token from `git2` must be valid")
|
||||
},
|
||||
value: Cow::Borrowed(value),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright © 2021 The Radicle Link Contributors
|
||||
|
||||
use git_ext::Oid;
|
||||
use oid::Oid;
|
||||
|
||||
pub mod store;
|
||||
pub use store::{Contents, EntryId, Storage, Template, Timestamp};
|
||||
|
|
|
|||
|
|
@ -3,14 +3,15 @@
|
|||
use std::{error::Error, fmt, num::NonZeroUsize};
|
||||
|
||||
use nonempty::NonEmpty;
|
||||
use radicle_git_ext::Oid;
|
||||
use oid::Oid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::object::collaboration::error::{Create, Update};
|
||||
use crate::{signatures, TypeName};
|
||||
|
||||
/// Change entry storage.
|
||||
pub trait Storage {
|
||||
type StoreError: Error + Send + Sync + 'static;
|
||||
type StoreError: Error + Send + Sync + 'static + Into<Create> + Into<Update>;
|
||||
type LoadError: Error + Send + Sync + 'static;
|
||||
|
||||
type ObjectId;
|
||||
|
|
@ -194,6 +195,7 @@ pub struct Embed<T = Vec<u8>> {
|
|||
pub content: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "git2")]
|
||||
impl<T: From<Oid>> Embed<T> {
|
||||
/// Create a new embed.
|
||||
pub fn store(
|
||||
|
|
@ -210,6 +212,7 @@ impl<T: From<Oid>> Embed<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "git2")]
|
||||
impl Embed<Vec<u8>> {
|
||||
/// Get the object id of the embedded content.
|
||||
pub fn oid(&self) -> Oid {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
use std::ops::ControlFlow;
|
||||
use std::{cmp::Ordering, collections::BTreeSet};
|
||||
|
||||
use git_ext::Oid;
|
||||
use radicle_dag::Dag;
|
||||
use dag::Dag;
|
||||
use oid::Oid;
|
||||
|
||||
use crate::{
|
||||
change, object, object::collaboration::Evaluate, signatures::ExtendedSignature,
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
#![allow(clippy::too_many_arguments)]
|
||||
use std::{cmp::Ordering, collections::BTreeSet, ops::ControlFlow};
|
||||
|
||||
use git_ext::Oid;
|
||||
use radicle_dag::Dag;
|
||||
use dag::Dag;
|
||||
use oid::Oid;
|
||||
|
||||
pub use crate::change::{Contents, Entry, EntryId, Timestamp};
|
||||
|
||||
|
|
@ -77,7 +77,7 @@ impl History {
|
|||
self.graph.node(id, change);
|
||||
|
||||
for tip in tips {
|
||||
self.graph.dependency(id, (*tip).into());
|
||||
self.graph.dependency(id, tip);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,12 +59,20 @@ extern crate qcheck;
|
|||
#[macro_use(quickcheck)]
|
||||
extern crate qcheck_macros;
|
||||
|
||||
extern crate git_ref_format_core as fmt;
|
||||
extern crate radicle_crypto as crypto;
|
||||
extern crate radicle_git_ext as git_ext;
|
||||
extern crate radicle_dag as dag;
|
||||
extern crate radicle_git_metadata as metadata;
|
||||
extern crate radicle_oid as oid;
|
||||
|
||||
mod backend;
|
||||
|
||||
#[cfg(all(any(test, feature = "test"), feature = "git2"))]
|
||||
pub use backend::git;
|
||||
|
||||
#[cfg(feature = "stable-commit-ids")]
|
||||
pub use backend::stable;
|
||||
|
||||
mod change_graph;
|
||||
mod trailers;
|
||||
|
||||
|
|
@ -105,19 +113,9 @@ mod tests;
|
|||
///
|
||||
/// * [`object::Storage`]
|
||||
///
|
||||
/// **Note**: [`change::Storage`] is already implemented for
|
||||
/// [`git2::Repository`]. It is expected that the underlying storage
|
||||
/// for `object::Storage` will also be `git2::Repository`, but if not
|
||||
/// please open an issue to change the definition of `Store` :)
|
||||
pub trait Store
|
||||
where
|
||||
Self: object::Storage
|
||||
+ change::Storage<
|
||||
StoreError = git::change::error::Create,
|
||||
LoadError = git::change::error::Load,
|
||||
ObjectId = git_ext::Oid,
|
||||
Parent = git_ext::Oid,
|
||||
Signatures = ExtendedSignature,
|
||||
>,
|
||||
+ change::Storage<ObjectId = oid::Oid, Parent = oid::Oid, Signatures = ExtendedSignature>,
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright © 2022 The Radicle Link Contributors
|
||||
|
||||
use std::{convert::TryFrom as _, fmt, ops::Deref, str::FromStr};
|
||||
use std::{convert::TryFrom as _, ops::Deref, str::FromStr};
|
||||
|
||||
use git_ext::ref_format::{Component, RefString};
|
||||
use git_ext::Oid;
|
||||
use fmt::{Component, RefString};
|
||||
use oid::Oid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ pub use storage::{Commit, Objects, Reference, Storage};
|
|||
#[derive(Debug, Error)]
|
||||
pub enum ParseObjectId {
|
||||
#[error(transparent)]
|
||||
Git(#[from] git2::Error),
|
||||
Git(#[from] oid::str::ParseOidError),
|
||||
}
|
||||
|
||||
/// The id of an object
|
||||
|
|
@ -48,12 +48,14 @@ impl From<&Oid> for ObjectId {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "git2")]
|
||||
impl From<git2::Oid> for ObjectId {
|
||||
fn from(oid: git2::Oid) -> Self {
|
||||
Oid::from(oid).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "git2")]
|
||||
impl From<&git2::Oid> for ObjectId {
|
||||
fn from(oid: &git2::Oid) -> Self {
|
||||
ObjectId(Oid::from(*oid))
|
||||
|
|
@ -68,8 +70,8 @@ impl Deref for ObjectId {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ObjectId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
impl std::fmt::Display for ObjectId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
use std::convert::Infallible;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use git_ext::Oid;
|
||||
use nonempty::NonEmpty;
|
||||
use oid::Oid;
|
||||
|
||||
use crate::change::store::{Manifest, Version};
|
||||
use crate::{change, Entry, History, ObjectId, TypeName};
|
||||
|
|
@ -101,11 +101,11 @@ impl<R> Evaluate<R> for NonEmpty<Entry> {
|
|||
/// [`TypeName`] and [`ObjectId`] from it.
|
||||
///
|
||||
/// This assumes that the `refname` is in a
|
||||
/// [`git_ext::ref_format::Qualified`] format. If it has any
|
||||
/// [`fmt::Qualified`] format. If it has any
|
||||
/// `refs/namespaces`, they will be stripped to access the underlying
|
||||
/// [`git_ext::ref_format::Qualified`] format.
|
||||
/// [`fmt::Qualified`] format.
|
||||
///
|
||||
/// In the [`git_ext::ref_format::Qualified`] format it assumes that the
|
||||
/// In the [`fmt::Qualified`] format it assumes that the
|
||||
/// reference name is of the form:
|
||||
///
|
||||
/// `refs/<category>/<typename>/<object_id>[/<rest>*]`
|
||||
|
|
@ -115,14 +115,14 @@ impl<R> Evaluate<R> for NonEmpty<Entry> {
|
|||
///
|
||||
/// Also note that this will return `None` if:
|
||||
///
|
||||
/// * The `refname` is not [`git_ext::ref_format::Qualified`]
|
||||
/// * The `refname` is not [`fmt::Qualified`]
|
||||
/// * The parsing of the [`ObjectId`] fails
|
||||
/// * The parsing of the [`TypeName`] fails
|
||||
pub fn parse_refstr<R>(name: &R) -> Option<(TypeName, ObjectId)>
|
||||
where
|
||||
R: AsRef<git_ext::ref_format::RefStr>,
|
||||
R: AsRef<fmt::RefStr>,
|
||||
{
|
||||
use git_ext::ref_format::Qualified;
|
||||
use fmt::Qualified;
|
||||
let name = name.as_ref();
|
||||
let refs_cobs = match name.to_namespaced() {
|
||||
None => Qualified::from_refstr(name)?,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ use nonempty::NonEmpty;
|
|||
|
||||
use crate::Embed;
|
||||
use crate::Evaluate;
|
||||
use crate::Store;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
|
@ -23,7 +22,7 @@ pub struct Create {
|
|||
}
|
||||
|
||||
impl Create {
|
||||
fn template(self) -> change::Template<git_ext::Oid> {
|
||||
fn template(self) -> change::Template<oid::Oid> {
|
||||
change::Template {
|
||||
type_name: self.type_name,
|
||||
tips: Vec::new(),
|
||||
|
|
@ -38,7 +37,7 @@ impl Create {
|
|||
///
|
||||
/// The `storage` is the backing storage for storing
|
||||
/// [`crate::Entry`]s at content-addressable locations. Please see
|
||||
/// [`Store`] for further information.
|
||||
/// [`crate::Store`] for further information.
|
||||
///
|
||||
/// The `signer` is expected to be a cryptographic signing key. This
|
||||
/// ensures that the objects origin is cryptographically verifiable.
|
||||
|
|
@ -57,19 +56,24 @@ pub fn create<T, S, G>(
|
|||
signer: &G,
|
||||
resource: Option<Oid>,
|
||||
related: Vec<Oid>,
|
||||
identifier: &S::Namespace,
|
||||
identifier: &<S as crate::object::Storage>::Namespace,
|
||||
args: Create,
|
||||
) -> Result<CollaborativeObject<T>, error::Create>
|
||||
where
|
||||
T: Evaluate<S>,
|
||||
S: Store,
|
||||
S: crate::object::Storage
|
||||
+ crate::change::Storage<
|
||||
ObjectId = crate::object::Oid,
|
||||
Parent = crate::object::Oid,
|
||||
Signatures = crate::ExtendedSignature,
|
||||
>,
|
||||
G: signature::Signer<crate::ExtendedSignature>,
|
||||
{
|
||||
let type_name = args.type_name.clone();
|
||||
let version = args.version;
|
||||
let init_change = storage
|
||||
.store(resource, related, signer, args.template())
|
||||
.map_err(error::Create::from)?;
|
||||
.map_err(Into::<error::Create>::into)?;
|
||||
let object_id = init_change.id().into();
|
||||
let object = T::init(&init_change, storage).map_err(error::Create::evaluate)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,13 @@
|
|||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::git;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Create {
|
||||
#[error(transparent)]
|
||||
Evaluate(Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
#[error(transparent)]
|
||||
CreateChange(#[from] git::change::error::Create),
|
||||
#[cfg(feature = "git2")]
|
||||
CreateChange(#[from] crate::backend::git::change::error::Create),
|
||||
#[error("failed to updated references for during object creation: {err}")]
|
||||
Refs {
|
||||
#[source]
|
||||
|
|
@ -39,6 +38,7 @@ pub enum Retrieve {
|
|||
#[error(transparent)]
|
||||
Evaluate(Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
#[error(transparent)]
|
||||
#[cfg(feature = "git2")]
|
||||
Git(#[from] git2::Error),
|
||||
#[error("failed to get references during object retrieval: {err}")]
|
||||
Refs {
|
||||
|
|
@ -62,13 +62,15 @@ pub enum Update {
|
|||
#[error("no object found")]
|
||||
NoSuchObject,
|
||||
#[error(transparent)]
|
||||
CreateChange(#[from] git::change::error::Create),
|
||||
#[cfg(feature = "git2")]
|
||||
CreateChange(#[from] crate::backend::git::change::error::Create),
|
||||
#[error("failed to get references during object update: {err}")]
|
||||
Refs {
|
||||
#[source]
|
||||
err: Box<dyn std::error::Error + Send + Sync + 'static>,
|
||||
},
|
||||
#[error(transparent)]
|
||||
#[cfg(feature = "git2")]
|
||||
Git(#[from] git2::Error),
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright © 2022 The Radicle Link Contributors
|
||||
|
||||
use crate::{change_graph::ChangeGraph, CollaborativeObject, Evaluate, ObjectId, Store, TypeName};
|
||||
use crypto::ssh::ExtendedSignature;
|
||||
|
||||
use crate::{change_graph::ChangeGraph, CollaborativeObject, Evaluate, ObjectId, TypeName};
|
||||
|
||||
use super::error;
|
||||
|
||||
|
|
@ -8,7 +10,7 @@ use super::error;
|
|||
///
|
||||
/// The `storage` is the backing storage for storing
|
||||
/// [`crate::Entry`]s at content-addressable locations. Please see
|
||||
/// [`Store`] for further information.
|
||||
/// [`crate::Store`] for further information.
|
||||
///
|
||||
/// The `typename` is the type of object to be found, while the
|
||||
/// `object_id` is the identifier for the particular object under that
|
||||
|
|
@ -20,7 +22,12 @@ pub fn get<T, S>(
|
|||
) -> Result<Option<CollaborativeObject<T>>, error::Retrieve>
|
||||
where
|
||||
T: Evaluate<S>,
|
||||
S: Store,
|
||||
S: crate::object::Storage,
|
||||
S: crate::change::Storage<
|
||||
ObjectId = crate::object::Oid,
|
||||
Parent = crate::object::Oid,
|
||||
Signatures = ExtendedSignature,
|
||||
>,
|
||||
{
|
||||
let tip_refs = storage
|
||||
.objects(typename, oid)
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@
|
|||
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use git_ext::Oid;
|
||||
use crypto::ssh::ExtendedSignature;
|
||||
use oid::Oid;
|
||||
|
||||
use crate::{change_graph::ChangeGraph, ObjectId, Store, TypeName};
|
||||
use crate::{change_graph::ChangeGraph, ObjectId, TypeName};
|
||||
|
||||
use super::error;
|
||||
|
||||
|
|
@ -28,7 +29,7 @@ pub struct ChangeGraphInfo {
|
|||
///
|
||||
/// The `storage` is the backing storage for storing
|
||||
/// [`crate::Entry`]s at content-addressable locations. Please see
|
||||
/// [`Store`] for further information.
|
||||
/// [`crate::Store`] for further information.
|
||||
///
|
||||
/// The `typename` is the type of object to be found, while the `oid`
|
||||
/// is the identifier for the particular object under that type.
|
||||
|
|
@ -38,7 +39,12 @@ pub fn changegraph<S>(
|
|||
oid: &ObjectId,
|
||||
) -> Result<Option<ChangeGraphInfo>, error::Retrieve>
|
||||
where
|
||||
S: Store,
|
||||
S: crate::object::Storage,
|
||||
S: crate::change::Storage<
|
||||
ObjectId = crate::object::Oid,
|
||||
Parent = crate::object::Oid,
|
||||
Signatures = ExtendedSignature,
|
||||
>,
|
||||
{
|
||||
let tip_refs = storage
|
||||
.objects(typename, oid)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright © 2022 The Radicle Link Contributors
|
||||
|
||||
use crate::{change_graph::ChangeGraph, CollaborativeObject, Evaluate, Store, TypeName};
|
||||
use crate::{change_graph::ChangeGraph, CollaborativeObject, Evaluate, TypeName};
|
||||
|
||||
use super::error;
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ use super::error;
|
|||
///
|
||||
/// The `storage` is the backing storage for storing
|
||||
/// [`crate::Entry`]s at content-addressable locations. Please see
|
||||
/// [`Store`] for further information.
|
||||
/// [`crate::Store`] for further information.
|
||||
///
|
||||
/// The `typename` is the type of objects to be listed.
|
||||
pub fn list<T, S>(
|
||||
|
|
@ -17,7 +17,12 @@ pub fn list<T, S>(
|
|||
) -> Result<Vec<CollaborativeObject<T>>, error::Retrieve>
|
||||
where
|
||||
T: Evaluate<S>,
|
||||
S: Store,
|
||||
S: crate::object::Storage,
|
||||
S: crate::change::Storage<
|
||||
ObjectId = crate::object::Oid,
|
||||
Parent = crate::object::Oid,
|
||||
Signatures = crate::ExtendedSignature,
|
||||
>,
|
||||
{
|
||||
let references = storage
|
||||
.types(typename)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright © 2022 The Radicle Link Contributors
|
||||
|
||||
use crate::{ObjectId, Store, TypeName};
|
||||
use crate::{ObjectId, TypeName};
|
||||
|
||||
use super::error;
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ use super::error;
|
|||
///
|
||||
/// The `storage` is the backing storage for storing
|
||||
/// [`crate::Entry`]s at content-addressable locations. Please see
|
||||
/// [`Store`] for further information.
|
||||
/// [`crate::Store`] for further information.
|
||||
///
|
||||
/// The `typename` is the type of object to be found, while the
|
||||
/// `object_id` is the identifier for the particular object under that
|
||||
|
|
@ -20,7 +20,7 @@ pub fn remove<S>(
|
|||
oid: &ObjectId,
|
||||
) -> Result<(), error::Remove>
|
||||
where
|
||||
S: Store,
|
||||
S: crate::object::Storage,
|
||||
{
|
||||
storage
|
||||
.remove(identifier, typename, oid)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright © 2022 The Radicle Link Contributors
|
||||
use std::iter;
|
||||
|
||||
use git_ext::Oid;
|
||||
use nonempty::NonEmpty;
|
||||
use oid::Oid;
|
||||
|
||||
use crate::{
|
||||
change, change_graph::ChangeGraph, history::EntryId, CollaborativeObject, Embed, Evaluate,
|
||||
ExtendedSignature, ObjectId, Store, TypeName,
|
||||
ExtendedSignature, ObjectId, TypeName,
|
||||
};
|
||||
|
||||
use super::error;
|
||||
|
|
@ -40,7 +40,7 @@ pub struct Update {
|
|||
///
|
||||
/// The `storage` is the backing storage for storing
|
||||
/// [`crate::Entry`]s at content-addressable locations. Please see
|
||||
/// [`Store`] for further information.
|
||||
/// [`crate::Store`] for further information.
|
||||
///
|
||||
/// The `signer` is expected to be a cryptographic signing key. This
|
||||
/// ensures that the objects origin is cryptographically verifiable.
|
||||
|
|
@ -65,7 +65,8 @@ pub fn update<T, S, G>(
|
|||
) -> Result<Updated<T>, error::Update>
|
||||
where
|
||||
T: Evaluate<S>,
|
||||
S: Store,
|
||||
S: crate::object::Storage,
|
||||
S: change::Storage<ObjectId = Oid, Parent = Oid, Signatures = ExtendedSignature>,
|
||||
G: signature::Signer<ExtendedSignature>,
|
||||
{
|
||||
let Update {
|
||||
|
|
@ -86,7 +87,8 @@ where
|
|||
graph.evaluate(storage).map_err(error::Update::evaluate)?;
|
||||
|
||||
// Create a commit for this change, but don't update any references yet.
|
||||
let entry = storage.store(
|
||||
let entry = storage
|
||||
.store(
|
||||
resource,
|
||||
related,
|
||||
signer,
|
||||
|
|
@ -97,7 +99,8 @@ where
|
|||
type_name: typename.clone(),
|
||||
message,
|
||||
},
|
||||
)?;
|
||||
)
|
||||
.map_err(Into::<error::Update>::into)?;
|
||||
let head = entry.id;
|
||||
let parents = entry.parents.to_vec();
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
use std::{collections::BTreeMap, error::Error};
|
||||
|
||||
use git_ext::ref_format::RefString;
|
||||
use git_ext::Oid;
|
||||
use fmt::RefString;
|
||||
use oid::Oid;
|
||||
|
||||
use crate::change::EntryId;
|
||||
use crate::{ObjectId, TypeName};
|
||||
|
|
@ -93,31 +93,32 @@ pub trait Storage {
|
|||
pub mod convert {
|
||||
use std::str;
|
||||
|
||||
use git_ext::ref_format::RefString;
|
||||
use fmt::RefString;
|
||||
use thiserror::Error;
|
||||
|
||||
use super::{Commit, Reference};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("the reference '{name}' does not point to a commit object")]
|
||||
NotCommit {
|
||||
name: RefString,
|
||||
#[cfg(feature = "git2")]
|
||||
#[source]
|
||||
err: git2::Error,
|
||||
},
|
||||
#[error(transparent)]
|
||||
Ref(#[from] git_ext::ref_format::Error),
|
||||
Ref(#[from] fmt::Error),
|
||||
#[error(transparent)]
|
||||
Utf8(#[from] str::Utf8Error),
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<git2::Reference<'a>> for Reference {
|
||||
#[cfg(feature = "git2")]
|
||||
impl<'a> TryFrom<git2::Reference<'a>> for super::Reference {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: git2::Reference<'a>) -> Result<Self, Self::Error> {
|
||||
let name = RefString::try_from(str::from_utf8(value.name_bytes())?)?;
|
||||
let target = Commit::from(value.peel_to_commit().map_err(|err| Error::NotCommit {
|
||||
let target =
|
||||
super::Commit::from(value.peel_to_commit().map_err(|err| Error::NotCommit {
|
||||
name: name.clone(),
|
||||
err,
|
||||
})?);
|
||||
|
|
@ -125,9 +126,10 @@ pub mod convert {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<git2::Commit<'a>> for Commit {
|
||||
#[cfg(feature = "git2")]
|
||||
impl<'a> From<git2::Commit<'a>> for super::Commit {
|
||||
fn from(commit: git2::Commit<'a>) -> Self {
|
||||
Commit {
|
||||
Self {
|
||||
id: commit.id().into(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ use std::{
|
|||
};
|
||||
|
||||
use crypto::{ssh, PublicKey};
|
||||
use git_ext::commit::{
|
||||
use metadata::commit::{
|
||||
headers::Signature::{Pgp, Ssh},
|
||||
Commit,
|
||||
CommitData,
|
||||
};
|
||||
|
||||
pub use ssh::ExtendedSignature;
|
||||
|
|
@ -55,10 +55,10 @@ impl From<Signatures> for BTreeMap<PublicKey, crypto::Signature> {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Commit> for Signatures {
|
||||
impl<Tree, Parent> TryFrom<&CommitData<Tree, Parent>> for Signatures {
|
||||
type Error = error::Signatures;
|
||||
|
||||
fn try_from(value: &Commit) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: &CommitData<Tree, Parent>) -> Result<Self, Self::Error> {
|
||||
value
|
||||
.signatures()
|
||||
.filter_map(|signature| {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright © 2019-2020 The Radicle Foundation <hello@radicle.foundation>
|
||||
|
||||
use radicle_crypto::ssh::ExtendedSignatureError;
|
||||
use crypto::ssh::ExtendedSignatureError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
#[cfg(feature = "git2")]
|
||||
pub mod identity;
|
||||
#[cfg(feature = "git2")]
|
||||
pub use identity::{Person, Project, RemoteProject};
|
||||
|
||||
#[cfg(feature = "git2")]
|
||||
pub mod storage;
|
||||
#[cfg(feature = "git2")]
|
||||
pub use storage::Storage;
|
||||
|
||||
pub mod arbitrary;
|
||||
|
|
|
|||
|
|
@ -26,11 +26,7 @@ impl Arbitrary for TypeName {
|
|||
|
||||
impl Arbitrary for ObjectId {
|
||||
fn arbitrary(g: &mut qcheck::Gen) -> Self {
|
||||
let mut rng = fastrand::Rng::with_seed(u64::arbitrary(g));
|
||||
let bytes = iter::repeat_with(|| rng.u8(..))
|
||||
.take(20)
|
||||
.collect::<Vec<_>>();
|
||||
Self::from(git_ext::Oid::try_from(bytes.as_slice()).unwrap())
|
||||
Self::from(oid::Oid::arbitrary(g))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
pub mod project;
|
||||
pub use project::{Project, RemoteProject};
|
||||
|
||||
#[cfg(feature = "git2")]
|
||||
pub mod person;
|
||||
#[cfg(feature = "git2")]
|
||||
pub use person::Person;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use git_ext::Oid;
|
||||
use oid::Oid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::test::storage::{self, Storage};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::collections::BTreeSet;
|
||||
|
||||
use git_ext::Oid;
|
||||
use oid::Oid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::test;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use std::{collections::BTreeMap, convert::TryFrom as _};
|
||||
|
||||
use radicle_git_ext::ref_format::{refname, Component};
|
||||
use radicle_git_ref_format::refname;
|
||||
|
||||
use fmt::Component;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -29,7 +31,7 @@ pub mod error {
|
|||
#[error(transparent)]
|
||||
Git(#[from] git2::Error),
|
||||
#[error(transparent)]
|
||||
Format(#[from] git_ext::ref_format::Error),
|
||||
Format(#[from] fmt::Error),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -93,16 +95,16 @@ impl change::Storage for Storage {
|
|||
self.as_raw().load(id)
|
||||
}
|
||||
|
||||
fn parents_of(&self, id: &git_ext::Oid) -> Result<Vec<git_ext::Oid>, Self::LoadError> {
|
||||
fn parents_of(&self, id: &oid::Oid) -> Result<Vec<radicle_oid::Oid>, Self::LoadError> {
|
||||
Ok(self
|
||||
.as_raw()
|
||||
.find_commit(**id)?
|
||||
.find_commit(id.into())?
|
||||
.parent_ids()
|
||||
.map(git_ext::Oid::from)
|
||||
.map(oid::Oid::from)
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
fn manifest_of(&self, id: &git_ext::Oid) -> Result<crate::Manifest, Self::LoadError> {
|
||||
fn manifest_of(&self, id: &oid::Oid) -> Result<crate::Manifest, Self::LoadError> {
|
||||
self.as_raw().manifest_of(id)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,23 @@
|
|||
use fmt::{Component, RefString};
|
||||
|
||||
use radicle_git_ref_format::refname;
|
||||
|
||||
use crate::{object, test::arbitrary::Invalid, ObjectId, TypeName};
|
||||
|
||||
#[cfg(feature = "git2")]
|
||||
mod git {
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use crypto::test::signer::MockSigner;
|
||||
use crypto::{PublicKey, Signer};
|
||||
use git_ext::ref_format::{refname, Component, RefString};
|
||||
use nonempty::{nonempty, NonEmpty};
|
||||
use qcheck::Arbitrary;
|
||||
|
||||
use crate::{
|
||||
create, get, list, object, test::arbitrary::Invalid, update, Create, Entry, ObjectId, TypeName,
|
||||
Update, Updated, Version,
|
||||
create, get, list, update, Create, Entry, ObjectId, TypeName, Update, Updated, Version,
|
||||
};
|
||||
|
||||
use super::test;
|
||||
use crate::test;
|
||||
|
||||
#[test]
|
||||
fn roundtrip() {
|
||||
|
|
@ -235,6 +241,36 @@ fn traverse_cobs() {
|
|||
assert_eq!(contents, vec![b"issue 1".to_vec(), b"issue 2".to_vec()]);
|
||||
}
|
||||
|
||||
fn copy_to(
|
||||
repo: &git2::Repository,
|
||||
from: &PublicKey,
|
||||
to: &test::RemoteProject,
|
||||
typename: &TypeName,
|
||||
object: ObjectId,
|
||||
) -> Result<(), git2::Error> {
|
||||
let original = {
|
||||
let name = format!("refs/rad/{from}/cobs/{typename}/{object}");
|
||||
let r = repo.find_reference(&name)?;
|
||||
r.target().unwrap()
|
||||
};
|
||||
|
||||
let name = format!(
|
||||
"refs/rad/{}/cobs/{}/{}",
|
||||
to.identifier().to_path(),
|
||||
typename,
|
||||
object
|
||||
);
|
||||
repo.reference(&name, original, false, "copying object reference")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn gen<T: Arbitrary>(size: usize) -> T {
|
||||
let mut gen = qcheck::Gen::new(size);
|
||||
|
||||
T::arbitrary(&mut gen)
|
||||
}
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn parse_refstr(oid: ObjectId, typename: TypeName) {
|
||||
let suffix = refname!("refs/cobs")
|
||||
|
|
@ -297,32 +333,3 @@ fn invalid_parse_refstr(oid: Invalid<ObjectId>, typename: TypeName) {
|
|||
None
|
||||
);
|
||||
}
|
||||
|
||||
fn gen<T: Arbitrary>(size: usize) -> T {
|
||||
let mut gen = qcheck::Gen::new(size);
|
||||
|
||||
T::arbitrary(&mut gen)
|
||||
}
|
||||
|
||||
fn copy_to(
|
||||
repo: &git2::Repository,
|
||||
from: &PublicKey,
|
||||
to: &test::RemoteProject,
|
||||
typename: &TypeName,
|
||||
object: ObjectId,
|
||||
) -> Result<(), git2::Error> {
|
||||
let original = {
|
||||
let name = format!("refs/rad/{from}/cobs/{typename}/{object}");
|
||||
let r = repo.find_reference(&name)?;
|
||||
r.target().unwrap()
|
||||
};
|
||||
|
||||
let name = format!(
|
||||
"refs/rad/{}/cobs/{}/{}",
|
||||
to.identifier().to_path(),
|
||||
typename,
|
||||
object
|
||||
);
|
||||
repo.reference(&name, original, false, "copying object reference")?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright © 2019-2020 The Radicle Foundation <hello@radicle.foundation>
|
||||
|
||||
use git_ext::commit::trailers::{OwnedTrailer, Token, Trailer};
|
||||
use metadata::commit::trailers::{OwnedTrailer, Token, Trailer};
|
||||
use std::ops::Deref as _;
|
||||
|
||||
pub mod error {
|
||||
|
|
@ -12,21 +12,22 @@ pub mod error {
|
|||
WrongToken,
|
||||
#[error("no value for Rad-Resource")]
|
||||
NoValue,
|
||||
#[error("invalid git OID")]
|
||||
InvalidOid,
|
||||
/// Invalid object ID.
|
||||
#[error("invalid oid: {0}")]
|
||||
InvalidOid(#[from] radicle_oid::str::ParseOidError),
|
||||
}
|
||||
}
|
||||
|
||||
/// Commit trailer for COB commits.
|
||||
pub enum CommitTrailer {
|
||||
/// Points to the owning resource.
|
||||
Resource(git2::Oid),
|
||||
Resource(oid::Oid),
|
||||
/// Points to a related change.
|
||||
Related(git2::Oid),
|
||||
Related(oid::Oid),
|
||||
}
|
||||
|
||||
impl CommitTrailer {
|
||||
pub fn oid(&self) -> git2::Oid {
|
||||
pub fn oid(&self) -> oid::Oid {
|
||||
match self {
|
||||
Self::Resource(oid) => *oid,
|
||||
Self::Related(oid) => *oid,
|
||||
|
|
@ -38,12 +39,11 @@ impl TryFrom<&Trailer<'_>> for CommitTrailer {
|
|||
type Error = error::InvalidResourceTrailer;
|
||||
|
||||
fn try_from(Trailer { value, token }: &Trailer<'_>) -> Result<Self, Self::Error> {
|
||||
let ext_oid =
|
||||
git_ext::Oid::try_from(value.as_ref()).map_err(|_| Self::Error::InvalidOid)?;
|
||||
let oid = value.as_ref().parse::<oid::Oid>()?;
|
||||
if token.deref() == "Rad-Resource" {
|
||||
Ok(CommitTrailer::Resource(ext_oid.into()))
|
||||
Ok(CommitTrailer::Resource(oid))
|
||||
} else if token.deref() == "Rad-Related" {
|
||||
Ok(CommitTrailer::Related(ext_oid.into()))
|
||||
Ok(CommitTrailer::Related(oid))
|
||||
} else {
|
||||
Err(Self::Error::WrongToken)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright © 2022 The Radicle Link Contributors
|
||||
|
||||
use std::{fmt, str::FromStr};
|
||||
use std::str::FromStr;
|
||||
|
||||
use git_ext::ref_format::{Component, RefString};
|
||||
use fmt::{Component, RefString};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
|
|
@ -24,8 +24,8 @@ impl TypeName {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TypeName {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
impl std::fmt::Display for TypeName {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(self.0.as_str())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,5 +20,6 @@ gix-transport = { version = "0.44.0", features = ["blocking-client"] }
|
|||
log = { workspace = true, features = ["std"] }
|
||||
nonempty = { workspace = true }
|
||||
radicle = { workspace = true }
|
||||
radicle-git-ext = { workspace = true, features = ["bstr"] }
|
||||
radicle-oid = { workspace = true, features = ["gix"] }
|
||||
radicle-git-ref-format = { workspace = true, features = ["bstr"] }
|
||||
thiserror = { workspace = true }
|
||||
|
|
@ -3,21 +3,3 @@ pub(crate) mod packfile;
|
|||
pub(crate) mod repository;
|
||||
|
||||
pub mod refs;
|
||||
|
||||
pub(crate) mod oid {
|
||||
//! Helper functions for converting to/from [`radicle::git::Oid`] and
|
||||
//! [`ObjectId`].
|
||||
|
||||
use gix_hash::ObjectId;
|
||||
use radicle::git::Oid;
|
||||
|
||||
/// Convert from an [`ObjectId`] to an [`Oid`].
|
||||
pub fn to_oid(oid: ObjectId) -> Oid {
|
||||
Oid::try_from(oid.as_bytes()).expect("invalid gix Oid")
|
||||
}
|
||||
|
||||
/// Convert from an [`Oid`] to an [`ObjectId`].
|
||||
pub fn to_object_id(oid: Oid) -> ObjectId {
|
||||
ObjectId::from(gix_hash::oid::from_bytes_unchecked(oid.as_ref()))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use radicle::git::{Component, Oid, Qualified, RefString};
|
||||
use radicle::git::fmt::{Component, Qualified, RefString};
|
||||
use radicle::git::Oid;
|
||||
use radicle::prelude::PublicKey;
|
||||
|
||||
use super::refs::{Applied, RefUpdate, Update};
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use either::Either;
|
||||
use radicle::git::{Namespaced, Oid, Qualified};
|
||||
use radicle::git::fmt::{Namespaced, Qualified};
|
||||
use radicle::git::Oid;
|
||||
use radicle::prelude::PublicKey;
|
||||
|
||||
pub use radicle::storage::RefUpdate;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@ pub mod error;
|
|||
|
||||
use either::Either;
|
||||
use radicle::git::raw::ErrorExt as _;
|
||||
use radicle::git::{self, Namespaced, Oid, Qualified};
|
||||
use radicle::git::{
|
||||
self,
|
||||
fmt::{Namespaced, Qualified},
|
||||
Oid,
|
||||
};
|
||||
use radicle::storage::git::Repository;
|
||||
|
||||
use super::refs::{Applied, Policy, RefUpdate, Update};
|
||||
|
|
@ -48,7 +52,7 @@ pub fn contains(repo: &Repository, oid: Oid) -> Result<bool, error::Contains> {
|
|||
/// - The object does not peel to a commit
|
||||
/// - Attempting to find the object fails
|
||||
fn find_and_peel(repo: &Repository, oid: Oid) -> Result<Oid, error::Ancestry> {
|
||||
match repo.backend.find_object(*oid, None) {
|
||||
match repo.backend.find_object(oid.into(), None) {
|
||||
Ok(object) => Ok(object
|
||||
.peel(git::raw::ObjectType::Commit)
|
||||
.map_err(|err| error::Ancestry::Peel { oid, err })?
|
||||
|
|
@ -80,7 +84,7 @@ pub fn ahead_behind(
|
|||
|
||||
let (ahead, behind) = repo
|
||||
.backend
|
||||
.graph_ahead_behind(*new_commit, *old_commit)
|
||||
.graph_ahead_behind(new_commit.into(), old_commit.into())
|
||||
.map_err(|err| error::Ancestry::Check {
|
||||
old: old_commit,
|
||||
new: new_commit,
|
||||
|
|
@ -100,7 +104,7 @@ pub fn refname_to_id<'a, N>(repo: &Repository, refname: N) -> Result<Option<Oid>
|
|||
where
|
||||
N: Into<Qualified<'a>>,
|
||||
{
|
||||
use radicle::git::raw::ErrorCode::NotFound;
|
||||
use git::raw::ErrorCode::NotFound;
|
||||
|
||||
let refname = refname.into();
|
||||
match repo.backend.refname_to_id(refname.as_ref()) {
|
||||
|
|
@ -170,7 +174,7 @@ fn direct<'a>(
|
|||
});
|
||||
};
|
||||
|
||||
if prev == *target {
|
||||
if target == prev {
|
||||
// If the two objects are identical, their ancestry does not matter,
|
||||
// we can always skip the update.
|
||||
return Ok(RefUpdate::Skipped {
|
||||
|
|
@ -193,7 +197,7 @@ fn direct<'a>(
|
|||
|
||||
let target = repo
|
||||
.backend
|
||||
.find_object(*target, ANY_KIND)
|
||||
.find_object(target.into(), ANY_KIND)
|
||||
.map_err(|err| error::Update::Ancestry(error::Ancestry::Object { oid: target, err }))?;
|
||||
|
||||
match (prev.kind(), target.kind()) {
|
||||
|
|
@ -276,7 +280,7 @@ fn prune<'a>(
|
|||
name: Namespaced<'a>,
|
||||
prev: Either<Oid, Qualified<'a>>,
|
||||
) -> Result<Updated<'a>, error::Update> {
|
||||
use radicle::git::raw::ObjectType;
|
||||
use git::raw::ObjectType;
|
||||
|
||||
match find(repo, &name)? {
|
||||
Some(mut r) => {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
use radicle::git::{ext, raw, Namespaced, Oid, Qualified};
|
||||
use radicle::git::{
|
||||
self,
|
||||
fmt::{Namespaced, Qualified},
|
||||
Oid,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("could not open Git ODB")]
|
||||
pub struct Contains(#[source] pub raw::Error);
|
||||
pub struct Contains(#[source] pub git::raw::Error);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Ancestry {
|
||||
|
|
@ -14,19 +18,19 @@ pub enum Ancestry {
|
|||
old: Oid,
|
||||
new: Oid,
|
||||
#[source]
|
||||
err: raw::Error,
|
||||
err: git::raw::Error,
|
||||
},
|
||||
#[error("failed to peel object to commit {oid}: {err}")]
|
||||
Peel {
|
||||
oid: Oid,
|
||||
#[source]
|
||||
err: raw::Error,
|
||||
err: git::raw::Error,
|
||||
},
|
||||
#[error("failed to find object {oid}: {err}")]
|
||||
Object {
|
||||
oid: Oid,
|
||||
#[source]
|
||||
err: raw::Error,
|
||||
err: git::raw::Error,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -35,15 +39,15 @@ pub enum Ancestry {
|
|||
pub struct Resolve {
|
||||
pub name: Qualified<'static>,
|
||||
#[source]
|
||||
pub err: raw::Error,
|
||||
pub err: git::raw::Error,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("failed to scan for refs matching {pattern}")]
|
||||
pub struct Scan {
|
||||
pub pattern: radicle::git::PatternString,
|
||||
pub pattern: radicle::git::fmt::refspec::PatternString,
|
||||
#[source]
|
||||
pub err: ext::Error,
|
||||
pub err: git::raw::Error,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
@ -55,19 +59,19 @@ pub enum Update {
|
|||
name: Namespaced<'static>,
|
||||
target: Oid,
|
||||
#[source]
|
||||
err: raw::Error,
|
||||
err: git::raw::Error,
|
||||
},
|
||||
#[error("failed to delete reference {name}")]
|
||||
Delete {
|
||||
name: Namespaced<'static>,
|
||||
#[source]
|
||||
err: raw::Error,
|
||||
err: git::raw::Error,
|
||||
},
|
||||
#[error("failed to find ref {name}")]
|
||||
Find {
|
||||
name: Namespaced<'static>,
|
||||
#[source]
|
||||
err: raw::Error,
|
||||
err: git::raw::Error,
|
||||
},
|
||||
#[error("non-fast-forward update of {name} (current: {cur}, new: {new})")]
|
||||
NonFF {
|
||||
|
|
@ -76,7 +80,7 @@ pub enum Update {
|
|||
cur: Oid,
|
||||
},
|
||||
#[error("failed to peel ref to object")]
|
||||
Peel(#[source] raw::Error),
|
||||
Peel(#[source] git::raw::Error),
|
||||
#[error(transparent)]
|
||||
Resolve(#[from] Resolve),
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
use bstr::{BString, ByteSlice};
|
||||
use either::Either;
|
||||
use radicle::crypto::PublicKey;
|
||||
use radicle::git::{self, Component, Namespaced, Oid, Qualified};
|
||||
use radicle::git::{
|
||||
self,
|
||||
fmt::{Component, Namespaced, Qualified},
|
||||
Oid,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
pub use radicle::git::refs::storage::Special;
|
||||
|
|
@ -26,7 +30,6 @@ pub enum Error {
|
|||
pub(crate) fn unpack_ref<'a>(
|
||||
r: gix_protocol::handshake::Ref,
|
||||
) -> Result<(ReceivedRefname<'a>, Oid), Error> {
|
||||
use crate::git::oid;
|
||||
use gix_protocol::handshake::Ref;
|
||||
|
||||
match r {
|
||||
|
|
@ -43,7 +46,7 @@ pub(crate) fn unpack_ref<'a>(
|
|||
full_ref_name,
|
||||
object,
|
||||
..
|
||||
} => ReceivedRefname::try_from(full_ref_name).map(|name| (name, oid::to_oid(object))),
|
||||
} => ReceivedRefname::try_from(full_ref_name).map(|name| (name, object.into())),
|
||||
Ref::Unborn { full_ref_name, .. } => {
|
||||
unreachable!("BUG: unborn ref {}", full_ref_name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ use either::Either;
|
|||
use gix_protocol::handshake::Ref;
|
||||
use nonempty::NonEmpty;
|
||||
use radicle::crypto::PublicKey;
|
||||
use radicle::git::{refname, Component, Namespaced, Qualified};
|
||||
use radicle::git::fmt::{refname, Component, Namespaced, Qualified};
|
||||
use radicle::storage::git::Repository;
|
||||
use radicle::storage::refs::{RefsAt, Special};
|
||||
use radicle::storage::ReadRepository;
|
||||
|
|
@ -52,7 +52,7 @@ use crate::{policy, refs};
|
|||
|
||||
pub mod error {
|
||||
use radicle::crypto::PublicKey;
|
||||
use radicle::git::RefString;
|
||||
use radicle::git::fmt::RefString;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::transport::WantsHavesError;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::time::Instant;
|
|||
|
||||
use gix_protocol::handshake;
|
||||
use radicle::crypto::PublicKey;
|
||||
use radicle::git::{Oid, Qualified};
|
||||
use radicle::git::{fmt::Qualified, Oid};
|
||||
use radicle::identity::{Did, Doc, DocError};
|
||||
|
||||
use radicle::prelude::Verified;
|
||||
|
|
|
|||
|
|
@ -13,12 +13,11 @@ use gix_protocol::handshake;
|
|||
use gix_transport::client;
|
||||
use gix_transport::Protocol;
|
||||
use gix_transport::Service;
|
||||
use radicle::git::fmt::Qualified;
|
||||
use radicle::git::Oid;
|
||||
use radicle::git::Qualified;
|
||||
use radicle::storage::git::Repository;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::git::oid;
|
||||
use crate::git::packfile::Keepfile;
|
||||
use crate::git::repository;
|
||||
|
||||
|
|
@ -163,7 +162,7 @@ where
|
|||
|
||||
let idx = File::at(pack_path, gix_hash::Kind::Sha1)?;
|
||||
for oid in wants_haves.wants {
|
||||
if idx.lookup(oid::to_object_id(oid)).is_none() {
|
||||
if idx.lookup(oid).is_none() {
|
||||
return Err(Error::NotFound(oid));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use gix_protocol::fetch::negotiate::one_round::State;
|
|||
use gix_protocol::handshake;
|
||||
use gix_protocol::handshake::Ref;
|
||||
|
||||
use crate::git::{oid, packfile};
|
||||
use crate::git::packfile;
|
||||
|
||||
use super::{agent_name, Connection, WantsHaves};
|
||||
|
||||
|
|
@ -109,7 +109,7 @@ impl fetch::Negotiate for Negotiate {
|
|||
) -> bool {
|
||||
let mut has_want = false;
|
||||
for oid in &self.wants_haves.wants {
|
||||
arguments.want(oid::to_object_id(*oid));
|
||||
arguments.want(oid);
|
||||
has_want = true;
|
||||
}
|
||||
has_want
|
||||
|
|
@ -126,7 +126,7 @@ impl fetch::Negotiate for Negotiate {
|
|||
_previous_response: Option<&fetch::Response>,
|
||||
) -> Result<(fetch::negotiate::Round, bool), fetch::negotiate::Error> {
|
||||
for oid in &self.wants_haves.haves {
|
||||
arguments.have(oid::to_object_id(*oid));
|
||||
arguments.have(oid);
|
||||
}
|
||||
|
||||
let round = fetch::negotiate::Round {
|
||||
|
|
|
|||
|
|
@ -31,9 +31,6 @@ nonempty = { workspace = true, features = ["serialize"] }
|
|||
qcheck = { workspace = true, optional = true }
|
||||
radicle = { workspace = true, features = ["logger"] }
|
||||
radicle-fetch = { workspace = true }
|
||||
# N.b. this is required to use macros, even though it's re-exported
|
||||
# through radicle
|
||||
radicle-git-ext = { workspace = true, features = ["serde"] }
|
||||
radicle-protocol = { workspace = true }
|
||||
radicle-signals = { workspace = true }
|
||||
sqlite = { workspace = true, features = ["bundled"] }
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex};
|
|||
use std::time;
|
||||
|
||||
use radicle::crypto::PublicKey;
|
||||
use radicle::git;
|
||||
use radicle::git::Oid;
|
||||
use radicle::storage::refs::RefsAt;
|
||||
|
||||
use crate::identity::RepoId;
|
||||
|
|
@ -108,7 +108,7 @@ impl radicle::node::Handle for Handle {
|
|||
|
||||
Ok(RefsAt {
|
||||
remote: self.nid()?,
|
||||
at: git::raw::Oid::zero().into(),
|
||||
at: Oid::sha1_zero(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use radicle::crypto::ssh::keystore::MemorySigner;
|
|||
use radicle::crypto::test::signer::MockSigner;
|
||||
use radicle::crypto::Signature;
|
||||
use radicle::git;
|
||||
use radicle::git::refname;
|
||||
use radicle::git::fmt::refname;
|
||||
use radicle::identity::{RepoId, Visibility};
|
||||
use radicle::node::config::ConnectAddress;
|
||||
use radicle::node::policy::store as policy;
|
||||
|
|
@ -363,7 +363,7 @@ impl<G: Signer<Signature> + cyphernet::Ecdh> NodeHandle<G> {
|
|||
/// of the new commit, and the reference will be updated.
|
||||
///
|
||||
/// The `rad/sigrefs` are then updated to reflect the new change.
|
||||
pub fn commit_to(&self, rid: RepoId, refname: impl AsRef<git::RefStr>) {
|
||||
pub fn commit_to(&self, rid: RepoId, refname: impl AsRef<git::fmt::RefStr>) {
|
||||
use radicle::test::arbitrary;
|
||||
|
||||
let refname = refname.as_ref();
|
||||
|
|
@ -516,14 +516,14 @@ impl<G: cyphernet::Ecdh<Pk = NodeId> + Signer<Signature> + Clone> Node<G> {
|
|||
);
|
||||
|
||||
// Push local branches to storage.
|
||||
let mut refs = Vec::<(git::Qualified, git::Qualified)>::new();
|
||||
let mut refs = Vec::<(git::fmt::Qualified, git::fmt::Qualified)>::new();
|
||||
for branch in repo.branches(Some(git::raw::BranchType::Local)).unwrap() {
|
||||
let (branch, _) = branch.unwrap();
|
||||
let name = git::RefString::try_from(branch.name().unwrap().unwrap()).unwrap();
|
||||
let name = git::fmt::RefString::try_from(branch.name().unwrap().unwrap()).unwrap();
|
||||
|
||||
refs.push((
|
||||
git::lit::refs_heads(&name).into(),
|
||||
git::lit::refs_heads(&name).into(),
|
||||
git::fmt::lit::refs_heads(&name).into(),
|
||||
git::fmt::lit::refs_heads(&name).into(),
|
||||
));
|
||||
}
|
||||
git::push(repo, "rad", refs.iter().map(|(a, b)| (a, b))).unwrap();
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ impl<G: crypto::signature::Signer<crypto::Signature>> Peer<Storage, G> {
|
|||
&repo,
|
||||
name.try_into().unwrap(),
|
||||
description,
|
||||
radicle::git::refname!("master"),
|
||||
radicle::git::fmt::refname!("master"),
|
||||
Visibility::default(),
|
||||
self.signer(),
|
||||
self.storage(),
|
||||
|
|
|
|||
|
|
@ -1567,7 +1567,7 @@ fn test_queued_fetch_from_ann_same_rid() {
|
|||
let refname = carol
|
||||
.id()
|
||||
.to_namespace()
|
||||
.join(git::refname!("refs/sigrefs"));
|
||||
.join(git::fmt::refname!("refs/sigrefs"));
|
||||
|
||||
// Finish the 1st fetch.
|
||||
// Ensure the ref is in the storage and cache.
|
||||
|
|
@ -1749,7 +1749,7 @@ fn test_init_and_seed() {
|
|||
&repo,
|
||||
"alice".try_into().unwrap(),
|
||||
"alice's repo",
|
||||
git::refname!("master"),
|
||||
git::fmt::refname!("master"),
|
||||
Visibility::default(),
|
||||
alice.signer(),
|
||||
alice.storage(),
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use std::{collections::HashSet, thread, time};
|
|||
use radicle::cob::Title;
|
||||
use test_log::test;
|
||||
|
||||
use radicle::git::raw::ErrorExt as _;
|
||||
use radicle::node::device::Device;
|
||||
use radicle::node::policy::Scope;
|
||||
use radicle::node::Event;
|
||||
|
|
@ -248,7 +249,7 @@ fn test_replication_ref_in_sigrefs() {
|
|||
bob.storage
|
||||
.repository_mut(acme)
|
||||
.unwrap()
|
||||
.reference(&bob.id, &git::qualified!("refs/heads/master"))
|
||||
.reference(&bob.id, &git::fmt::qualified!("refs/heads/master"))
|
||||
.unwrap()
|
||||
.delete()
|
||||
.unwrap();
|
||||
|
|
@ -271,7 +272,7 @@ fn test_replication_ref_in_sigrefs() {
|
|||
.storage
|
||||
.repository(acme)
|
||||
.unwrap()
|
||||
.reference(&bob.id, &git::qualified!("refs/heads/master"))
|
||||
.reference(&bob.id, &git::fmt::qualified!("refs/heads/master"))
|
||||
.is_ok(),
|
||||
"refs/namespaces/{}/refs/heads/master does not exist",
|
||||
bob.id
|
||||
|
|
@ -292,8 +293,8 @@ fn test_replication_invalid() {
|
|||
// Create some unsigned refs for Carol in Bob's storage.
|
||||
repo.raw()
|
||||
.reference(
|
||||
&git::qualified!("refs/heads/carol").with_namespace(carol.public_key().into()),
|
||||
*head,
|
||||
&git::fmt::qualified!("refs/heads/carol").with_namespace(carol.public_key().into()),
|
||||
head.into(),
|
||||
true,
|
||||
&String::default(),
|
||||
)
|
||||
|
|
@ -579,7 +580,7 @@ fn test_clone() {
|
|||
.canonical_head()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(oid, *canonical);
|
||||
assert_eq!(canonical, oid);
|
||||
|
||||
// Make sure that bob has refs/rad/id set
|
||||
assert!(bob
|
||||
|
|
@ -1193,7 +1194,6 @@ fn missing_default_branch() {
|
|||
|
||||
#[test]
|
||||
fn missing_delegate_default_branch() {
|
||||
use radicle::git::raw;
|
||||
use radicle::identity::Identity;
|
||||
use radicle::storage::git::Repository;
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
|
|
@ -1238,7 +1238,7 @@ fn missing_delegate_default_branch() {
|
|||
);
|
||||
assert!(matches!(
|
||||
default_branch,
|
||||
Err(radicle::git::Error::Git(e)) if e.code() == raw::ErrorCode::NotFound
|
||||
Err(e) if e.is_not_found()
|
||||
));
|
||||
};
|
||||
|
||||
|
|
@ -1528,10 +1528,10 @@ fn test_fetch_emits_canonical_ref_update() {
|
|||
let result = bob.handle.fetch(rid, alice.id, DEFAULT_TIMEOUT).unwrap();
|
||||
assert!(result.is_success());
|
||||
|
||||
let default_branch: git::Qualified = {
|
||||
let default_branch: git::fmt::Qualified = {
|
||||
let repo = alice.storage.repository(rid).unwrap();
|
||||
let proj = repo.project().unwrap();
|
||||
git::lit::refs_heads(proj.default_branch()).into()
|
||||
git::fmt::lit::refs_heads(proj.default_branch()).into()
|
||||
};
|
||||
alice.commit_to(rid, &default_branch);
|
||||
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ fn notify(
|
|||
// for sigref verification.
|
||||
continue;
|
||||
}
|
||||
if let Some(rest) = r.strip_prefix(git::refname!("refs/heads/patches")) {
|
||||
if let Some(rest) = r.strip_prefix(git::fmt::refname!("refs/heads/patches")) {
|
||||
if radicle::cob::ObjectId::from_str(rest.as_str()).is_ok() {
|
||||
// Don't notify about patch branches, since we already get
|
||||
// notifications about patch updates.
|
||||
|
|
@ -400,7 +400,7 @@ fn set_canonical_refs(
|
|||
let oid = object.id();
|
||||
if let Err(e) = repo.backend.reference(
|
||||
refname.clone().as_str(),
|
||||
*oid,
|
||||
oid.into(),
|
||||
true,
|
||||
"set-canonical-reference from fetch (radicle)",
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ nonempty = { workspace = true, features = ["serialize"] }
|
|||
qcheck = { workspace = true, optional = true }
|
||||
radicle = { workspace = true, features = ["logger"] }
|
||||
radicle-fetch = { workspace = true }
|
||||
radicle-git-ext = { workspace = true, features = ["serde"] }
|
||||
sqlite = { workspace = true, features = ["bundled"] }
|
||||
scrypt = { version = "0.11.0", default-features = false }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
|
|
|||
|
|
@ -186,8 +186,6 @@ pub enum Error {
|
|||
#[error(transparent)]
|
||||
Git(#[from] radicle::git::raw::Error),
|
||||
#[error(transparent)]
|
||||
GitExt(#[from] radicle::git::ext::Error),
|
||||
#[error(transparent)]
|
||||
Storage(#[from] storage::Error),
|
||||
#[error(transparent)]
|
||||
Gossip(#[from] gossip::Error),
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ use cyphernet::addr::tor;
|
|||
use radicle::crypto::{PublicKey, Signature, Unverified};
|
||||
use radicle::git;
|
||||
use radicle::git::fmt;
|
||||
use radicle::git::raw;
|
||||
use radicle::identity::RepoId;
|
||||
use radicle::node;
|
||||
use radicle::node::Alias;
|
||||
|
|
@ -285,7 +286,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl Encode for git::RefString {
|
||||
impl Encode for git::fmt::RefString {
|
||||
fn encode(&self, buf: &mut impl BufMut) {
|
||||
self.as_str().encode(buf)
|
||||
}
|
||||
|
|
@ -300,7 +301,8 @@ impl Encode for Signature {
|
|||
impl Encode for git::Oid {
|
||||
fn encode(&self, buf: &mut impl BufMut) {
|
||||
// Nb. We use length-encoding here to support future SHA-2 object ids.
|
||||
self.as_bytes().encode(buf)
|
||||
let bytes: &[u8] = self.as_ref();
|
||||
bytes.encode(buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -321,7 +323,7 @@ impl Decode for Refs {
|
|||
|
||||
for _ in 0..len {
|
||||
let name = String::decode(buf)?;
|
||||
let name = git::RefString::try_from(name).map_err(Invalid::from)?;
|
||||
let name = git::fmt::RefString::try_from(name).map_err(Invalid::from)?;
|
||||
let oid = git::Oid::decode(buf)?;
|
||||
|
||||
refs.insert(name, oid);
|
||||
|
|
@ -330,10 +332,10 @@ impl Decode for Refs {
|
|||
}
|
||||
}
|
||||
|
||||
impl Decode for git::RefString {
|
||||
impl Decode for git::fmt::RefString {
|
||||
fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
|
||||
let ref_str = String::decode(buf)?;
|
||||
Ok(git::RefString::try_from(ref_str).map_err(Invalid::from)?)
|
||||
Ok(git::fmt::RefString::try_from(ref_str).map_err(Invalid::from)?)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -365,7 +367,7 @@ where
|
|||
|
||||
impl Decode for git::Oid {
|
||||
fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
|
||||
const LEN_EXPECTED: usize = mem::size_of::<git::raw::Oid>();
|
||||
const LEN_EXPECTED: usize = mem::size_of::<raw::Oid>();
|
||||
|
||||
let len = Size::decode(buf)? as usize;
|
||||
|
||||
|
|
@ -378,7 +380,7 @@ impl Decode for git::Oid {
|
|||
}
|
||||
|
||||
let buf: [u8; LEN_EXPECTED] = Decode::decode(buf)?;
|
||||
let oid = git::raw::Oid::from_bytes(&buf).expect("the buffer is exactly the right size");
|
||||
let oid = raw::Oid::from_bytes(&buf).expect("the buffer is exactly the right size");
|
||||
let oid = git::Oid::from(oid);
|
||||
|
||||
Ok(oid)
|
||||
|
|
@ -627,7 +629,7 @@ mod tests {
|
|||
|
||||
#[quickcheck]
|
||||
fn prop_oid(input: [u8; 20]) {
|
||||
roundtrip(git::Oid::try_from(input.as_slice()).unwrap());
|
||||
roundtrip(git::Oid::from_sha1(input));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -36,12 +36,12 @@ impl FetchResult {
|
|||
/// corresponding targets.
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct UpdatedCanonicalRefs {
|
||||
inner: BTreeMap<git::Qualified<'static>, git::Oid>,
|
||||
inner: BTreeMap<git::fmt::Qualified<'static>, git::Oid>,
|
||||
}
|
||||
|
||||
impl IntoIterator for UpdatedCanonicalRefs {
|
||||
type Item = (git::Qualified<'static>, git::Oid);
|
||||
type IntoIter = std::collections::btree_map::IntoIter<git::Qualified<'static>, git::Oid>;
|
||||
type Item = (git::fmt::Qualified<'static>, git::Oid);
|
||||
type IntoIter = std::collections::btree_map::IntoIter<git::fmt::Qualified<'static>, git::Oid>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.inner.into_iter()
|
||||
|
|
@ -51,12 +51,12 @@ impl IntoIterator for UpdatedCanonicalRefs {
|
|||
impl UpdatedCanonicalRefs {
|
||||
/// Insert a new updated entry for the canonical reference identified by
|
||||
/// `refname` and its new `target.`
|
||||
pub fn updated(&mut self, refname: git::Qualified<'static>, target: git::Oid) {
|
||||
pub fn updated(&mut self, refname: git::fmt::Qualified<'static>, target: git::Oid) {
|
||||
self.inner.insert(refname, target);
|
||||
}
|
||||
|
||||
/// Return an iterator of all the updates.
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&git::Qualified<'static>, &git::Oid)> {
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&git::fmt::Qualified<'static>, &git::Oid)> {
|
||||
self.inner.iter()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::io;
|
|||
|
||||
use thiserror::Error;
|
||||
|
||||
use radicle::{cob, git, identity, storage};
|
||||
use radicle::{cob, git::raw, identity, storage};
|
||||
use radicle_fetch as fetch;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
@ -10,7 +10,7 @@ pub enum Fetch {
|
|||
#[error(transparent)]
|
||||
Run(#[from] fetch::Error),
|
||||
#[error(transparent)]
|
||||
Git(#[from] git::raw::Error),
|
||||
Git(#[from] raw::Error),
|
||||
#[error(transparent)]
|
||||
Storage(#[from] storage::Error),
|
||||
#[error(transparent)]
|
||||
|
|
|
|||
|
|
@ -19,5 +19,4 @@ log = { workspace = true }
|
|||
radicle = { workspace = true }
|
||||
radicle-cli = { workspace = true }
|
||||
radicle-crypto = { workspace = true }
|
||||
radicle-git-ext = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
|
@ -19,9 +19,9 @@ pub enum Error {
|
|||
/// Invalid reference name.
|
||||
#[error("invalid ref: {0}")]
|
||||
InvalidRef(#[from] radicle::git::fmt::Error),
|
||||
/// Git error.
|
||||
#[error("git: {0}")]
|
||||
InvalidOid(#[source] git::raw::Error),
|
||||
/// Invalid object ID.
|
||||
#[error("invalid oid: {0}")]
|
||||
InvalidOid(#[from] radicle::git::ParseOidError),
|
||||
|
||||
/// Error fetching pack from storage to working copy.
|
||||
#[error("`git fetch-pack` failed with exit status {status}, stderr and stdout follow:\n{stderr}\n{stdout}")]
|
||||
|
|
@ -34,7 +34,7 @@ pub enum Error {
|
|||
|
||||
/// Run a git fetch command.
|
||||
pub fn run<R: ReadRepository>(
|
||||
mut refs: Vec<(git::Oid, git::RefString)>,
|
||||
mut refs: Vec<(git::Oid, git::fmt::RefString)>,
|
||||
stored: R,
|
||||
stdin: &io::Stdin,
|
||||
verbosity: Verbosity,
|
||||
|
|
@ -45,8 +45,8 @@ pub fn run<R: ReadRepository>(
|
|||
let tokens = read_line(stdin, &mut line)?;
|
||||
match tokens.as_slice() {
|
||||
["fetch", oid, refstr] => {
|
||||
let oid = git::Oid::from_str(oid).map_err(Error::InvalidOid)?;
|
||||
let refstr = git::RefString::try_from(*refstr)?;
|
||||
let oid = git::Oid::from_str(oid)?;
|
||||
let refstr = git::fmt::RefString::try_from(*refstr)?;
|
||||
|
||||
refs.push((oid, refstr));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ pub enum Error {
|
|||
Identity(#[from] radicle::identity::DocError),
|
||||
/// Git error.
|
||||
#[error(transparent)]
|
||||
Git(#[from] radicle::git::ext::Error),
|
||||
Git(#[from] radicle::git::raw::Error),
|
||||
/// Profile error.
|
||||
#[error(transparent)]
|
||||
Profile(#[from] profile::Error),
|
||||
|
|
@ -56,8 +56,8 @@ pub fn for_fetch<R: ReadRepository + cob::Store<Namespace = NodeId> + 'static>(
|
|||
// List canonical references.
|
||||
// Skip over `refs/rad/*`, since those are not meant to be fetched into a working copy.
|
||||
for glob in [
|
||||
git::refspec::pattern!("refs/heads/*"),
|
||||
git::refspec::pattern!("refs/tags/*"),
|
||||
git::fmt::pattern!("refs/heads/*"),
|
||||
git::fmt::pattern!("refs/tags/*"),
|
||||
] {
|
||||
for (name, oid) in stored.references_glob(&glob)? {
|
||||
println!("{oid} {name}");
|
||||
|
|
@ -81,8 +81,8 @@ pub fn for_push<R: ReadRepository>(profile: &Profile, stored: &R) -> Result<(),
|
|||
// Only our own refs can be pushed to.
|
||||
for (name, oid) in stored.references_of(profile.id())? {
|
||||
// Only branches and tags can be pushed to.
|
||||
if name.starts_with(git::refname!("refs/heads").as_str())
|
||||
|| name.starts_with(git::refname!("refs/tags").as_str())
|
||||
if name.starts_with(git::fmt::refname!("refs/heads").as_str())
|
||||
|| name.starts_with(git::fmt::refname!("refs/tags").as_str())
|
||||
{
|
||||
println!("{oid} {name}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,6 +117,9 @@ pub enum Error {
|
|||
/// List error.
|
||||
#[error(transparent)]
|
||||
List(#[from] list::Error),
|
||||
/// Invalid object ID.
|
||||
#[error("invalid oid: {0}")]
|
||||
InvalidOid(#[from] radicle::git::ParseOidError),
|
||||
}
|
||||
|
||||
/// Models values for the `verbosity` option, see
|
||||
|
|
@ -162,13 +165,13 @@ pub enum Branch {
|
|||
/// Create a branch with the same name as the upstream branch (i.e. `patches/<patch id>`).
|
||||
MirrorUpstream,
|
||||
/// Create a branch with the provided name.
|
||||
Provided(git::RefString),
|
||||
Provided(git::fmt::RefString),
|
||||
}
|
||||
|
||||
impl Branch {
|
||||
/// Return the branch name to be used for the local branch when creating a
|
||||
/// patch.
|
||||
pub fn to_branch_name(self, object: &radicle::patch::PatchId) -> Option<git::Qualified> {
|
||||
pub fn to_branch_name(self, object: &radicle::patch::PatchId) -> Option<git::fmt::Qualified> {
|
||||
match self {
|
||||
Self::None => None,
|
||||
Self::MirrorUpstream => Some(git::refs::patch(object)),
|
||||
|
|
@ -207,12 +210,15 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> {
|
|||
// module is aware of that.
|
||||
cli::Paint::set_terminal(cli::TerminalFile::Stderr);
|
||||
|
||||
let (remote, url): (Option<git::RefString>, Url) = {
|
||||
let (remote, url): (Option<git::fmt::RefString>, Url) = {
|
||||
let args = env::args().skip(1).take(2).collect::<Vec<_>>();
|
||||
|
||||
match args.as_slice() {
|
||||
[url] => (None, url.parse()?),
|
||||
[remote, url] => (git::RefString::try_from(remote.as_str()).ok(), url.parse()?),
|
||||
[remote, url] => (
|
||||
git::fmt::RefString::try_from(remote.as_str()).ok(),
|
||||
url.parse()?,
|
||||
),
|
||||
|
||||
_ => {
|
||||
return Err(Error::InvalidArguments(args));
|
||||
|
|
@ -274,7 +280,7 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> {
|
|||
}
|
||||
["fetch", oid, refstr] => {
|
||||
let oid = git::Oid::from_str(oid)?;
|
||||
let refstr = git::RefString::try_from(*refstr)?;
|
||||
let refstr = git::fmt::RefString::try_from(*refstr)?;
|
||||
|
||||
return Ok(fetch::run(
|
||||
vec![(oid, refstr)],
|
||||
|
|
@ -347,7 +353,9 @@ fn push_option(args: &[&str], opts: &mut Options) -> Result<(), Error> {
|
|||
|
||||
opts.base = Some(git::Oid::from(commit.id()));
|
||||
}
|
||||
"patch.branch" => opts.branch = Branch::Provided(git::RefString::try_from(val)?),
|
||||
"patch.branch" => {
|
||||
opts.branch = Branch::Provided(git::fmt::RefString::try_from(val)?)
|
||||
}
|
||||
other => {
|
||||
return Err(Error::UnsupportedPushOption(other.to_owned()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ pub enum Error {
|
|||
NoKey,
|
||||
/// User tried to delete the canonical branch.
|
||||
#[error("refusing to delete default branch ref '{0}'")]
|
||||
DeleteForbidden(git::RefString),
|
||||
DeleteForbidden(git::fmt::RefString),
|
||||
/// Identity document error.
|
||||
#[error("doc: {0}")]
|
||||
Doc(#[from] radicle::identity::doc::DocError),
|
||||
|
|
@ -65,9 +65,6 @@ pub enum Error {
|
|||
/// Git error.
|
||||
#[error("git: {0}")]
|
||||
Git(#[from] git::raw::Error),
|
||||
/// Git extension error.
|
||||
#[error("git: {0}")]
|
||||
GitExt(#[from] git::ext::Error),
|
||||
/// Storage error.
|
||||
#[error(transparent)]
|
||||
Storage(#[from] radicle::storage::Error),
|
||||
|
|
@ -136,9 +133,9 @@ pub enum Error {
|
|||
/// Push command.
|
||||
enum Command {
|
||||
/// Update ref.
|
||||
Push(git::Refspec<git::Oid, git::RefString>),
|
||||
Push(git::fmt::refspec::Refspec<git::Oid, git::fmt::RefString>),
|
||||
/// Delete ref.
|
||||
Delete(git::RefString),
|
||||
Delete(git::fmt::RefString),
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
|
@ -173,7 +170,7 @@ impl Command {
|
|||
let Some((src, dst)) = s.split_once(':') else {
|
||||
return Err(CommandError::Empty { rev: s.to_string() });
|
||||
};
|
||||
let dst = git::RefString::try_from(dst).map_err(|err| CommandError::Delete {
|
||||
let dst = git::fmt::RefString::try_from(dst).map_err(|err| CommandError::Delete {
|
||||
rev: dst.to_string(),
|
||||
err,
|
||||
})?;
|
||||
|
|
@ -195,12 +192,12 @@ impl Command {
|
|||
.id()
|
||||
.into();
|
||||
|
||||
Ok(Self::Push(git::Refspec { src, dst, force }))
|
||||
Ok(Self::Push(git::fmt::refspec::Refspec { src, dst, force }))
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the destination refname.
|
||||
fn dst(&self) -> &git::RefStr {
|
||||
fn dst(&self) -> &git::fmt::RefStr {
|
||||
match self {
|
||||
Self::Push(rs) => rs.dst.as_refstr(),
|
||||
Self::Delete(rs) => rs,
|
||||
|
|
@ -211,30 +208,30 @@ impl Command {
|
|||
enum PushAction {
|
||||
OpenPatch,
|
||||
UpdatePatch {
|
||||
dst: git::Qualified<'static>,
|
||||
dst: git::fmt::Qualified<'static>,
|
||||
patch: patch::PatchId,
|
||||
},
|
||||
PushRef {
|
||||
dst: git::Qualified<'static>,
|
||||
dst: git::fmt::Qualified<'static>,
|
||||
},
|
||||
}
|
||||
|
||||
impl PushAction {
|
||||
fn new(dst: &git::RefString) -> Result<Self, error::PushAction> {
|
||||
fn new(dst: &git::fmt::RefString) -> Result<Self, error::PushAction> {
|
||||
if dst == &*rad::PATCHES_REFNAME {
|
||||
Ok(Self::OpenPatch)
|
||||
} else {
|
||||
let dst = git::Qualified::from_refstr(dst)
|
||||
let dst = git::fmt::Qualified::from_refstr(dst)
|
||||
.ok_or_else(|| error::PushAction::InvalidRef {
|
||||
refname: dst.clone(),
|
||||
})?
|
||||
.to_owned();
|
||||
|
||||
if let Some(oid) = dst.strip_prefix(git::refname!("refs/heads/patches")) {
|
||||
if let Some(oid) = dst.strip_prefix(git::fmt::refname!("refs/heads/patches")) {
|
||||
let patch = git::Oid::from_str(oid)
|
||||
.map_err(|err| error::PushAction::InvalidPatchId {
|
||||
.map_err(|source| error::PushAction::InvalidPatchId {
|
||||
suffix: oid.to_string(),
|
||||
source: err,
|
||||
source,
|
||||
})
|
||||
.map(patch::PatchId::from)?;
|
||||
Ok(Self::UpdatePatch { dst, patch })
|
||||
|
|
@ -248,7 +245,7 @@ impl PushAction {
|
|||
/// Run a git push command.
|
||||
pub fn run(
|
||||
mut specs: Vec<String>,
|
||||
remote: Option<git::RefString>,
|
||||
remote: Option<git::fmt::RefString>,
|
||||
url: Url,
|
||||
stored: &storage::git::Repository,
|
||||
profile: &Profile,
|
||||
|
|
@ -290,7 +287,7 @@ pub fn run(
|
|||
let identity = stored.identity()?;
|
||||
let project = identity.project()?;
|
||||
let canonical_ref = git::refs::branch(project.default_branch());
|
||||
let mut set_canonical_refs: Vec<(git::Qualified, git::canonical::Object)> =
|
||||
let mut set_canonical_refs: Vec<(git::fmt::Qualified, git::canonical::Object)> =
|
||||
Vec::with_capacity(specs.len());
|
||||
|
||||
// Rely on the environment variable `GIT_DIR`.
|
||||
|
|
@ -317,7 +314,7 @@ pub fn run(
|
|||
.map(|_| None)
|
||||
.map_err(Error::from)
|
||||
}
|
||||
Command::Push(git::Refspec { src, dst, force }) => {
|
||||
Command::Push(git::fmt::refspec::Refspec { src, dst, force }) => {
|
||||
let patches = crate::patches_mut(profile, stored)?;
|
||||
let action = PushAction::new(dst)?;
|
||||
|
||||
|
|
@ -373,7 +370,7 @@ pub fn run(
|
|||
// canonical branch.
|
||||
if let Some(canonical) = rules.canonical(dst.clone(), stored) {
|
||||
let object = working
|
||||
.find_object(**src, None)
|
||||
.find_object(src.into(), None)
|
||||
.map(|obj| git::canonical::Object::new(&obj))?
|
||||
.ok_or(Error::UnknownObjectType { oid: *src })?;
|
||||
|
||||
|
|
@ -429,10 +426,10 @@ pub fn run(
|
|||
}
|
||||
|
||||
match stored.backend.refname_to_id(refname.as_str()) {
|
||||
Ok(new) if new != *oid => {
|
||||
Ok(new) if oid != new => {
|
||||
stored.backend.reference(
|
||||
refname.as_str(),
|
||||
*oid,
|
||||
oid.into(),
|
||||
true,
|
||||
"set-canonical-reference from git-push (radicle)",
|
||||
)?;
|
||||
|
|
@ -441,7 +438,7 @@ pub fn run(
|
|||
Err(e) if e.code() == git::raw::ErrorCode::NotFound => {
|
||||
stored.backend.reference(
|
||||
refname.as_str(),
|
||||
*oid,
|
||||
oid.into(),
|
||||
true,
|
||||
"set-canonical-reference from git-push (radicle)",
|
||||
)?;
|
||||
|
|
@ -505,7 +502,7 @@ fn patch_base(
|
|||
/// [`Drop::drop`].
|
||||
struct TempPatchRef<'a> {
|
||||
stored: &'a storage::git::Repository,
|
||||
reference: git::Namespaced<'a>,
|
||||
reference: git::fmt::Namespaced<'a>,
|
||||
}
|
||||
|
||||
impl<'a> TempPatchRef<'a> {
|
||||
|
|
@ -539,7 +536,7 @@ impl<'a> Drop for TempPatchRef<'a> {
|
|||
/// Open a new patch.
|
||||
fn patch_open<G>(
|
||||
head: &git::Oid,
|
||||
upstream: &Option<git::RefString>,
|
||||
upstream: &Option<git::fmt::RefString>,
|
||||
nid: &NodeId,
|
||||
working: &git::raw::Repository,
|
||||
stored: &storage::git::Repository,
|
||||
|
|
@ -563,7 +560,7 @@ where
|
|||
}
|
||||
|
||||
let (title, description) =
|
||||
term::patch::get_create_message(opts.message, &stored.backend, &base, head)?;
|
||||
term::patch::get_create_message(opts.message, &stored.backend, &base.into(), &head.into())?;
|
||||
|
||||
let patch = if opts.draft {
|
||||
patches.draft(
|
||||
|
|
@ -607,24 +604,29 @@ where
|
|||
let refname = git::refs::patch(&patch).with_namespace(nid.into());
|
||||
let _ = stored.raw().reference(
|
||||
refname.as_str(),
|
||||
**head,
|
||||
head.into(),
|
||||
true,
|
||||
"Create reference for patch head",
|
||||
)?;
|
||||
|
||||
if let Some(upstream) = upstream {
|
||||
if let Some(local_branch) = opts.branch.to_branch_name(&patch) {
|
||||
fn strip_refs_heads(qualified: git::Qualified) -> git::RefString {
|
||||
fn strip_refs_heads(qualified: git::fmt::Qualified) -> git::fmt::RefString {
|
||||
let (_refs, _heads, x, xs) = qualified.non_empty_components();
|
||||
std::iter::once(x).chain(xs).collect()
|
||||
}
|
||||
|
||||
working.reference(&local_branch, **head, true, "Create local branch for patch")?;
|
||||
working.reference(
|
||||
&local_branch,
|
||||
head.into(),
|
||||
true,
|
||||
"Create local branch for patch",
|
||||
)?;
|
||||
|
||||
let remote_branch = git::refs::workdir::patch_upstream(&patch);
|
||||
let remote_branch = working.reference(
|
||||
&remote_branch,
|
||||
**head,
|
||||
head.into(),
|
||||
true,
|
||||
"Create remote tracking branch for patch",
|
||||
)?;
|
||||
|
|
@ -667,7 +669,7 @@ where
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
fn patch_update<G>(
|
||||
head: &git::Oid,
|
||||
dst: &git::Qualified,
|
||||
dst: &git::fmt::Qualified,
|
||||
force: bool,
|
||||
patch_id: patch::PatchId,
|
||||
nid: &NodeId,
|
||||
|
|
@ -703,7 +705,8 @@ where
|
|||
let (latest_id, latest) = patch.latest();
|
||||
let latest = latest.clone();
|
||||
|
||||
let message = term::patch::get_update_message(opts.message, &stored.backend, &latest, head)?;
|
||||
let message =
|
||||
term::patch::get_update_message(opts.message, &stored.backend, &latest, &head.into())?;
|
||||
|
||||
let dst = dst.with_namespace(nid.into());
|
||||
push_ref(head, &dst, force, stored.raw(), opts.verbosity)?;
|
||||
|
|
@ -744,7 +747,7 @@ where
|
|||
|
||||
fn push<G>(
|
||||
src: &git::Oid,
|
||||
dst: &git::Qualified,
|
||||
dst: &git::fmt::Qualified,
|
||||
force: bool,
|
||||
nid: &NodeId,
|
||||
working: &git::raw::Repository,
|
||||
|
|
@ -768,7 +771,7 @@ where
|
|||
|
||||
if let Some(old) = old {
|
||||
let proj = stored.project()?;
|
||||
let master = &*git::Qualified::from(git::lit::refs_heads(proj.default_branch()));
|
||||
let master = &*git::fmt::Qualified::from(git::fmt::lit::refs_heads(proj.default_branch()));
|
||||
|
||||
// If we're pushing to the project's default branch, we want to see if any patches got
|
||||
// merged or reverted, and if so, update the patch COB.
|
||||
|
|
@ -800,8 +803,8 @@ where
|
|||
{
|
||||
// Find all commits reachable from the old OID but not from the new OID.
|
||||
let mut revwalk = stored.revwalk()?;
|
||||
revwalk.push(*old)?;
|
||||
revwalk.hide(*new)?;
|
||||
revwalk.push(old.into())?;
|
||||
revwalk.hide(new.into())?;
|
||||
|
||||
// List of commits that have been dropped.
|
||||
let dropped = revwalk
|
||||
|
|
@ -943,7 +946,7 @@ where
|
|||
/// Push a single reference to storage.
|
||||
fn push_ref(
|
||||
src: &git::Oid,
|
||||
dst: &git::Namespaced,
|
||||
dst: &git::fmt::Namespaced,
|
||||
force: bool,
|
||||
stored: &git::raw::Repository,
|
||||
verbosity: Verbosity,
|
||||
|
|
@ -951,7 +954,7 @@ fn push_ref(
|
|||
let path = dunce::canonicalize(stored.path())?.display().to_string();
|
||||
// Nb. The *force* indicator (`+`) is processed by Git tooling before we even reach this code.
|
||||
// This happens during the `list for-push` phase.
|
||||
let refspec = git::Refspec { src, dst, force };
|
||||
let refspec = git::fmt::refspec::Refspec { src, dst, force };
|
||||
|
||||
let mut args = vec!["send-pack".to_string()];
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ where
|
|||
/// copy, and that checks that any two commits are related in the graph.
|
||||
///
|
||||
/// Ensures that the new head and the canonical commit do not diverge.
|
||||
pub fn quorum(self) -> Result<(git::Qualified<'a>, canonical::Object), QuorumError> {
|
||||
pub fn quorum(self) -> Result<(git::fmt::Qualified<'a>, canonical::Object), QuorumError> {
|
||||
self.canonical
|
||||
.quorum()
|
||||
.map(|QuorumWithConvergence { quorum, .. }| (quorum.refname, quorum.object))
|
||||
|
|
|
|||
|
|
@ -37,10 +37,10 @@ pub struct HeadsDiverge {
|
|||
#[derive(Debug, Error)]
|
||||
pub enum PushAction {
|
||||
#[error("invalid reference {refname}, expected qualified reference starting with `refs/`")]
|
||||
InvalidRef { refname: git::RefString },
|
||||
#[error("found refs/heads/patches/{suffix} where {suffix} was an invalid Patch ID")]
|
||||
InvalidRef { refname: git::fmt::RefString },
|
||||
#[error("found refs/heads/patches/{suffix} where {suffix} was an invalid Patch ID: {source}")]
|
||||
InvalidPatchId {
|
||||
suffix: String,
|
||||
source: git::raw::Error,
|
||||
source: radicle::git::ParseOidError,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Changed
|
||||
|
||||
- Re-exports from `git2` at `radicle::git::raw` were limited, using
|
||||
the heartwood workspace as a filter. Dependents that require members that
|
||||
are not exported anymore will have to depend on `git2` directly.
|
||||
- Some re-exports from `git-ref-format-core` were moved from `radicle::git`
|
||||
to `radicle::fmt`.
|
||||
- The crate now re-exports `radicle::git::Oid` from a new `radicle-oid` crate,
|
||||
in an effort to decrease dependence on `git2` via `radicle-git-ext`. This
|
||||
new object identifier type does not implement `Deref` anymore. Use `Into`
|
||||
to convert to a `git2::Oid` as necessary.
|
||||
- Re-exports of `radicle-git-ext` were removed, as this dependency is removed.
|
||||
Instead of `radicle_git_ext::Error`, use `git2::Error` (re-exported as
|
||||
`radicle::git::raw::Error`) together with the new extension trait
|
||||
`radicle::git::raw::ErrorExt`.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- `radicle::node::Handle::announce_refs` is deprecated in favor of
|
||||
|
|
|
|||
|
|
@ -11,8 +11,9 @@ rust-version.workspace = true
|
|||
|
||||
[features]
|
||||
default = []
|
||||
test = ["tempfile", "qcheck", "radicle-crypto/test"]
|
||||
test = ["tempfile", "qcheck", "radicle-crypto/test", "radicle-cob/test"]
|
||||
logger = ["colored", "chrono"]
|
||||
schemars = ["radicle-oid/schemars", "dep:schemars"]
|
||||
|
||||
[dependencies]
|
||||
amplify = { workspace = true, features = ["std"] }
|
||||
|
|
@ -32,9 +33,10 @@ log = { workspace = true, features = ["std"] }
|
|||
multibase = { workspace = true }
|
||||
nonempty = { workspace = true, features = ["serialize"] }
|
||||
qcheck = { workspace = true, optional = true }
|
||||
radicle-cob = { workspace = true }
|
||||
radicle-cob = { workspace = true, features = ["git2"] }
|
||||
radicle-crypto = { workspace = true, features = ["git-ref-format-core", "ssh", "sqlite", "cyphernet"] }
|
||||
radicle-git-ext = { workspace = true, features = ["serde"] }
|
||||
radicle-git-ref-format = { workspace = true, features = ["macro", "serde"] }
|
||||
radicle-oid = { workspace = true, features = ["git2", "serde", "std", "sha1"] }
|
||||
radicle-ssh = { workspace = true }
|
||||
schemars = { workspace = true, optional = true, features = ["derive", "std"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
|
@ -58,6 +60,7 @@ jsonschema = { version = "0.30", default-features = false }
|
|||
pretty_assertions = { workspace = true }
|
||||
qcheck = { workspace = true }
|
||||
qcheck-macros = { workspace = true }
|
||||
radicle-cob = { workspace = true, features = ["stable-commit-ids"] }
|
||||
radicle-cob = { workspace = true, features = ["stable-commit-ids", "test"] }
|
||||
radicle-crypto = { workspace = true, features = ["test"] }
|
||||
radicle-git-metadata = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ pub mod thread;
|
|||
#[cfg(test)]
|
||||
pub mod test;
|
||||
|
||||
#[cfg(test)]
|
||||
pub use radicle_cob::stable;
|
||||
|
||||
pub use cache::{migrate, MigrateCallback};
|
||||
pub use common::*;
|
||||
pub use op::{ActorId, Op};
|
||||
|
|
@ -21,7 +24,7 @@ pub use radicle_cob::{
|
|||
CollaborativeObject, Contents, Create, Embed, Entry, Evaluate, History, Manifest, ObjectId,
|
||||
Store, TypeName, Update, Updated, Version,
|
||||
};
|
||||
pub use radicle_cob::{create, get, git, list, remove, update};
|
||||
pub use radicle_cob::{create, get, list, remove, update};
|
||||
|
||||
/// The exact identifier for a particular COB.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize)]
|
||||
|
|
@ -66,17 +69,17 @@ impl TypedId {
|
|||
self.type_name == *identity::TYPENAME
|
||||
}
|
||||
|
||||
/// Parse a [`crate::git::Namespaced`] refname into a [`TypedId`].
|
||||
/// Parse a [`crate::git::fmt::Namespaced`] refname into a [`TypedId`].
|
||||
///
|
||||
/// All namespaces are stripped before parsing the suffix for the
|
||||
/// [`TypedId`] (see [`TypedId::from_qualified`]).
|
||||
pub fn from_namespaced(
|
||||
n: &crate::git::Namespaced,
|
||||
n: &crate::git::fmt::Namespaced,
|
||||
) -> Result<Option<Self>, ParseIdentifierError> {
|
||||
Self::from_qualified(&n.strip_namespace_recursive())
|
||||
}
|
||||
|
||||
/// Parse a [`crate::git::Qualified`] refname into a [`TypedId`].
|
||||
/// Parse a [`crate::git::fmt::Qualified`] refname into a [`TypedId`].
|
||||
///
|
||||
/// The refname is expected to be of the form:
|
||||
/// `refs/cobs/<type name>/<object id>`
|
||||
|
|
@ -87,7 +90,9 @@ impl TypedId {
|
|||
///
|
||||
/// This will fail if the refname is of the correct form, but the
|
||||
/// type name or object id fail to parse.
|
||||
pub fn from_qualified(q: &crate::git::Qualified) -> Result<Option<Self>, ParseIdentifierError> {
|
||||
pub fn from_qualified(
|
||||
q: &crate::git::fmt::Qualified,
|
||||
) -> Result<Option<Self>, ParseIdentifierError> {
|
||||
match q.non_empty_iter() {
|
||||
("refs", "cobs", type_name, mut id) => {
|
||||
let Some(id) = id.next() else {
|
||||
|
|
|
|||
|
|
@ -355,7 +355,7 @@ impl From<Oid> for Uri {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Uri> for Oid {
|
||||
impl TryFrom<&Uri> for crate::git::raw::Oid {
|
||||
type Error = Uri;
|
||||
|
||||
fn try_from(value: &Uri) -> Result<Self, Self::Error> {
|
||||
|
|
@ -368,6 +368,14 @@ impl TryFrom<&Uri> for Oid {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Uri> for crate::git::Oid {
|
||||
type Error = Uri;
|
||||
|
||||
fn try_from(value: &Uri) -> Result<Self, Self::Error> {
|
||||
crate::git::raw::Oid::try_from(value).map(crate::git::Oid::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Uri {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ impl<R: ReadRepository> Evaluate<R> for External {
|
|||
Self::from_root(Op::try_from(entry)?, store)
|
||||
}
|
||||
|
||||
fn apply<'a, I: Iterator<Item = (&'a radicle_git_ext::Oid, &'a radicle_cob::Entry)>>(
|
||||
fn apply<'a, I: Iterator<Item = (&'a crate::git::Oid, &'a radicle_cob::Entry)>>(
|
||||
&mut self,
|
||||
entry: &radicle_cob::Entry,
|
||||
concurrent: I,
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ use std::{fmt, ops::Deref, str::FromStr};
|
|||
|
||||
use crypto::{PublicKey, Signature};
|
||||
use radicle_cob::{Embed, ObjectId, TypeName};
|
||||
use radicle_git_ext as git_ext;
|
||||
use radicle_git_ext::Oid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::git;
|
||||
use crate::git::Oid;
|
||||
use crate::identity::doc::Doc;
|
||||
use crate::node::device::Device;
|
||||
use crate::node::NodeId;
|
||||
|
|
@ -126,9 +126,7 @@ pub enum ApplyError {
|
|||
#[error("document does not contain any changes to current identity")]
|
||||
DocUnchanged,
|
||||
#[error("git: {0}")]
|
||||
Git(#[from] crate::git::raw::Error),
|
||||
#[error("git: {0}")]
|
||||
GitExt(#[from] git_ext::Error),
|
||||
Git(#[from] git::raw::Error),
|
||||
#[error("identity document error: {0}")]
|
||||
Doc(#[from] DocError),
|
||||
}
|
||||
|
|
@ -1323,16 +1321,15 @@ mod test {
|
|||
.unwrap();
|
||||
|
||||
bob.repo.fetch(alice);
|
||||
let a3 = cob::git::stable::with_advanced_timestamp(|| {
|
||||
let a3 = cob::stable::with_advanced_timestamp(|| {
|
||||
alice_identity.redact(a2, &alice.signer).unwrap()
|
||||
});
|
||||
assert!(alice_identity.revision(&a1).is_some());
|
||||
assert_eq!(alice_identity.timeline, vec![a0, a1, a2, a3]);
|
||||
|
||||
let mut bob_identity = Identity::load_mut(&*bob.repo).unwrap();
|
||||
let b1 = cob::git::stable::with_advanced_timestamp(|| {
|
||||
bob_identity.accept(&a2, &bob.signer).unwrap()
|
||||
});
|
||||
let b1 =
|
||||
cob::stable::with_advanced_timestamp(|| bob_identity.accept(&a2, &bob.signer).unwrap());
|
||||
|
||||
assert_eq!(bob_identity.timeline, vec![a0, a1, a2, b1]);
|
||||
assert_eq!(bob_identity.revision(&a2).unwrap().state, State::Accepted);
|
||||
|
|
@ -1381,15 +1378,14 @@ mod test {
|
|||
eve.repo.fetch(bob);
|
||||
|
||||
let mut bob_identity = Identity::load_mut(&*bob.repo).unwrap();
|
||||
let b1 = cob::git::stable::with_advanced_timestamp(|| {
|
||||
bob_identity.accept(&a2, &bob.signer).unwrap()
|
||||
});
|
||||
let b1 =
|
||||
cob::stable::with_advanced_timestamp(|| bob_identity.accept(&a2, &bob.signer).unwrap());
|
||||
assert_eq!(bob_identity.current, a2);
|
||||
|
||||
let mut eve_identity = Identity::load_mut(&*eve.repo).unwrap();
|
||||
let mut eve_doc = eve_identity.doc().clone().edit();
|
||||
eve_doc.visibility = Visibility::private([eve.signer.public_key().into()]);
|
||||
let e1 = cob::git::stable::with_advanced_timestamp(|| {
|
||||
let e1 = cob::stable::with_advanced_timestamp(|| {
|
||||
eve_identity
|
||||
.update(
|
||||
cob::Title::new("Change visibility").unwrap(),
|
||||
|
|
@ -1458,15 +1454,13 @@ mod test {
|
|||
|
||||
// Bob accepts alice's revision.
|
||||
let mut bob_identity = Identity::load_mut(&*bob.repo).unwrap();
|
||||
let b1 = cob::git::stable::with_advanced_timestamp(|| {
|
||||
bob_identity.accept(&a2, &bob.signer).unwrap()
|
||||
});
|
||||
let b1 =
|
||||
cob::stable::with_advanced_timestamp(|| bob_identity.accept(&a2, &bob.signer).unwrap());
|
||||
|
||||
// Eve rejects the revision, not knowing.
|
||||
let mut eve_identity = Identity::load_mut(&*eve.repo).unwrap();
|
||||
let e1 = cob::git::stable::with_advanced_timestamp(|| {
|
||||
eve_identity.reject(a2, &eve.signer).unwrap()
|
||||
});
|
||||
let e1 =
|
||||
cob::stable::with_advanced_timestamp(|| eve_identity.reject(a2, &eve.signer).unwrap());
|
||||
assert!(eve_identity.revision(&a2).unwrap().is_active());
|
||||
|
||||
// Then she submits a new revision.
|
||||
|
|
@ -1576,7 +1570,7 @@ mod test {
|
|||
assert_eq!(eve_identity.revision(&e1).unwrap().state, State::Active);
|
||||
|
||||
alice_identity.reload().unwrap();
|
||||
let a2 = cob::git::stable::with_advanced_timestamp(|| {
|
||||
let a2 = cob::stable::with_advanced_timestamp(|| {
|
||||
alice_identity.accept(&b1, &alice.signer).unwrap()
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1765,7 +1765,7 @@ mod test {
|
|||
.unwrap();
|
||||
|
||||
// Comments require references, so adding two of them to the same transaction errors.
|
||||
let mut tx: Transaction<Issue, test::storage::git::Repository> =
|
||||
let mut tx: Transaction<Issue, crate::storage::git::Repository> =
|
||||
Transaction::<Issue, _>::default();
|
||||
tx.comment("First reply", *issue.id, vec![]).unwrap();
|
||||
let err = tx.comment("Second reply", *issue.id, vec![]).unwrap_err();
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@ use radicle_crypto::PublicKey;
|
|||
|
||||
use crate::cob;
|
||||
use crate::cob::Timestamp;
|
||||
use crate::git;
|
||||
use crate::identity;
|
||||
use crate::identity::DocAt;
|
||||
use crate::storage::ReadRepository;
|
||||
use crate::{git, identity};
|
||||
|
||||
/// The author of an [`Op`].
|
||||
pub type ActorId = PublicKey;
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ pub enum Error {
|
|||
Payload(#[from] PayloadError),
|
||||
/// Git error.
|
||||
#[error("git: {0}")]
|
||||
Git(#[from] git::ext::Error),
|
||||
Git(#[from] git::raw::Error),
|
||||
/// Store error.
|
||||
#[error("store: {0}")]
|
||||
Store(#[from] store::Error),
|
||||
|
|
@ -601,12 +601,15 @@ impl Patch {
|
|||
}
|
||||
|
||||
/// Get the merge base of this patch.
|
||||
pub fn merge_base<R: ReadRepository>(&self, repo: &R) -> Result<git::Oid, git::ext::Error> {
|
||||
pub fn merge_base<R: ReadRepository>(
|
||||
&self,
|
||||
repo: &R,
|
||||
) -> Result<crate::git::Oid, crate::git::raw::Error> {
|
||||
repo.merge_base(self.base(), self.head())
|
||||
}
|
||||
|
||||
/// Get the commit range of this patch.
|
||||
pub fn range(&self) -> Result<(git::Oid, git::Oid), git::ext::Error> {
|
||||
pub fn range(&self) -> Result<(crate::git::Oid, crate::git::Oid), crate::git::raw::Error> {
|
||||
Ok((*self.base(), *self.head()))
|
||||
}
|
||||
|
||||
|
|
@ -3334,7 +3337,7 @@ mod test {
|
|||
},
|
||||
&alice,
|
||||
);
|
||||
let patch = Patch::from_history(&h0, &repo).unwrap();
|
||||
let patch: Patch = Patch::from_history(&h0, &repo).unwrap();
|
||||
assert_eq!(patch.revisions().count(), 2);
|
||||
|
||||
let mut h1 = h0.clone();
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ pub enum Error {
|
|||
Git(git::raw::Error),
|
||||
#[error("failed to find reference '{name}': {err}")]
|
||||
RefLookup {
|
||||
name: git::RefString,
|
||||
name: git::fmt::RefString,
|
||||
#[source]
|
||||
err: git::raw::Error,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ use std::marker::PhantomData;
|
|||
use serde::Deserialize;
|
||||
|
||||
use crate::cob::{Op, TypeName};
|
||||
use crate::git::{self, Oid, PatternString};
|
||||
use crate::git;
|
||||
use crate::git::fmt::refspec::PatternString;
|
||||
use crate::git::Oid;
|
||||
|
||||
use super::error;
|
||||
use super::CobRange;
|
||||
|
|
@ -83,7 +85,7 @@ impl Walk {
|
|||
match self.until {
|
||||
Until::Tip(tip) => walk.push_range(&format!("{}..{}", self.from, tip))?,
|
||||
Until::Glob(glob) => {
|
||||
walk.push(*self.from)?;
|
||||
walk.push(self.from.into())?;
|
||||
walk.push_glob(glob.as_str())?
|
||||
}
|
||||
}
|
||||
|
|
@ -103,7 +105,7 @@ impl<'a> Iterator for WalkIter<'a> {
|
|||
// N.b. ensure that we start using the `from` commit and use the revwalk
|
||||
// after that.
|
||||
if let Some(from) = self.from.take() {
|
||||
return Some(self.repo.find_commit(*from));
|
||||
return Some(self.repo.find_commit(from.into()));
|
||||
}
|
||||
let oid = self.inner.next()?;
|
||||
Some(oid.and_then(|oid| self.repo.find_commit(oid)))
|
||||
|
|
@ -132,7 +134,7 @@ where
|
|||
let commit = self.walk.next()?;
|
||||
match commit {
|
||||
Ok(commit) => {
|
||||
let entry = git::Oid::from(commit.id());
|
||||
let entry = crate::git::Oid::from(commit.id());
|
||||
// N.b. mark this commit as seen, so that it is not walked again
|
||||
self.walk.inner.hide(commit.id()).ok();
|
||||
// Skip any Op that do not match the manifest
|
||||
|
|
@ -155,7 +157,7 @@ impl<'a, A> OpsIter<'a, A> {
|
|||
|
||||
/// Load the `Op` for the given `entry`, ensuring that manifest matches with
|
||||
/// the expected manifest.
|
||||
fn load(&self, entry: git::Oid) -> Result<Option<Op<A>>, error::Ops>
|
||||
fn load(&self, entry: crate::git::Oid) -> Result<Option<Op<A>>, error::Ops>
|
||||
where
|
||||
A: for<'de> Deserialize<'de>,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,10 +12,6 @@ use crate::cob::store::encoding;
|
|||
use crate::cob::{patch, Title};
|
||||
use crate::cob::{Entry, History, Manifest, Timestamp, Version};
|
||||
use crate::crypto::Signer;
|
||||
use crate::git;
|
||||
use crate::git::ext::author::Author;
|
||||
use crate::git::ext::commit::headers::Headers;
|
||||
use crate::git::ext::commit::{trailers::OwnedTrailer, Commit};
|
||||
use crate::git::Oid;
|
||||
use crate::node::device::Device;
|
||||
use crate::prelude::Did;
|
||||
|
|
@ -101,7 +97,7 @@ where
|
|||
self.history.merge(other.history);
|
||||
}
|
||||
|
||||
pub fn commit<G: Signer>(&mut self, action: &T::Action, signer: &G) -> git::ext::Oid {
|
||||
pub fn commit<G: Signer>(&mut self, action: &T::Action, signer: &G) -> crate::git::Oid {
|
||||
let timestamp = self.time;
|
||||
let tips = self.tips();
|
||||
let revision = arbitrary::oid();
|
||||
|
|
@ -182,7 +178,8 @@ impl<G: Signer> Actor<G> {
|
|||
"nonce": fastrand::u64(..),
|
||||
}))
|
||||
.unwrap();
|
||||
let oid = git::raw::Oid::hash_object(git::raw::ObjectType::Blob, &data).unwrap();
|
||||
let oid =
|
||||
crate::git::raw::Oid::hash_object(crate::git::raw::ObjectType::Blob, &data).unwrap();
|
||||
let id = oid.into();
|
||||
let author = *self.signer.public_key();
|
||||
let actions = NonEmpty::from_vec(actions).unwrap();
|
||||
|
|
@ -226,8 +223,8 @@ impl<G: Signer> Actor<G> {
|
|||
&mut self,
|
||||
title: Title,
|
||||
description: impl ToString,
|
||||
base: git::Oid,
|
||||
oid: git::Oid,
|
||||
base: crate::git::Oid,
|
||||
oid: crate::git::Oid,
|
||||
repo: &R,
|
||||
) -> Result<Patch, patch::Error> {
|
||||
Patch::from_root(
|
||||
|
|
@ -252,21 +249,26 @@ impl<G: Signer> Actor<G> {
|
|||
///
|
||||
/// Doesn't encode in the same way as we do in production, but attempts to include the same data
|
||||
/// that feeds into the hash entropy, so that changing any input will change the resulting oid.
|
||||
pub fn encoded<T: Cob, G: Signer>(
|
||||
fn encoded<T: Cob, G: Signer>(
|
||||
action: &T::Action,
|
||||
timestamp: Timestamp,
|
||||
parents: impl IntoIterator<Item = Oid>,
|
||||
signer: &G,
|
||||
) -> (Vec<u8>, git::ext::Oid) {
|
||||
) -> (Vec<u8>, crate::git::Oid) {
|
||||
use radicle_git_metadata::{
|
||||
author::{Author, Time},
|
||||
commit::{headers::Headers, trailers::OwnedTrailer, CommitData},
|
||||
};
|
||||
|
||||
let data = encoding::encode(action).unwrap();
|
||||
let oid = git::raw::Oid::hash_object(git::raw::ObjectType::Blob, &data).unwrap();
|
||||
let parents = parents.into_iter().map(|o| *o);
|
||||
let oid = crate::git::raw::Oid::hash_object(crate::git::raw::ObjectType::Blob, &data).unwrap();
|
||||
let parents = parents.into_iter().map(|o| o.into());
|
||||
let author = Author {
|
||||
name: "radicle".to_owned(),
|
||||
email: signer.public_key().to_human(),
|
||||
time: git_ext::author::Time::new(timestamp.as_secs() as i64, 0),
|
||||
time: Time::new(timestamp.as_secs() as i64, 0),
|
||||
};
|
||||
let commit = Commit::new::<_, _, OwnedTrailer>(
|
||||
let commit = CommitData::<git2::Oid, git2::Oid>::new::<_, _, OwnedTrailer>(
|
||||
oid,
|
||||
parents,
|
||||
author.clone(),
|
||||
|
|
@ -277,7 +279,9 @@ pub fn encoded<T: Cob, G: Signer>(
|
|||
)
|
||||
.to_string();
|
||||
|
||||
let hash = git::raw::Oid::hash_object(git::raw::ObjectType::Commit, commit.as_bytes()).unwrap();
|
||||
let hash =
|
||||
crate::git::raw::Oid::hash_object(crate::git::raw::ObjectType::Commit, commit.as_bytes())
|
||||
.unwrap();
|
||||
|
||||
(data, hash.into())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,33 +5,24 @@ use std::io;
|
|||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::str::FromStr;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use git_ext::ref_format as format;
|
||||
pub use radicle_oid::{str::ParseOidError, Oid};
|
||||
|
||||
pub extern crate radicle_git_ref_format as fmt;
|
||||
|
||||
use crate::collections::RandomMap;
|
||||
use crate::crypto::PublicKey;
|
||||
use crate::node::Alias;
|
||||
use crate::rad;
|
||||
use crate::storage;
|
||||
use crate::storage::refs::Refs;
|
||||
use crate::storage::RemoteId;
|
||||
|
||||
pub use ext::Error;
|
||||
pub use ext::NotFound;
|
||||
pub use ext::Oid;
|
||||
pub use git_ext::ref_format as fmt;
|
||||
pub use git_ext::ref_format::{
|
||||
component, lit, name, qualified, refname, refspec,
|
||||
refspec::{PatternStr, PatternString, Refspec},
|
||||
Component, Namespaced, Qualified, RefStr, RefString,
|
||||
};
|
||||
pub use radicle_git_ext as ext;
|
||||
pub use storage::git::transport::local::Url;
|
||||
pub use storage::BranchName;
|
||||
pub use crate::storage::git::transport::local::Url;
|
||||
|
||||
use raw::ErrorExt as _;
|
||||
|
||||
pub type BranchName = crate::git::fmt::RefString;
|
||||
|
||||
/// Default port of the `git` transport protocol.
|
||||
pub const PROTOCOL_PORT: u16 = 9418;
|
||||
/// Minimum required git version.
|
||||
|
|
@ -160,16 +151,16 @@ pub enum RefError {
|
|||
#[error("ref name is not valid UTF-8")]
|
||||
InvalidName,
|
||||
#[error("unexpected unqualified ref: {0}")]
|
||||
Unqualified(RefString),
|
||||
Unqualified(fmt::RefString),
|
||||
#[error("invalid ref format: {0}")]
|
||||
Format(#[from] format::Error),
|
||||
Format(#[from] fmt::Error),
|
||||
#[error("reference has no target")]
|
||||
NoTarget,
|
||||
#[error("expected ref to begin with 'refs/namespaces' but found '{0}'")]
|
||||
MissingNamespace(format::RefString),
|
||||
MissingNamespace(fmt::RefString),
|
||||
#[error("ref name contains invalid namespace identifier '{name}'")]
|
||||
InvalidNamespace {
|
||||
name: format::RefString,
|
||||
name: fmt::RefString,
|
||||
#[source]
|
||||
err: Box<dyn std::error::Error + Send + Sync + 'static>,
|
||||
},
|
||||
|
|
@ -186,9 +177,13 @@ pub enum ListRefsError {
|
|||
}
|
||||
|
||||
pub mod refs {
|
||||
use super::*;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use radicle_cob as cob;
|
||||
|
||||
use super::fmt::*;
|
||||
use super::*;
|
||||
|
||||
/// Try to get a qualified reference from a generic reference.
|
||||
pub fn qualified_from<'a>(r: &'a raw::Reference) -> Result<(Qualified<'a>, Oid), RefError> {
|
||||
let name = r.name().ok_or(RefError::InvalidName)?;
|
||||
|
|
@ -214,35 +209,28 @@ pub mod refs {
|
|||
///
|
||||
pub fn patch<'a>(object_id: &cob::ObjectId) -> Qualified<'a> {
|
||||
Qualified::from_components(
|
||||
name::component!("heads"),
|
||||
name::component!("patches"),
|
||||
component!("heads"),
|
||||
component!("patches"),
|
||||
Some(object_id.into()),
|
||||
)
|
||||
}
|
||||
|
||||
pub mod storage {
|
||||
use format::{
|
||||
lit,
|
||||
name::component,
|
||||
refspec::{self, PatternString},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Where the repo's identity document is stored.
|
||||
///
|
||||
/// `refs/rad/id`
|
||||
///
|
||||
pub static IDENTITY_BRANCH: LazyLock<Qualified> = LazyLock::new(|| {
|
||||
Qualified::from_components(name::component!("rad"), name::component!("id"), None)
|
||||
});
|
||||
pub static IDENTITY_BRANCH: LazyLock<Qualified> =
|
||||
LazyLock::new(|| Qualified::from_components(component!("rad"), component!("id"), None));
|
||||
|
||||
/// Where the repo's identity root document is stored.
|
||||
///
|
||||
/// `refs/rad/root`
|
||||
///
|
||||
pub static IDENTITY_ROOT: LazyLock<Qualified> = LazyLock::new(|| {
|
||||
Qualified::from_components(name::component!("rad"), name::component!("root"), None)
|
||||
Qualified::from_components(component!("rad"), component!("root"), None)
|
||||
});
|
||||
|
||||
/// Where the project's signed references are stored.
|
||||
|
|
@ -250,7 +238,7 @@ pub mod refs {
|
|||
/// `refs/rad/sigrefs`
|
||||
///
|
||||
pub static SIGREFS_BRANCH: LazyLock<Qualified> = LazyLock::new(|| {
|
||||
Qualified::from_components(name::component!("rad"), name::component!("sigrefs"), None)
|
||||
Qualified::from_components(component!("rad"), component!("sigrefs"), None)
|
||||
});
|
||||
|
||||
/// The set of special references used in the Heartwood protocol.
|
||||
|
|
@ -341,8 +329,8 @@ pub mod refs {
|
|||
///
|
||||
/// `refs/namespaces/*/refs/cobs/<typename>/<object_id>`
|
||||
///
|
||||
pub fn cobs(typename: &cob::TypeName, object_id: &cob::ObjectId) -> PatternString {
|
||||
refspec::pattern!("refs/namespaces/*")
|
||||
pub fn cobs(typename: &cob::TypeName, object_id: &cob::ObjectId) -> refspec::PatternString {
|
||||
pattern!("refs/namespaces/*")
|
||||
.join(refname!("refs/cobs"))
|
||||
.join(Component::from(typename))
|
||||
.join(Component::from(object_id))
|
||||
|
|
@ -390,8 +378,11 @@ pub mod refs {
|
|||
///
|
||||
/// `refs/namespaces/*/refs/drafts/cobs/<typename>/<object_id>`
|
||||
///
|
||||
pub fn cobs(typename: &cob::TypeName, object_id: &cob::ObjectId) -> PatternString {
|
||||
refspec::pattern!("refs/namespaces/*")
|
||||
pub fn cobs(
|
||||
typename: &cob::TypeName,
|
||||
object_id: &cob::ObjectId,
|
||||
) -> refspec::PatternString {
|
||||
pattern!("refs/namespaces/*")
|
||||
.join(refname!("refs/drafts/cobs"))
|
||||
.join(Component::from(typename))
|
||||
.join(Component::from(object_id))
|
||||
|
|
@ -423,7 +414,6 @@ pub mod refs {
|
|||
|
||||
pub mod workdir {
|
||||
use super::*;
|
||||
use format::name::component;
|
||||
|
||||
/// Create a [`RefString`] that corresponds to `refs/heads/<branch>`.
|
||||
pub fn branch(branch: &RefStr) -> RefString {
|
||||
|
|
@ -483,7 +473,7 @@ pub fn remote_refs(url: &Url) -> Result<RandomMap<RemoteId, Refs>, ListRefsError
|
|||
Ok(remotes)
|
||||
}
|
||||
|
||||
/// Parse a [`format::Qualified`] reference string while expecting the reference
|
||||
/// Parse a [`fmt::Qualified`] reference string while expecting the reference
|
||||
/// to start with `refs/namespaces`. If the namespace is not present, then an
|
||||
/// error will be returned.
|
||||
///
|
||||
|
|
@ -498,7 +488,7 @@ pub fn remote_refs(url: &Url) -> Result<RandomMap<RemoteId, Refs>, ListRefsError
|
|||
/// The `T` can be specified when calling the function. For example, if you
|
||||
/// wanted to parse the namespace as a `PublicKey`, then you would the function
|
||||
/// like so, `parse_ref_namespaced::<PublicKey>(s)`.
|
||||
pub fn parse_ref_namespaced<T>(s: &str) -> Result<(T, format::Qualified), RefError>
|
||||
pub fn parse_ref_namespaced<T>(s: &str) -> Result<(T, fmt::Qualified), RefError>
|
||||
where
|
||||
T: FromStr,
|
||||
T::Err: std::error::Error + Send + Sync + 'static,
|
||||
|
|
@ -510,7 +500,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Parse a [`format::Qualified`] reference string. It will optionally return
|
||||
/// Parse a [`fmt::Qualified`] reference string. It will optionally return
|
||||
/// the namespace, if present.
|
||||
///
|
||||
/// The qualified form could be of the form: `refs/heads/main`,
|
||||
|
|
@ -527,15 +517,15 @@ where
|
|||
/// The `T` can be specified when calling the function. For example, if you
|
||||
/// wanted to parse the namespace as a `PublicKey`, then you would the function
|
||||
/// like so, `parse_ref::<PublicKey>(s)`.
|
||||
pub fn parse_ref<T>(s: &str) -> Result<(Option<T>, format::Qualified), RefError>
|
||||
pub fn parse_ref<T>(s: &str) -> Result<(Option<T>, fmt::Qualified), RefError>
|
||||
where
|
||||
T: FromStr,
|
||||
T::Err: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
let input = format::RefStr::try_from_str(s)?;
|
||||
let input = fmt::RefStr::try_from_str(s)?;
|
||||
match input.to_namespaced() {
|
||||
None => {
|
||||
let refname = Qualified::from_refstr(input)
|
||||
let refname = fmt::Qualified::from_refstr(input)
|
||||
.ok_or_else(|| RefError::Unqualified(input.to_owned()))?;
|
||||
|
||||
Ok((None, refname))
|
||||
|
|
@ -573,7 +563,7 @@ pub fn initial_commit<'a>(
|
|||
pub fn commit<'a>(
|
||||
repo: &'a raw::Repository,
|
||||
parent: &'a raw::Commit,
|
||||
target: &RefStr,
|
||||
target: &fmt::RefStr,
|
||||
message: &str,
|
||||
sig: &raw::Signature,
|
||||
tree: &raw::Tree,
|
||||
|
|
@ -588,7 +578,7 @@ pub fn commit<'a>(
|
|||
pub fn empty_commit<'a>(
|
||||
repo: &'a raw::Repository,
|
||||
parent: &'a raw::Commit,
|
||||
target: &RefStr,
|
||||
target: &fmt::RefStr,
|
||||
message: &str,
|
||||
sig: &raw::Signature,
|
||||
) -> Result<raw::Commit<'a>, raw::Error> {
|
||||
|
|
@ -611,7 +601,7 @@ pub fn write_tree<'r>(
|
|||
path: &Path,
|
||||
bytes: &[u8],
|
||||
repo: &'r raw::Repository,
|
||||
) -> Result<raw::Tree<'r>, Error> {
|
||||
) -> Result<raw::Tree<'r>, raw::Error> {
|
||||
let blob_id = repo.blob(bytes)?;
|
||||
let mut builder = repo.treebuilder(None)?;
|
||||
builder.insert(path, blob_id, 0o100_644)?;
|
||||
|
|
@ -697,7 +687,7 @@ pub fn fetch(repo: &raw::Repository, remote: &str) -> Result<(), raw::Error> {
|
|||
pub fn push<'a>(
|
||||
repo: &raw::Repository,
|
||||
remote: &str,
|
||||
refspecs: impl IntoIterator<Item = (&'a Qualified<'a>, &'a Qualified<'a>)>,
|
||||
refspecs: impl IntoIterator<Item = (&'a fmt::Qualified<'a>, &'a fmt::Qualified<'a>)>,
|
||||
) -> Result<(), raw::Error> {
|
||||
let refspecs = refspecs
|
||||
.into_iter()
|
||||
|
|
|
|||
|
|
@ -19,11 +19,12 @@ use std::fmt;
|
|||
use std::marker::PhantomData;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use git_ext::ref_format::Namespaced;
|
||||
use crate::git::fmt::Namespaced;
|
||||
|
||||
use crate::prelude::Did;
|
||||
|
||||
use super::{Oid, Qualified};
|
||||
use super::fmt::Qualified;
|
||||
use crate::git::Oid;
|
||||
|
||||
/// A marker for the initial state of [`Canonical`], after construction using
|
||||
/// [`Canonical::new`].
|
||||
|
|
@ -510,20 +511,19 @@ mod tests {
|
|||
|
||||
/// Test helper to construct a Canonical and get the quorum
|
||||
fn quorum(
|
||||
heads: &[git::raw::Oid],
|
||||
heads: &[crate::git::Oid],
|
||||
threshold: usize,
|
||||
repo: &git::raw::Repository,
|
||||
repo: &crate::git::raw::Repository,
|
||||
) -> Result<Oid, QuorumError> {
|
||||
let refname =
|
||||
git::refs::branch(git_ext::ref_format::RefStr::try_from_str("master").unwrap());
|
||||
let refname = git::refs::branch(crate::git::fmt::RefStr::try_from_str("master").unwrap());
|
||||
|
||||
let mut delegates = Vec::new();
|
||||
for (i, head) in heads.iter().enumerate() {
|
||||
let signer = Device::mock_from_seed([(i + 1) as u8; 32]);
|
||||
let did = Did::from(signer.public_key());
|
||||
delegates.push(did);
|
||||
let ns = git::Component::from(signer.public_key());
|
||||
repo.reference(refname.with_namespace(ns).as_str(), *head, true, "")
|
||||
let ns = git::fmt::Component::from(signer.public_key());
|
||||
repo.reference(refname.with_namespace(ns).as_str(), head.into(), true, "")
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
|
|
@ -557,18 +557,18 @@ mod tests {
|
|||
fn test_quorum_properties() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let (repo, c0) = fixtures::repository(tmp.path());
|
||||
let c0: git::Oid = c0.into();
|
||||
let a1 = fixtures::commit("A1", &[*c0], &repo);
|
||||
let a2 = fixtures::commit("A2", &[*a1], &repo);
|
||||
let d1 = fixtures::commit("D1", &[*c0], &repo);
|
||||
let c1 = fixtures::commit("C1", &[*c0], &repo);
|
||||
let c2 = fixtures::commit("C2", &[*c1], &repo);
|
||||
let b2 = fixtures::commit("B2", &[*c1], &repo);
|
||||
let a1 = fixtures::commit("A1", &[*c0], &repo);
|
||||
let m1 = fixtures::commit("M1", &[*c2, *b2], &repo);
|
||||
let m2 = fixtures::commit("M2", &[*a1, *b2], &repo);
|
||||
let c0: crate::git::Oid = c0.into();
|
||||
let a1 = fixtures::commit("A1", &[c0.into()], &repo);
|
||||
let a2 = fixtures::commit("A2", &[a1.into()], &repo);
|
||||
let d1 = fixtures::commit("D1", &[c0.into()], &repo);
|
||||
let c1 = fixtures::commit("C1", &[c0.into()], &repo);
|
||||
let c2 = fixtures::commit("C2", &[c1.into()], &repo);
|
||||
let b2 = fixtures::commit("B2", &[c1.into()], &repo);
|
||||
let a1 = fixtures::commit("A1", &[c0.into()], &repo);
|
||||
let m1 = fixtures::commit("M1", &[c2.into(), b2.into()], &repo);
|
||||
let m2 = fixtures::commit("M2", &[a1.into(), b2.into()], &repo);
|
||||
let mut rng = fastrand::Rng::new();
|
||||
let choices = [*c0, *c1, *c2, *b2, *a1, *a2, *d1, *m1, *m2];
|
||||
let choices = [c0, c1, c2, b2, a1, a2, d1, m1, m2];
|
||||
|
||||
for _ in 0..100 {
|
||||
let count = rng.usize(1..=choices.len());
|
||||
|
|
@ -591,11 +591,10 @@ mod tests {
|
|||
fn test_quorum_different_types() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let (repo, c0) = fixtures::repository(tmp.path());
|
||||
let c0: git::Oid = c0.into();
|
||||
let t0 = fixtures::tag("v1", "", *c0, &repo);
|
||||
let t0 = fixtures::tag("v1", "", c0, &repo);
|
||||
|
||||
assert_matches!(
|
||||
quorum(&[*c0, *t0], 1, &repo),
|
||||
quorum(&[c0.into(), t0], 1, &repo),
|
||||
Err(QuorumError::DifferentTypes { .. })
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use crate::git;
|
||||
use crate::git::fmt::Qualified;
|
||||
use crate::git::raw::ErrorExt as _;
|
||||
use crate::git::{Oid, Qualified};
|
||||
use crate::git::Oid;
|
||||
use crate::prelude::Did;
|
||||
|
||||
use super::{FoundObjects, GraphAheadBehind, MergeBase, Object};
|
||||
|
|
@ -40,7 +41,7 @@ pub enum FindObjectsError {
|
|||
},
|
||||
#[error("failed to find reference {refname} due to: {source}")]
|
||||
FindReference {
|
||||
refname: git::Namespaced<'static>,
|
||||
refname: git::fmt::Namespaced<'static>,
|
||||
source: Box<dyn std::error::Error + Send + Sync + 'static>,
|
||||
},
|
||||
#[error("failed to find objects")]
|
||||
|
|
@ -60,7 +61,7 @@ impl FindObjectsError {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn find_reference<E>(refname: git::Namespaced<'static>, err: E) -> Self
|
||||
pub fn find_reference<E>(refname: git::fmt::Namespaced<'static>, err: E) -> Self
|
||||
where
|
||||
E: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
|
|
@ -170,7 +171,7 @@ pub struct GraphDescendant {
|
|||
|
||||
impl FindMergeBase for git::raw::Repository {
|
||||
fn merge_base(&self, a: Oid, b: Oid) -> Result<MergeBase, MergeBaseError> {
|
||||
self.merge_base(*a, *b)
|
||||
self.merge_base(a.into(), b.into())
|
||||
.map_err(|err| MergeBaseError {
|
||||
a,
|
||||
b,
|
||||
|
|
@ -190,7 +191,7 @@ impl Ancestry for git::raw::Repository {
|
|||
commit: Oid,
|
||||
upstream: Oid,
|
||||
) -> Result<GraphAheadBehind, GraphDescendant> {
|
||||
self.graph_ahead_behind(*commit, *upstream)
|
||||
self.graph_ahead_behind(commit.into(), upstream.into())
|
||||
.map_err(|err| GraphDescendant {
|
||||
commit,
|
||||
upstream,
|
||||
|
|
@ -228,7 +229,7 @@ impl FindObjects for git::raw::Repository {
|
|||
log::warn!(target: "radicle", "Missing target for reference `{name}`");
|
||||
continue;
|
||||
};
|
||||
let object = match self.find_object(*oid, None) {
|
||||
let object = match self.find_object(oid.into(), None) {
|
||||
Ok(object) => Object::new(&object).ok_or_else(|| {
|
||||
FindObjectsError::invalid_object_type(
|
||||
*did,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue