Re-use `HEAD` reference if it exists

Since the `HEAD` reference is trusted (we set it locally), use that
when possible, instead of re-computing it.

Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
Alexis Sellier 2022-10-20 12:44:51 +02:00
parent 2ec320acec
commit 1906124497
No known key found for this signature in database
5 changed files with 56 additions and 20 deletions

View File

@ -31,8 +31,12 @@ pub const PROTOCOL_PORT: u16 = 9418;
#[derive(thiserror::Error, Debug)]
pub enum RefError {
#[error("invalid ref name '{0}'")]
InvalidName(format::RefString),
#[error("ref name is not valid UTF-8")]
InvalidName,
#[error("unexpected symbolic ref: {0}")]
Symbolic(RefString),
#[error("unexpected unqualified ref: {0}")]
Unqualified(RefString),
#[error("invalid ref format: {0}")]
Format(#[from] format::Error),
#[error("expected ref to begin with 'refs/namespaces' but found '{0}'")]
@ -67,8 +71,20 @@ pub mod refs {
/// Where project information is kept.
pub static IDENTITY_BRANCH: Lazy<RefString> = Lazy::new(|| refname!("radicle/id"));
pub mod storage {
/// Try to get a qualified reference from a generic reference.
pub fn qualified_from<'a>(r: &'a git2::Reference) -> Result<(Qualified<'a>, Oid), RefError> {
let name = r.name().ok_or(RefError::InvalidName)?;
let refstr = RefStr::try_from_str(name)?;
let target = r
.target()
.ok_or_else(|| RefError::Symbolic(refstr.to_owned()))?;
let qualified = Qualified::from_refstr(refstr)
.ok_or_else(|| RefError::Unqualified(refstr.to_owned()))?;
Ok((qualified, target.into()))
}
pub mod storage {
use super::*;
/// Create the [`Namespaced`] `branch` under the `remote` namespace, i.e.
@ -157,7 +173,7 @@ where
match input.to_namespaced() {
None => {
let refname = Qualified::from_refstr(input)
.ok_or_else(|| RefError::InvalidName(input.to_owned()))?;
.ok_or_else(|| RefError::Unqualified(input.to_owned()))?;
Ok((None, refname))
}

View File

@ -154,15 +154,13 @@ pub fn fork<G: Signer, S: storage::WriteStorage>(
) -> Result<(), ForkError> {
let me = signer.public_key();
let repository = storage.repository(proj)?;
let (canonical_id, project) = repository.project_identity()?;
let (canonical_head, _) = repository.head()?;
// TODO: We should get the id branch pointer from a stored canonical reference.
let (canonical_id, _) = repository.project_identity()?;
let (canonical_branch, canonical_head) = repository.head()?;
let raw = repository.raw();
// TODO: We should only get the project HEAD once we've stored the canonical identity
// branch on disk. This way it can use what we stored, instead of recomputing it.
raw.reference(
&git::refs::storage::branch(me, &project.default_branch),
&canonical_branch.with_namespace(me.into()),
*canonical_head,
false,
&format!("creating default branch for {me}"),
@ -353,7 +351,7 @@ mod tests {
.unwrap();
let project_repo = storage.repository(proj).unwrap();
let (head, _) = project_repo.head().unwrap();
let (_, head) = project_repo.head().unwrap();
// Test canonical refs.
assert_eq!(project_repo.raw().refname_to_id("HEAD").unwrap(), *head);

View File

@ -242,10 +242,20 @@ pub trait ReadRepository {
fn blob_at<'a>(&'a self, oid: Oid, path: &'a Path) -> Result<git2::Blob<'a>, git_ext::Error>;
/// Get the canonical head of this repository.
/// Get the head of this repository.
///
/// Returns the value of `HEAD` if it is set, otherwise computes the canonical head
/// using [`ReadRepository::canonical_head`].
///
/// Returns the [`Oid`] as well as the qualified reference name.
fn head(&self) -> Result<(Oid, Qualified), ProjectError>;
fn head(&self) -> Result<(Qualified, Oid), ProjectError>;
/// Compute the canonical head of this repository.
///
/// Ignores any existing `HEAD` reference.
///
/// Returns the [`Oid`] as well as the qualified reference name.
fn canonical_head(&self) -> Result<(Qualified, Oid), ProjectError>;
/// Get the `reference` for the given `remote`.
///

View File

@ -487,10 +487,18 @@ impl ReadRepository for Repository {
Repository::project(self)
}
fn head(&self) -> Result<(Oid, Qualified), ProjectError> {
// TODO: We shouldn't need to re-construct the history here; we should use the cached
// document head of the "trusted" (local) user for this project.
// In the `fork` function for example, we call Repository::project_identity again,
fn head(&self) -> Result<(Qualified, Oid), ProjectError> {
// If `HEAD` is already set locally, just return that.
if let Ok(head) = self.backend.head() {
if let Ok((name, oid)) = git::refs::qualified_from(&head) {
return Ok((name.to_owned(), oid));
}
}
self.canonical_head()
}
fn canonical_head(&self) -> Result<(Qualified, Oid), ProjectError> {
// TODO: In the `fork` function for example, we call Repository::project_identity again,
// This should only be necessary once.
let (_, project) = self.project_identity()?;
let branch_ref = Qualified::from(lit::refs_heads(&project.default_branch));
@ -509,7 +517,7 @@ impl ReadRepository for Repository {
heads => raw.merge_base_many(heads),
}?;
Ok((oid.into(), branch_ref))
Ok((branch_ref, oid.into()))
}
}
@ -631,7 +639,7 @@ impl WriteRepository for Repository {
fn set_head(&self) -> Result<Oid, ProjectError> {
let head_ref = refname!("HEAD");
let (head, branch_ref) = self.head()?;
let (branch_ref, head) = self.canonical_head()?;
log::debug!("Setting ref {:?} -> {:?}", &branch_ref, head);
self.raw()

View File

@ -78,7 +78,11 @@ impl ReadRepository for MockRepository {
Ok(true)
}
fn head(&self) -> Result<(Oid, fmt::Qualified), ProjectError> {
fn head(&self) -> Result<(fmt::Qualified, Oid), ProjectError> {
todo!()
}
fn canonical_head(&self) -> Result<(fmt::Qualified, Oid), ProjectError> {
todo!()
}