Skip to main content

gix_ref/store/file/
packed.rs

1use gix_error::{ExnResult, ResultExt, message};
2
3use std::path::PathBuf;
4
5use crate::store_impl::{file, packed};
6
7impl file::Store {
8    /// Return a packed transaction ready to receive updates. Use this to create or update `packed-refs`.
9    /// Note that if you already have a [`packed::Buffer`] then use its [`packed::Buffer::into_transaction()`] method instead.
10    pub(crate) fn packed_transaction(&self, lock_mode: gix_lock::acquire::Fail) -> ExnResult<packed::Transaction> {
11        let lock = gix_lock::File::acquire_to_update_resource(self.packed_refs_path(), lock_mode, None)
12            .or_raise_erased(|| message("Could not lock packed refs"))?;
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) -> ExnResult<Option<packed::Buffer>> {
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(err) if err.is_not_found() => 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(&self) -> ExnResult<Option<file::packed::SharedBufferSnapshot>> {
46        self.assure_packed_refs_uptodate()
47    }
48
49    /// Return the path at which packed-refs would usually be stored
50    pub fn packed_refs_path(&self) -> PathBuf {
51        self.common_dir_resolved().join("packed-refs")
52    }
53
54    pub(crate) fn packed_refs_lock_path(&self) -> PathBuf {
55        let mut p = self.packed_refs_path();
56        p.set_extension("lock");
57        p
58    }
59}
60
61/// An up-to-date snapshot of the packed refs buffer.
62pub type SharedBufferSnapshot = gix_fs::SharedFileSnapshot<packed::Buffer>;
63
64pub(crate) mod modifiable {
65    use gix_features::threading::OwnShared;
66
67    use crate::{file, packed};
68    use gix_error::{ExnResult, Message, ResultExt};
69
70    pub(crate) type MutableSharedBuffer = OwnShared<gix_fs::SharedFileSnapshotMut<packed::Buffer>>;
71
72    impl file::Store {
73        /// Forcefully reload the packed refs buffer.
74        ///
75        /// This method should be used if it's clear that the buffer on disk has changed, to
76        /// make the latest changes visible before other operations are done on this instance.
77        ///
78        /// As some filesystems don't have nanosecond granularity, changes are likely to be missed
79        /// if they happen within one second otherwise.
80        ///
81        /// [Metadata](gix_error::Exn::metadata()) `path` (native path) identifies a packed-refs file whose modification
82        /// time could not be read.
83        pub fn force_refresh_packed_buffer(&self) -> ExnResult {
84            self.packed.force_refresh(|| {
85                let path = self.packed_refs_path();
86                let modified = path
87                    .metadata()
88                    .and_then(|metadata| metadata.modified())
89                    .or_raise_erased(|| {
90                        Message::new("Could not read packed refs modification time").with("path", path)
91                    })?;
92                self.open_packed_buffer().map(|packed| Some(modified).zip(packed))
93            })
94        }
95        pub(crate) fn assure_packed_refs_uptodate(&self) -> ExnResult<Option<super::SharedBufferSnapshot>> {
96            self.packed.recent_snapshot(
97                || self.packed_refs_path().metadata().and_then(|m| m.modified()).ok(),
98                || self.open_packed_buffer(),
99            )
100        }
101    }
102}