diff --git a/Cargo.lock b/Cargo.lock index d965ff49..9b98a0ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,12 +216,11 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -287,9 +286,9 @@ dependencies = [ [[package]] name = "ed25519-compact" -version = "1.0.12" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c25036262e9b9c81fe4c6decb438f753f66a8f06aac5dbe9eb2b28355c85c3f5" +checksum = "bee9df587982575886a8682edcee11877894349a805f25629c27f63abe3e9ae8" dependencies = [ "ct-codecs", "getrandom", @@ -417,14 +416,13 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.48" +version = "0.1.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237a0714f28b1ee39ccec0770ccb544eb02c9ef2c82bb096230eefcffa6468b0" +checksum = "fd911b35d940d2bd0bea0f9100068e5b97b51a1cbe13d13382f132e0365257a0" dependencies = [ "android_system_properties", "core-foundation-sys", "js-sys", - "once_cell", "wasm-bindgen", "winapi", ] @@ -466,9 +464,9 @@ checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" [[package]] name = "jobserver" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" dependencies = [ "libc", ] @@ -502,9 +500,9 @@ checksum = "478ee9e62aaeaf5b140bd4138753d1f109765488581444218d3ddda43234f3e8" [[package]] name = "libc" -version = "0.2.133" +version = "0.2.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" +checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" [[package]] name = "libgit2-sys" @@ -641,9 +639,9 @@ dependencies = [ [[package]] name = "olpc-cjson" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ca49fe685014bbf124ee547da94ed7bb65a6eb9dc9c4711773c081af96a39c" +checksum = "87dc75cf72208cd853671c1abccc5d5d1e43b1e378dde67340ef933219a8c13c" dependencies = [ "serde", "serde_json", @@ -670,9 +668,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.75" +version = "0.9.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f" +checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" dependencies = [ "autocfg", "cc", @@ -728,9 +726,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" dependencies = [ "unicode-ident", ] @@ -910,18 +908,18 @@ checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "serde" -version = "1.0.144" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.144" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ "proc-macro2", "quote", @@ -1037,9 +1035,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" +checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" dependencies = [ "proc-macro2", "quote", @@ -1062,18 +1060,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", diff --git a/radicle-tools/src/rad-push.rs b/radicle-tools/src/rad-push.rs index 3263cbfe..a6070f9a 100644 --- a/radicle-tools/src/rad-push.rs +++ b/radicle-tools/src/rad-push.rs @@ -8,7 +8,7 @@ fn main() -> anyhow::Result<()> { let profile = radicle::Profile::load()?; let (_, id) = radicle::rad::remote(&repo)?; - let output = radicle::git::run(&cwd, &["push", "rad"])?; + let output = radicle::git::run::<_, _, &str, &str>(&cwd, &["push", "rad"], None)?; println!("{}", output); let project = profile.storage.repository(id)?; diff --git a/radicle/src/git.rs b/radicle/src/git.rs index 01fabcb7..cd627ee1 100644 --- a/radicle/src/git.rs +++ b/radicle/src/git.rs @@ -18,7 +18,7 @@ pub use ext::NotFound; pub use ext::Oid; pub use git2 as raw; pub use git_ref_format as fmt; -pub use git_ref_format::{refname, Component, Qualified, RefStr, RefString}; +pub use git_ref_format::{lit, name, refname, Component, Namespaced, Qualified, RefStr, RefString}; pub use git_url as url; pub use git_url::Url; pub use radicle_git_ext as ext; @@ -33,6 +33,14 @@ pub enum RefError { InvalidName(format::RefString), #[error("invalid ref format: {0}")] Format(#[from] format::Error), + #[error("expected ref to begin with 'refs/namespaces' but found '{0}'")] + MissingNamespace(format::RefString), + #[error("ref name contains invalid namespace identifier '{name}'")] + Id { + name: format::RefString, + #[source] + err: Box, + }, } #[derive(thiserror::Error, Debug)] @@ -43,9 +51,11 @@ pub enum ListRefsError { InvalidRef(#[from] RefError), } -impl From<&RemoteId> for RefString { +impl<'a> From<&RemoteId> for Component<'a> { fn from(id: &RemoteId) -> Self { - RefString::try_from(id.to_string()).expect("encoded public keys are valid ref strings") + let refstr = + RefString::try_from(id.to_string()).expect("encoded public keys are valid ref strings"); + Component::from_refstring(refstr).expect("encoded public keys are valid refname components") } } @@ -56,17 +66,19 @@ pub mod refs { pub static IDENTITY_BRANCH: Lazy = Lazy::new(|| refname!("radicle/id")); pub mod storage { + use super::*; - pub fn branch(remote: &RemoteId, branch: &RefStr) -> RefString { - refname!("refs/remotes") - .and::(remote.into()) - .and(refname!("heads")) - .and(branch) + /// Create the [`Namespaced`] `branch` under the `remote` namespace, i.e. + /// `refs/namespaces//refs/heads/` + pub fn branch<'a>(remote: &RemoteId, branch: &RefStr) -> Namespaced<'a> { + Qualified::from(git_ref_format::lit::refs_heads(branch)).add_namespace(remote.into()) } /// Get the branch used to track project information. - pub fn id(remote: &RemoteId) -> RefString { + /// + /// `refs/namespaces//refs/heads/radicle/id` + pub fn id(remote: &RemoteId) -> Namespaced { branch(remote, &IDENTITY_BRANCH) } } @@ -74,18 +86,22 @@ pub mod refs { pub mod workdir { use super::*; + /// Create a [`RefString`] that corresponds to `refs/heads/`. pub fn branch(branch: &RefStr) -> RefString { refname!("refs/heads").join(branch) } + /// Create a [`RefString`] that corresponds to `refs/notes/`. pub fn note(name: &RefStr) -> RefString { refname!("refs/notes").join(name) } + /// Create a [`RefString`] that corresponds to `refs/remotes//`. pub fn remote_branch(remote: &RefStr, branch: &RefStr) -> RefString { refname!("refs/remotes").and(remote).and(branch) } + /// Create a [`RefString`] that corresponds to `refs/tags/`. pub fn tag(name: &RefStr) -> RefString { refname!("refs/tags").join(name) } @@ -105,27 +121,34 @@ pub fn remote_refs(url: &Url) -> Result, ListRefsError> let (id, refname) = parse_ref::(r.name())?; let entry = remotes.entry(id).or_insert_with(Refs::default); - entry.insert(refname, r.oid().into()); + entry.insert(refname.into(), r.oid().into()); } Ok(remotes) } /// Parse a ref string. -pub fn parse_ref(s: &str) -> Result<(T, format::RefString), RefError> { +pub fn parse_ref(s: &str) -> Result<(T, format::Qualified), RefError> +where + T: FromStr, + T::Err: std::error::Error + Send + Sync + 'static, +{ let input = format::RefStr::try_from_str(s)?; - let suffix = input - .strip_prefix(format::refname!("refs/remotes")) - .ok_or_else(|| RefError::InvalidName(input.to_owned()))?; - - let mut components = suffix.components(); - let id = components - .next() - .ok_or_else(|| RefError::InvalidName(input.to_owned()))?; - let id = T::from_str(&id.to_string()).map_err(|_| RefError::InvalidName(input.to_owned()))?; - let refstr = components.collect::(); - - Ok((id, refstr)) + match input.namespaced() { + None => Err(RefError::MissingNamespace(input.to_owned())), + Some(ns) => { + let id = ns + .namespace() + .as_str() + .parse() + .map_err(|err| RefError::Id { + name: input.to_owned(), + err: Box::new(err), + })?; + let rest = ns.strip_namespace(); + Ok((id, rest)) + } + } } /// Create an initial empty commit. @@ -158,24 +181,6 @@ pub fn commit<'a>( Ok(commit) } -/// Push the refs to the radicle remote. -pub fn push(repo: &git2::Repository) -> Result<(), git2::Error> { - // NOTE: This function is going away soon. - #![allow(clippy::unwrap_used)] - - let mut remote = repo.find_remote("rad")?; - let refspecs = remote.push_refspecs()?; - let refspec = refspecs.into_iter().next().unwrap().unwrap(); - - // The `git2` crate doesn't seem to support push refspecs with '*' in them, - // so we manually replace it with the current branch. - let head = repo.head()?; - let branch = head.shorthand().unwrap(); - let refspec = refspec.replace('*', branch); - - remote.push::<&str>(&[&refspec], None) -} - /// Get the repository head. pub fn head(repo: &git2::Repository) -> Result { let head = repo.head()?.peel_to_commit()?; @@ -201,22 +206,62 @@ pub fn write_tree<'r>( /// Configure a repository's radicle remote. /// -/// Takes the repository in which to configure the remote, the name of the remote, the public -/// key of the remote, and the path to the remote repository on the filesystem. +/// The entry for this remote will be: +/// ```text +/// [remote.] +/// url = +/// fetch +refs/heads/*:refs/remotes//* +/// ``` pub fn configure_remote<'r>( repo: &'r git2::Repository, - remote_name: &str, - remote_id: &RemoteId, - remote_url: &Url, + name: &str, + url: &Url, ) -> Result, git2::Error> { - let fetch = format!("+refs/remotes/{remote_id}/heads/*:refs/remotes/rad/*"); - let push = format!("refs/heads/*:refs/remotes/{remote_id}/heads/*"); - let remote = repo.remote_with_fetch(remote_name, remote_url.to_string().as_str(), &fetch)?; - repo.remote_add_push(remote_name, &push)?; + let fetch = format!("+refs/heads/*:refs/remotes/{name}/*"); + let remote = repo.remote_with_fetch(name, url.to_string().as_str(), &fetch)?; Ok(remote) } +/// Fetch from the given `remote` using the provided `namespace`. +/// +/// This uses [`Command`] under the hood and is the equivalent to: +/// +/// `GIT_NAMESPACE= git fetch ` +pub fn fetch(repo: &git2::Repository, remote: &str, namespace: &RemoteId) -> io::Result { + run( + &repo.path(), + ["fetch", remote], + [("GIT_NAMESPACE", Component::from(namespace).as_str())], + ) +} + +/// Push `refspecs` to the given `remote` using the provided `namespace`. +/// +/// This uses [`Command`] under the hood and is the equivalent to: +/// +/// `GIT_NAMESPACE= git push []` +pub fn push( + repo: &git2::Repository, + remote: &str, + namespace: &RemoteId, + refspecs: impl IntoIterator, +) -> io::Result +where + Ref: AsRef, +{ + let mut args = vec!["push".to_owned(), remote.to_owned()]; + let refspecs = refspecs + .into_iter() + .map(|(src, dst)| format!("{}:{}", src.as_ref().as_str(), dst.as_ref().as_str())); + args.extend(refspecs); + run( + &repo.path(), + args, + [("GIT_NAMESPACE", Component::from(namespace).as_str())], + ) +} + /// Set the upstream of the given branch to the given remote. /// /// This writes to the `config` directly. The entry will look like the @@ -258,11 +303,22 @@ pub fn set_upstream( } /// Execute a git command by spawning a child process. -pub fn run, S: AsRef>( +pub fn run( repo: &P, args: impl IntoIterator, -) -> Result { - let output = Command::new("git").current_dir(repo).args(args).output()?; + envs: impl IntoIterator, +) -> Result +where + P: AsRef, + S: AsRef, + K: AsRef, + V: AsRef, +{ + let output = Command::new("git") + .current_dir(repo) + .envs(envs) + .args(args) + .output()?; if output.status.success() { let out = if output.stdout.is_empty() { diff --git a/radicle/src/identity/project.rs b/radicle/src/identity/project.rs index 48cafaf9..aeb99a30 100644 --- a/radicle/src/identity/project.rs +++ b/radicle/src/identity/project.rs @@ -358,8 +358,8 @@ impl Doc { impl Doc { pub fn head(remote: &RemoteId, repo: &R) -> Result { - let head = &git::refname!("heads").join(&*git::refs::IDENTITY_BRANCH); - repo.reference_oid(remote, head).map_err(DocError::from) + let head = git::Qualified::from(git::lit::refs_heads(&*git::refs::IDENTITY_BRANCH)); + repo.reference_oid(remote, &head).map_err(DocError::from) } } diff --git a/radicle/src/rad.rs b/radicle/src/rad.rs index 395fc6a3..d28f4076 100644 --- a/radicle/src/rad.rs +++ b/radicle/src/rad.rs @@ -3,6 +3,7 @@ use std::io; use std::path::Path; use std::str::FromStr; +use git_ref_format::{lit, Qualified}; use once_cell::sync::Lazy; use thiserror::Error; @@ -68,20 +69,11 @@ pub fn init( repo, &REMOTE_NAME, &default_branch, - &git::refs::storage::branch(pk, &default_branch), + &git::refs::workdir::branch(&default_branch), )?; - // TODO: Note that you'll likely want to use `RemoteCallbacks` and set - // `push_update_reference` to test whether all the references were pushed - // successfully. - git::configure_remote(repo, &REMOTE_NAME, pk, &url)?.push::<&str>( - &[&format!( - "{}:{}", - &git::refs::workdir::branch(&default_branch), - &git::refs::storage::branch(pk, &default_branch), - )], - None, - )?; + git::configure_remote(repo, &REMOTE_NAME, &url)?; + git::push(repo, &REMOTE_NAME, pk, [(&default_branch, &default_branch)])?; let signed = storage.sign_refs(&project, signer)?; Ok((id, signed)) @@ -171,10 +163,8 @@ pub fn fork( let canonical_head = { let mut heads = Vec::new(); for delegate in project.delegates.iter() { - let name = format!("heads/{}", &project.default_branch); - let refname = git::RefString::try_from(name.as_str())?; + let refname = Qualified::from(lit::refs_heads(&project.default_branch)); let r = repository.reference_oid(&delegate.id, &refname)?.into(); - heads.push(r); } @@ -258,6 +248,8 @@ pub fn clone_url, G: Signer, S: storage::WriteStorage>( #[derive(Error, Debug)] pub enum CheckoutError { + #[error("failed to fetch to working copy")] + Fetch(#[source] io::Error), #[error("git: {0}")] Git(#[from] git2::Error), #[error("storage: {0}")] @@ -289,12 +281,14 @@ pub fn checkout, S: storage::ReadStorage>( let url = storage.url(&proj); // Configure and fetch all refs from remote. - git::configure_remote(&repo, &REMOTE_NAME, remote, &url)?.fetch::<&str>(&[], None, None)?; + git::configure_remote(&repo, &REMOTE_NAME, &url)?; + git::fetch(&repo, &REMOTE_NAME, remote).map_err(CheckoutError::Fetch)?; { // Setup default branch. let remote_head_ref = git::refs::workdir::remote_branch(&REMOTE_NAME, &project.default_branch); + let remote_head_commit = repo.find_reference(&remote_head_ref)?.peel_to_commit()?; let _ = repo.branch(&project.default_branch, &remote_head_commit, true)?; @@ -303,7 +297,7 @@ pub fn checkout, S: storage::ReadStorage>( &repo, &REMOTE_NAME, &project.default_branch, - &git::refs::storage::branch(remote, &project.default_branch), + &git::refs::workdir::branch(&project.default_branch), )?; } diff --git a/radicle/src/storage.rs b/radicle/src/storage.rs index df124357..edca9e88 100644 --- a/radicle/src/storage.rs +++ b/radicle/src/storage.rs @@ -17,7 +17,7 @@ use crate::crypto; use crate::crypto::{PublicKey, Signer, Unverified, Verified}; use crate::git::ext as git_ext; use crate::git::Url; -use crate::git::{RefError, RefStr, RefString}; +use crate::git::{Qualified, RefError, RefString}; use crate::identity; use crate::identity::{Id, IdError}; use crate::storage::refs::Refs; @@ -54,6 +54,8 @@ pub enum FetchError { Git(#[from] git2::Error), #[error("i/o: {0}")] Io(#[from] io::Error), + #[error(transparent)] + Refs(#[from] refs::Error), #[error("verify: {0}")] Verify(#[from] git::VerifyError), #[error(transparent)] @@ -174,13 +176,16 @@ pub struct Remote { pub id: PublicKey, /// Git references published under this remote, and their hashes. pub refs: SignedRefs, - /// Whether this remote is of a project delegate. + /// Whether this remote is a delegate for the project. pub delegate: bool, /// Whether the remote is verified or not, ie. whether its signed refs were checked. verified: PhantomData, } impl Remote { + // TODO(finto): This function seems out of place in the API for a couple of reasons: + // * The SignedRefs aren't guaranteed to be by the `id` + // * I could write `Remote::::new(id, refs) and because of the above, it's a LIE pub fn new(id: PublicKey, refs: impl Into>) -> Self { Self { id, @@ -239,17 +244,34 @@ pub trait WriteStorage: ReadStorage { } pub trait ReadRepository { + /// Returns `true` if there are no references in the repository. fn is_empty(&self) -> Result; + + /// The [`Path`] to the git repository. fn path(&self) -> &Path; + fn blob_at<'a>(&'a self, oid: Oid, path: &'a Path) -> Result, git_ext::Error>; + + /// Get the `reference` for the given `remote`. + /// + /// Returns `None` is the reference did not exist. fn reference( &self, remote: &RemoteId, - reference: &RefStr, + reference: &Qualified, ) -> Result; + + /// Get the [`git2::Commit`] found using its `oid`. + /// + /// Returns `None` if the commit did not exist. fn commit(&self, oid: Oid) -> Result; + fn revwalk(&self, head: Oid) -> Result; - fn reference_oid(&self, remote: &RemoteId, reference: &RefStr) -> Result; + fn reference_oid( + &self, + remote: &RemoteId, + reference: &Qualified, + ) -> Result; fn references(&self, remote: &RemoteId) -> Result; fn remote(&self, remote: &RemoteId) -> Result, refs::Error>; fn remotes(&self) -> Result, refs::Error>; diff --git a/radicle/src/storage/git.rs b/radicle/src/storage/git.rs index 6c5c131d..2fa70db9 100644 --- a/radicle/src/storage/git.rs +++ b/radicle/src/storage/git.rs @@ -24,9 +24,9 @@ pub use crate::git::*; use super::{RefUpdate, RemoteId}; pub static REMOTES_GLOB: Lazy = - Lazy::new(|| refspec::pattern!("refs/remotes/*")); + Lazy::new(|| refspec::pattern!("refs/namespaces/*")); pub static SIGNATURES_GLOB: Lazy = - Lazy::new(|| refspec::pattern!("refs/remotes/*/radicle/signature")); + Lazy::new(|| refspec::pattern!("refs/namespaces/*/radicle/signature")); #[derive(Error, Debug)] pub enum ProjectError { @@ -254,6 +254,7 @@ impl Repository { let remote = remotes .get_mut(&remote_id) .ok_or(VerifyError::InvalidRemote(remote_id))?; + let refname = RefString::from(refname); let signed_oid = remote .remove(&refname) .ok_or_else(|| VerifyError::UnknownRef(remote_id, refname.clone()))?; @@ -396,8 +397,7 @@ impl Repository { impl ReadRepository for Repository { fn is_empty(&self) -> Result { - let some = self.remotes()?.next().is_some(); - Ok(!some) + Ok(self.remotes()?.next().is_none()) } fn path(&self) -> &Path { @@ -415,11 +415,9 @@ impl ReadRepository for Repository { fn reference( &self, remote: &RemoteId, - name: &git::RefStr, + name: &git::Qualified, ) -> Result { - let name = name.strip_prefix(git::refname!("refs")).unwrap_or(name); - let name = format!("refs/remotes/{remote}/{name}"); - + let name = name.add_namespace(remote.into()); self.backend.find_reference(&name).map_err(git::Error::from) } @@ -436,7 +434,11 @@ impl ReadRepository for Repository { Ok(revwalk) } - fn reference_oid(&self, remote: &RemoteId, reference: &git::RefStr) -> Result { + fn reference_oid( + &self, + remote: &RemoteId, + reference: &git::Qualified, + ) -> Result { let oid = self .reference(remote, reference)? .target() @@ -454,7 +456,7 @@ impl ReadRepository for Repository { // TODO: Only return known refs, eg. heads/ rad/ tags/ etc.. let entries = self .backend - .references_glob(format!("refs/remotes/{remote}/*").as_str())?; + .references_glob(format!("refs/namespaces/{remote}/*").as_str())?; let mut refs = BTreeMap::new(); for e in entries { @@ -463,7 +465,7 @@ impl ReadRepository for Repository { let (_, refname) = git::parse_ref::(name)?; let oid = e.target().ok_or(Error::InvalidRef)?; - refs.insert(refname, oid.into()); + refs.insert(refname.into(), oid.into()); } Ok(refs.into()) } @@ -523,7 +525,7 @@ impl WriteRepository for Repository { // local <- git-fetch -- staging # fetch from staging copy // let url = url.to_string(); - let refs: &[&str] = &["refs/remotes/*:refs/remotes/*"]; + let refs: &[&str] = &["refs/namespaces/*:refs/namespaces/*"]; let mut updates = Vec::new(); let mut callbacks = git2::RemoteCallbacks::new(); let tempdir = tempfile::tempdir()?; @@ -692,7 +694,8 @@ mod tests { // Strip the remote refs of sigrefs so we can compare them. for remote in refs.values_mut() { - remote.remove(&*SIGNATURE_REF).unwrap(); + let sigref = (*SIGNATURE_REF).to_ref_string(); + remote.remove(&sigref).unwrap(); } let remotes = remotes @@ -713,7 +716,7 @@ mod tests { let proj = *inventory.first().unwrap(); let repo = alice.repository(proj).unwrap(); let remotes = repo.remotes().unwrap().collect::>(); - let refname = git::refname!("heads/master"); + let refname = Qualified::from_refstr(git::refname!("refs/heads/master")).unwrap(); // Have Bob fetch Alice's refs. let updates = bob @@ -735,7 +738,7 @@ mod tests { for update in updates { assert_matches!( update, - RefUpdate::Created { name, .. } if name.starts_with("refs/remotes") + RefUpdate::Created { name, .. } if name.starts_with("refs/namespaces") ); } @@ -762,7 +765,7 @@ mod tests { let (proj_id, _, proj_repo, alice_head) = fixtures::project(tmp.path().join("alice/project"), &alice, &alice_signer).unwrap(); - let refname = git::refname!("refs/heads/master"); + let refname = Qualified::from_refstr(git::refname!("refs/heads/master")).unwrap(); let alice_url = git::Url { scheme: git_url::Scheme::File, path: paths::repository(&alice, &proj_id) @@ -782,7 +785,7 @@ mod tests { let alice_head = git::commit(&proj_repo, &alice_head, &refname, "Making changes", "Alice") .unwrap() .id(); - git::push(&proj_repo).unwrap(); + git::push(&proj_repo, "rad", alice_id, [(&refname, &refname)]).unwrap(); alice.sign_refs(&alice_proj_storage, &alice_signer).unwrap(); // Have Bob fetch Alice's new commit. @@ -894,9 +897,9 @@ mod tests { assert_eq!( updates, vec![ - format!("refs/remotes/{remote}/heads/master"), - format!("refs/remotes/{remote}/heads/radicle/id"), - format!("refs/remotes/{remote}/radicle/signature") + format!("refs/namespaces/{remote}/refs/heads/master"), + format!("refs/namespaces/{remote}/refs/heads/radicle/id"), + format!("refs/namespaces/{remote}/refs/radicle/signature") ] ); } @@ -928,7 +931,8 @@ mod tests { let mut unsigned = project.references(&alice).unwrap(); // The signed refs doesn't contain the signature ref itself. - unsigned.remove(&*SIGNATURE_REF).unwrap(); + let sigref = (*SIGNATURE_REF).to_ref_string(); + unsigned.remove(&sigref).unwrap(); assert_eq!(remote.refs, signed); assert_eq!(*remote.refs, unsigned); diff --git a/radicle/src/storage/refs.rs b/radicle/src/storage/refs.rs index 196ae43b..01d398c0 100644 --- a/radicle/src/storage/refs.rs +++ b/radicle/src/storage/refs.rs @@ -18,7 +18,13 @@ use crate::git::Oid; use crate::storage; use crate::storage::{ReadRepository, RemoteId, WriteRepository}; -pub static SIGNATURE_REF: Lazy = Lazy::new(|| git::refname!("radicle/signature")); +pub static SIGNATURE_REF: Lazy = Lazy::new(|| { + git::Qualified::from_components( + git::name::component!("radicle"), + git::name::component!("signature"), + None, + ) +}); pub const REFS_BLOB_PATH: &str = "refs"; pub const SIGNATURE_BLOB_PATH: &str = "signature"; @@ -124,7 +130,7 @@ impl Refs { let mut buf = String::new(); let refs = self .iter() - .filter(|(name, oid)| *name != &*SIGNATURE_REF && !oid.is_zero()); + .filter(|(name, oid)| name.as_refstr() != (*SIGNATURE_REF).as_ref() && !oid.is_zero()); for (name, oid) in refs { buf.push_str(&oid.to_string()); @@ -291,7 +297,7 @@ impl SignedRefs { } } - let sigref = format!("refs/remotes/{remote}/{sigref}"); + let sigref = sigref.add_namespace(remote.into()); let author = repo.raw().signature()?; let commit = repo.raw().commit( Some(&sigref), diff --git a/radicle/src/test/storage.rs b/radicle/src/test/storage.rs index cd403e5c..4fefa01d 100644 --- a/radicle/src/test/storage.rs +++ b/radicle/src/test/storage.rs @@ -116,7 +116,7 @@ impl ReadRepository for MockRepository { fn reference( &self, _remote: &RemoteId, - _reference: &git::RefStr, + _reference: &git::Qualified, ) -> Result { todo!() } @@ -124,7 +124,7 @@ impl ReadRepository for MockRepository { fn reference_oid( &self, _remote: &RemoteId, - _reference: &git::RefStr, + _reference: &git::Qualified, ) -> Result { todo!() }