git_ref/store/file/
packed.rs

1use std::path::PathBuf;
2
3use crate::store_impl::{file, packed};
4
5impl 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.
8    pub(crate) fn packed_transaction(
9        &self,
10        lock_mode: git_lock::acquire::Fail,
11    ) -> Result<packed::Transaction, transaction::Error> {
12        let 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.
16        Ok(packed::Transaction::new_from_pack_and_lock(
17            self.assure_packed_refs_uptodate()?,
18            lock,
19        ))
20    }
21
22    /// Try to open a new packed buffer. It's not an error if it doesn't exist, but yields `Ok(None)`.
23    pub fn open_packed_buffer(&self) -> Result<Option<packed::Buffer>, packed::buffer::open::Error> {
24        let need_more_than_this_many_bytes_to_use_mmap = 32 * 1024;
25        match packed::Buffer::open(self.packed_refs_path(), need_more_than_this_many_bytes_to_use_mmap) {
26            Ok(buf) => Ok(Some(buf)),
27            Err(packed::buffer::open::Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
28            Err(err) => Err(err),
29        }
30    }
31
32    /// 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.
37    pub fn cached_packed_buffer(
38        &self,
39    ) -> Result<Option<file::packed::SharedBufferSnapshot>, packed::buffer::open::Error> {
40        self.assure_packed_refs_uptodate()
41    }
42
43    /// Return the path at which packed-refs would usually be stored
44    pub fn packed_refs_path(&self) -> PathBuf {
45        self.common_dir_resolved().join("packed-refs")
46    }
47
48    pub(crate) fn packed_refs_lock_path(&self) -> PathBuf {
49        let mut p = self.packed_refs_path();
50        p.set_extension("lock");
51        p
52    }
53}
54
55///
56pub mod transaction {
57
58    use crate::store_impl::packed;
59
60    /// The error returned by [`file::Transaction::prepare()`][crate::file::Transaction::prepare()].
61    #[derive(Debug, thiserror::Error)]
62    #[allow(missing_docs)]
63    pub enum Error {
64        #[error("An existing pack couldn't be opened or read when preparing a transaction")]
65        BufferOpen(#[from] packed::buffer::open::Error),
66        #[error("The lock for a packed transaction could not be obtained")]
67        TransactionLock(#[from] git_lock::acquire::Error),
68    }
69}
70
71/// An up-to-date snapshot of the packed refs buffer.
72pub type SharedBufferSnapshot = git_features::fs::SharedSnapshot<packed::Buffer>;
73
74pub(crate) mod modifiable {
75    use git_features::threading::OwnShared;
76
77    use crate::{file, packed};
78
79    pub(crate) type MutableSharedBuffer = OwnShared<git_features::fs::MutableSnapshot<packed::Buffer>>;
80
81    impl file::Store {
82        pub(crate) fn force_refresh_packed_buffer(&self) -> Result<(), packed::buffer::open::Error> {
83            self.packed.force_refresh(|| {
84                let modified = self.packed_refs_path().metadata()?.modified()?;
85                self.open_packed_buffer().map(|packed| Some(modified).zip(packed))
86            })
87        }
88        pub(crate) fn assure_packed_refs_uptodate(
89            &self,
90        ) -> Result<Option<super::SharedBufferSnapshot>, packed::buffer::open::Error> {
91            self.packed.recent_snapshot(
92                || self.packed_refs_path().metadata().and_then(|m| m.modified()).ok(),
93                || self.open_packed_buffer(),
94            )
95        }
96    }
97}