Skip to main content

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