1use std::path::PathBuf;
23use crate::store_impl::{file, packed};
45impl file::Store {
6/// Return a packed transaction ready to receive updates. Use this to create or update `packed-refs`.
7 /// Note that if you already have a [`packed::Buffer`] then use its [`packed::Buffer::into_transaction()`] method instead.
8pub(crate) fn packed_transaction(
9&self,
10 lock_mode: git_lock::acquire::Fail,
11 ) -> Result<packed::Transaction, transaction::Error> {
12let lock = git_lock::File::acquire_to_update_resource(self.packed_refs_path(), lock_mode, None)?;
13// We 'steal' the possibly existing packed buffer which may safe time if it's already there and fresh.
14 // If nothing else is happening, nobody will get to see the soon stale buffer either, but if so, they will pay
15 // for reloading it. That seems preferred over always loading up a new one.
16Ok(packed::Transaction::new_from_pack_and_lock(
17self.assure_packed_refs_uptodate()?,
18 lock,
19 ))
20 }
2122/// Try to open a new packed buffer. It's not an error if it doesn't exist, but yields `Ok(None)`.
23pub fn open_packed_buffer(&self) -> Result<Option<packed::Buffer>, packed::buffer::open::Error> {
24let need_more_than_this_many_bytes_to_use_mmap = 32 * 1024;
25match packed::Buffer::open(self.packed_refs_path(), need_more_than_this_many_bytes_to_use_mmap) {
26Ok(buf) => Ok(Some(buf)),
27Err(packed::buffer::open::Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
28Err(err) => Err(err),
29 }
30 }
3132/// Return a possibly cached packed buffer with shared ownership. At retrieval it will assure it's up to date, but
33 /// after that it can be considered a snapshot as it cannot change anymore.
34 ///
35 /// Use this to make successive calls to [`file::Store::try_find_packed()`]
36 /// or obtain iterators using [`file::Store::iter_packed()`] in a way that assures the packed-refs content won't change.
37pub fn cached_packed_buffer(
38&self,
39 ) -> Result<Option<file::packed::SharedBufferSnapshot>, packed::buffer::open::Error> {
40self.assure_packed_refs_uptodate()
41 }
4243/// Return the path at which packed-refs would usually be stored
44pub fn packed_refs_path(&self) -> PathBuf {
45self.common_dir_resolved().join("packed-refs")
46 }
4748pub(crate) fn packed_refs_lock_path(&self) -> PathBuf {
49let mut p = self.packed_refs_path();
50 p.set_extension("lock");
51 p
52 }
53}
5455///
56pub mod transaction {
5758use crate::store_impl::packed;
5960/// The error returned by [`file::Transaction::prepare()`][crate::file::Transaction::prepare()].
61#[derive(Debug, thiserror::Error)]
62 #[allow(missing_docs)]
63pub enum Error {
64#[error("An existing pack couldn't be opened or read when preparing a transaction")]
65BufferOpen(#[from] packed::buffer::open::Error),
66#[error("The lock for a packed transaction could not be obtained")]
67TransactionLock(#[from] git_lock::acquire::Error),
68 }
69}
7071/// An up-to-date snapshot of the packed refs buffer.
72pub type SharedBufferSnapshot = git_features::fs::SharedSnapshot<packed::Buffer>;
7374pub(crate) mod modifiable {
75use git_features::threading::OwnShared;
7677use crate::{file, packed};
7879pub(crate) type MutableSharedBuffer = OwnShared<git_features::fs::MutableSnapshot<packed::Buffer>>;
8081impl file::Store {
82pub(crate) fn force_refresh_packed_buffer(&self) -> Result<(), packed::buffer::open::Error> {
83self.packed.force_refresh(|| {
84let modified = self.packed_refs_path().metadata()?.modified()?;
85self.open_packed_buffer().map(|packed| Some(modified).zip(packed))
86 })
87 }
88pub(crate) fn assure_packed_refs_uptodate(
89&self,
90 ) -> Result<Option<super::SharedBufferSnapshot>, packed::buffer::open::Error> {
91self.packed.recent_snapshot(
92 || self.packed_refs_path().metadata().and_then(|m| m.modified()).ok(),
93 || self.open_packed_buffer(),
94 )
95 }
96 }
97}