blob: 4e48b6a8c0fc3ac647ba1edca740d286818fc126 [file] [log] [blame]
use std::path::PathBuf;
use crate::store_impl::{file, packed};
impl file::Store {
/// Return a packed transaction ready to receive updates. Use this to create or update `packed-refs`.
/// Note that if you already have a [`packed::Buffer`] then use its [`packed::Buffer::into_transaction()`] method instead.
pub(crate) fn packed_transaction(
&self,
lock_mode: gix_lock::acquire::Fail,
) -> Result<packed::Transaction, transaction::Error> {
let lock = gix_lock::File::acquire_to_update_resource(self.packed_refs_path(), lock_mode, None)?;
// We 'steal' the possibly existing packed buffer which may safe time if it's already there and fresh.
// If nothing else is happening, nobody will get to see the soon stale buffer either, but if so, they will pay
// for reloading it. That seems preferred over always loading up a new one.
Ok(packed::Transaction::new_from_pack_and_lock(
self.assure_packed_refs_uptodate()?,
lock,
))
}
/// Try to open a new packed buffer. It's not an error if it doesn't exist, but yields `Ok(None)`.
///
/// Note that it will automatically be memory mapped if it exceeds the default threshold of 32KB.
/// Change the threshold with [file::Store::set_packed_buffer_mmap_threshold()].
pub fn open_packed_buffer(&self) -> Result<Option<packed::Buffer>, packed::buffer::open::Error> {
match packed::Buffer::open(self.packed_refs_path(), self.packed_buffer_mmap_threshold) {
Ok(buf) => Ok(Some(buf)),
Err(packed::buffer::open::Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(err) => Err(err),
}
}
/// Return a possibly cached packed buffer with shared ownership. At retrieval it will assure it's up to date, but
/// after that it can be considered a snapshot as it cannot change anymore.
///
/// Use this to make successive calls to [`file::Store::try_find_packed()`]
/// or obtain iterators using [`file::Store::iter_packed()`] in a way that assures the packed-refs content won't change.
pub fn cached_packed_buffer(
&self,
) -> Result<Option<file::packed::SharedBufferSnapshot>, packed::buffer::open::Error> {
self.assure_packed_refs_uptodate()
}
/// Return the path at which packed-refs would usually be stored
pub fn packed_refs_path(&self) -> PathBuf {
self.common_dir_resolved().join("packed-refs")
}
pub(crate) fn packed_refs_lock_path(&self) -> PathBuf {
let mut p = self.packed_refs_path();
p.set_extension("lock");
p
}
}
///
pub mod transaction {
use crate::store_impl::packed;
/// The error returned by [`file::Transaction::prepare()`][crate::file::Transaction::prepare()].
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error("An existing pack couldn't be opened or read when preparing a transaction")]
BufferOpen(#[from] packed::buffer::open::Error),
#[error("The lock for a packed transaction could not be obtained")]
TransactionLock(#[from] gix_lock::acquire::Error),
}
}
/// An up-to-date snapshot of the packed refs buffer.
pub type SharedBufferSnapshot = gix_fs::SharedFileSnapshot<packed::Buffer>;
pub(crate) mod modifiable {
use gix_features::threading::OwnShared;
use crate::{file, packed};
pub(crate) type MutableSharedBuffer = OwnShared<gix_fs::SharedFileSnapshotMut<packed::Buffer>>;
impl file::Store {
pub(crate) fn force_refresh_packed_buffer(&self) -> Result<(), packed::buffer::open::Error> {
self.packed.force_refresh(|| {
let modified = self.packed_refs_path().metadata()?.modified()?;
self.open_packed_buffer().map(|packed| Some(modified).zip(packed))
})
}
pub(crate) fn assure_packed_refs_uptodate(
&self,
) -> Result<Option<super::SharedBufferSnapshot>, packed::buffer::open::Error> {
self.packed.recent_snapshot(
|| self.packed_refs_path().metadata().and_then(|m| m.modified()).ok(),
|| self.open_packed_buffer(),
)
}
}
}