gix_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: gix_lock::acquire::Fail,
11    ) -> Result<packed::Transaction, transaction::Error> {
12        let lock = gix_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            self.precompose_unicode,
20            self.namespace.clone(),
21        ))
22    }
23
24    /// Try to open a new packed buffer. It's not an error if it doesn't exist, but yields `Ok(None)`.
25    ///
26    /// Note that it will automatically be memory mapped if it exceeds the default threshold of 32KB.
27    /// Change the threshold with [file::Store::set_packed_buffer_mmap_threshold()].
28    pub fn open_packed_buffer(&self) -> Result<Option<packed::Buffer>, packed::buffer::open::Error> {
29        match packed::Buffer::open(self.packed_refs_path(), self.packed_buffer_mmap_threshold) {
30            Ok(buf) => Ok(Some(buf)),
31            Err(packed::buffer::open::Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
32            Err(err) => Err(err),
33        }
34    }
35
36    /// Return a possibly cached packed buffer with shared ownership. At retrieval it will assure it's up to date, but
37    /// after that it can be considered a snapshot as it cannot change anymore.
38    ///
39    /// Use this to make successive calls to [`file::Store::try_find_packed()`]
40    /// or obtain iterators using [`file::Store::iter_packed()`] in a way that assures the packed-refs content won't change.
41    pub fn cached_packed_buffer(
42        &self,
43    ) -> Result<Option<file::packed::SharedBufferSnapshot>, packed::buffer::open::Error> {
44        self.assure_packed_refs_uptodate()
45    }
46
47    /// Return the path at which packed-refs would usually be stored
48    pub fn packed_refs_path(&self) -> PathBuf {
49        self.common_dir_resolved().join("packed-refs")
50    }
51
52    pub(crate) fn packed_refs_lock_path(&self) -> PathBuf {
53        let mut p = self.packed_refs_path();
54        p.set_extension("lock");
55        p
56    }
57}
58
59///
60pub mod transaction {
61
62    use crate::store_impl::packed;
63
64    /// The error returned by [`file::Transaction::prepare()`][crate::file::Transaction::prepare()].
65    #[derive(Debug, thiserror::Error)]
66    #[allow(missing_docs)]
67    pub enum Error {
68        #[error("An existing pack couldn't be opened or read when preparing a transaction")]
69        BufferOpen(#[from] packed::buffer::open::Error),
70        #[error("The lock for a packed transaction could not be obtained")]
71        TransactionLock(#[from] gix_lock::acquire::Error),
72    }
73}
74
75/// An up-to-date snapshot of the packed refs buffer.
76pub type SharedBufferSnapshot = gix_fs::SharedFileSnapshot<packed::Buffer>;
77
78pub(crate) mod modifiable {
79    use gix_features::threading::OwnShared;
80
81    use crate::{file, packed};
82
83    pub(crate) type MutableSharedBuffer = OwnShared<gix_fs::SharedFileSnapshotMut<packed::Buffer>>;
84
85    impl file::Store {
86        /// Forcefully reload the packed refs buffer.
87        ///
88        /// This method should be used if it's clear that the buffer on disk has changed, to
89        /// make the latest changes visible before other operations are done on this instance.
90        ///
91        /// As some filesystems don't have nanosecond granularity, changes are likely to be missed
92        /// if they happen within one second otherwise.
93        pub fn force_refresh_packed_buffer(&self) -> Result<(), packed::buffer::open::Error> {
94            self.packed.force_refresh(|| {
95                let modified = self.packed_refs_path().metadata()?.modified()?;
96                self.open_packed_buffer().map(|packed| Some(modified).zip(packed))
97            })
98        }
99        pub(crate) fn assure_packed_refs_uptodate(
100            &self,
101        ) -> Result<Option<super::SharedBufferSnapshot>, packed::buffer::open::Error> {
102            self.packed.recent_snapshot(
103                || self.packed_refs_path().metadata().and_then(|m| m.modified()).ok(),
104                || self.open_packed_buffer(),
105            )
106        }
107    }
108}