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}