diff --git a/crates/radicle-node/src/worker/fetch.rs b/crates/radicle-node/src/worker/fetch.rs index be0758b0..66fc4648 100644 --- a/crates/radicle-node/src/worker/fetch.rs +++ b/crates/radicle-node/src/worker/fetch.rs @@ -131,11 +131,9 @@ impl Handle { // points to a repository that is temporary and gets moved by [`mv`]. let repo = storage.repository(rid)?; repo.set_identity_head()?; - match repo.set_head() { - Ok(head) => { - if head.is_updated() { - log::trace!(target: "worker", "Set HEAD to {}", head.new); - } + match repo.set_head_to_default_branch() { + Ok(()) => { + log::trace!(target: "worker", "Set HEAD successfully"); } Err(RepositoryError::Quorum(e)) => { log::warn!(target: "worker", "Fetch could not set HEAD for {rid}: {e}") @@ -352,9 +350,6 @@ fn set_canonical_refs( applied: &Applied, ) -> Result, error::Canonical> { let identity = repo.identity()?; - // TODO(finto): it's unfortunate that we may end up computing the default - // branch again after `set_head` is called after the fetch. This is due to - // the storage capabilities being leaked to this part of the code base. let rules = identity .canonical_refs_or_default(|| { let rule = identity.doc().default_branch_rule()?; diff --git a/crates/radicle-remote-helper/src/push.rs b/crates/radicle-remote-helper/src/push.rs index 972b062a..e881e756 100644 --- a/crates/radicle-remote-helper/src/push.rs +++ b/crates/radicle-remote-helper/src/push.rs @@ -426,14 +426,8 @@ pub(super) fn run( // N.b. special case for handling the canonical ref, since it // creates a symlink to HEAD - if *refname == canonical_ref - && stored - .set_head() - .map(|head| head.is_updated()) - .unwrap_or(false) - { - print_update(); - continue; + if *refname == canonical_ref { + stored.set_head_to_default_branch()?; } match stored.backend.refname_to_id(refname.as_str()) { diff --git a/crates/radicle/src/rad.rs b/crates/radicle/src/rad.rs index 7791b66e..1d343caf 100644 --- a/crates/radicle/src/rad.rs +++ b/crates/radicle/src/rad.rs @@ -143,7 +143,8 @@ where )?; stored.set_remote_identity_root_to(pk, identity)?; stored.set_identity_head_to(identity)?; - stored.set_head()?; + stored.set_head_to_default_branch()?; + stored.set_default_branch_to_canonical_head()?; let signed = stored.sign_refs(signer)?; diff --git a/crates/radicle/src/storage.rs b/crates/radicle/src/storage.rs index 3315b13e..f407cd7d 100644 --- a/crates/radicle/src/storage.rs +++ b/crates/radicle/src/storage.rs @@ -115,7 +115,7 @@ impl FromIterator for Namespaces { } } -/// Output of [`WriteRepository::set_head`]. +/// Output of [`WriteRepository::set_default_branch_to_canonical_head`]. pub struct SetHead { /// Old branch head. pub old: Option, @@ -668,9 +668,16 @@ where /// Allows read-write access to a repository. pub trait WriteRepository: ReadRepository + SignRepository { - /// Set the repository head to the canonical branch. - /// This computes the head based on the delegate set. - fn set_head(&self) -> Result; + /// Sets the symbolic reference `HEAD` to target the default branch. + /// This only depends on the value for the default branch in the identity + /// document, and does not require the canonical reference behind the + /// default branch to be computed, or even exist. + fn set_head_to_default_branch(&self) -> Result<(), RepositoryError>; + + /// Computes the head of the default branch based on the delegate set, + /// and sets it. + fn set_default_branch_to_canonical_head(&self) -> Result; + /// Set the repository 'rad/id' to the canonical commit, agreed by quorum. fn set_identity_head(&self) -> Result { let head = self.canonical_identity_head()?; diff --git a/crates/radicle/src/storage/git.rs b/crates/radicle/src/storage/git.rs index ad865215..7e87c899 100644 --- a/crates/radicle/src/storage/git.rs +++ b/crates/radicle/src/storage/git.rs @@ -943,16 +943,42 @@ impl ReadRepository for Repository { } impl WriteRepository for Repository { - fn set_head(&self) -> Result { + fn set_head_to_default_branch(&self) -> Result<(), RepositoryError> { let head_ref = refname!("HEAD"); + let branch_ref = self.default_branch()?; + + match self.raw().find_reference(head_ref.as_str()) { + Ok(mut head_ref) => { + if head_ref + .symbolic_target() + .is_some_and(|t| t != branch_ref.as_str()) + { + head_ref.symbolic_set_target(branch_ref.as_str(), "set-head (radicle)")?; + } + Ok(()) + } + Err(err) if err.is_not_found() => { + self.raw().reference_symbolic( + head_ref.as_str(), + branch_ref.as_str(), + true, + "set-head (radicle)", + )?; + Ok(()) + } + Err(err) => Err(err.into()), + } + } + + fn set_default_branch_to_canonical_head(&self) -> Result { + let (branch_ref, new) = self.canonical_head()?; + let old = self .raw() - .refname_to_id(&head_ref) + .refname_to_id(&branch_ref) .ok() .map(|oid| oid.into()); - let (branch_ref, new) = self.canonical_head()?; - if old == Some(new) { return Ok(SetHead { old, new }); } @@ -960,10 +986,6 @@ impl WriteRepository for Repository { self.raw() .reference(&branch_ref, new.into(), true, "set-local-branch (radicle)")?; - log::debug!(target: "storage", "Setting ref: {head_ref} -> {branch_ref}"); - self.raw() - .reference_symbolic(&head_ref, &branch_ref, true, "set-head (radicle)")?; - Ok(SetHead { old, new }) } diff --git a/crates/radicle/src/test.rs b/crates/radicle/src/test.rs index 0c141c24..9ca677ce 100644 --- a/crates/radicle/src/test.rs +++ b/crates/radicle/src/test.rs @@ -59,7 +59,8 @@ pub fn fetch( drop(opts); repo.set_identity_head()?; - repo.set_head()?; + repo.set_head_to_default_branch()?; + repo.set_default_branch_to_canonical_head()?; let validations = repo.validate()?; if !validations.is_empty() { diff --git a/crates/radicle/src/test/storage.rs b/crates/radicle/src/test/storage.rs index f1b1ae21..c32881af 100644 --- a/crates/radicle/src/test/storage.rs +++ b/crates/radicle/src/test/storage.rs @@ -344,7 +344,11 @@ impl WriteRepository for MockRepository { todo!() } - fn set_head(&self) -> Result { + fn set_head_to_default_branch(&self) -> Result<(), RepositoryError> { + todo!() + } + + fn set_default_branch_to_canonical_head(&self) -> Result { todo!() }