use std::ops::ControlFlow;
use crate::error::Result;
use crate::objects::ObjectId;
use crate::reflog::ReflogEntry;
use crate::refs::store::{Expected, RawRef, RefName, RefUpdate, RefUpdateFlags};
use crate::refs::{
commit_ref_update_on_store, ensure_refname_safe_for_storage, list_refs_for_repository,
update_direct_ref_on_store,
};
use crate::repo::Repository;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ExistingRef {
pub(crate) raw: RawRef,
pub(crate) oid: ObjectId,
}
#[derive(Debug, Copy, Clone)]
pub struct References<'repo> {
repo: &'repo Repository,
}
impl<'repo> References<'repo> {
#[inline]
#[must_use]
pub fn new(repo: &'repo Repository) -> Self {
Self { repo }
}
#[inline]
#[must_use]
pub fn repository(&self) -> &'repo Repository {
self.repo
}
#[inline]
pub fn resolve(&self, name: &RefName) -> Result<ObjectId> {
self.repo.resolve_ref_name(name.as_str())
}
pub(crate) fn read_existing(&self, name: &str) -> Result<Option<ExistingRef>> {
let Some(raw) = self.repo.refs().read_raw(name)? else {
return Ok(None);
};
let oid = match &raw {
RawRef::Direct(oid) => *oid,
RawRef::Symbolic(_) => self.repo.resolve_ref_name(name)?,
};
Ok(Some(ExistingRef { raw, oid }))
}
pub(crate) fn resolve_if_exists(&self, name: &str) -> Result<Option<ObjectId>> {
Ok(self.read_existing(name)?.map(|existing| existing.oid))
}
pub(crate) fn delete_verified(&self, name: &str, existing: &ExistingRef) -> Result<()> {
ensure_refname_safe_for_storage(name)?;
let (expected, no_deref) = match &existing.raw {
RawRef::Symbolic(target) => (Expected::Symref(target.clone()), true),
RawRef::Direct(oid) => (Expected::Oid(*oid), false),
};
let store = self.repo.refs();
commit_ref_update_on_store(
store,
RefUpdate {
name: name.to_owned(),
new_value: None,
expected,
reflog: None,
flags: RefUpdateFlags {
no_deref,
..RefUpdateFlags::default()
},
},
)?;
store.delete_reflog(name)
}
#[inline]
pub fn list(&self, prefix: &str) -> Result<Vec<(String, ObjectId)>> {
list_refs_for_repository(self.repo, prefix)
}
#[inline]
pub fn update(&self, name: &RefName, oid: &ObjectId) -> Result<()> {
update_direct_ref_on_store(self.repo.refs(), self.repo.git_dir(), name.as_str(), oid)
}
#[inline]
pub fn reflog(&self, name: &RefName) -> Result<Vec<ReflogEntry>> {
let mut entries = Vec::new();
self.repo
.refs()
.for_each_reflog_entry(name.as_str(), false, &mut |entry| {
entries.push(entry.clone());
ControlFlow::Continue(())
})?;
Ok(entries)
}
pub fn pack(
&self,
opts: &crate::maintenance::PackRefsOptions,
) -> Result<crate::maintenance::PackRefsReport> {
crate::maintenance::pack_refs(self.repo, opts)
}
}