git_odb/store_impls/dynamic/
mod.rs

1//! The standard object store which should fit all needs.
2use std::{cell::RefCell, ops::Deref};
3
4use crate::Store;
5
6/// This effectively acts like a handle but exists to be usable from the actual `crate::Handle` implementation which adds caches on top.
7/// Each store is quickly cloned and contains thread-local state for shared packs.
8pub struct Handle<S>
9where
10    S: Deref<Target = Store> + Clone,
11{
12    pub(crate) store: S,
13    /// Defines what happens when there is no more indices to load.
14    pub refresh: RefreshMode,
15    /// The maximum recursion depth for resolving ref-delta base objects, that is objects referring to other objects within
16    /// a pack.
17    /// Recursive loops are possible only in purposefully crafted packs.
18    /// This value doesn't have to be huge as in typical scenarios, these kind of objects are rare and chains supposedly are
19    /// even more rare.
20    pub max_recursion_depth: usize,
21
22    /// If true, replacements will not be performed even if these are available.
23    pub ignore_replacements: bool,
24
25    pub(crate) token: Option<handle::Mode>,
26    snapshot: RefCell<load_index::Snapshot>,
27    packed_object_count: RefCell<Option<u64>>,
28}
29
30/// Decide what happens when all indices are loaded.
31#[derive(Clone, Copy)]
32pub enum RefreshMode {
33    /// Check for new or changed pack indices (and pack data files) when the last known index is loaded.
34    /// During runtime we will keep pack indices stable by never reusing them, however, there is the option for
35    /// clearing internal caches which is likely to change pack ids and it will trigger unloading of packs as they are missing on disk.
36    AfterAllIndicesLoaded,
37    /// Use this if you expect a lot of missing objects that shouldn't trigger refreshes even after all packs are loaded.
38    /// This comes at the risk of not learning that the packs have changed in the mean time.
39    Never,
40}
41
42impl Default for RefreshMode {
43    fn default() -> Self {
44        RefreshMode::AfterAllIndicesLoaded
45    }
46}
47
48impl RefreshMode {
49    /// Set this refresh mode to never refresh.
50    pub fn never(&mut self) {
51        *self = RefreshMode::Never;
52    }
53}
54
55///
56pub mod find;
57
58///
59pub mod prefix;
60
61mod header;
62
63///
64pub mod iter;
65
66///
67pub mod write;
68
69///
70pub mod init;
71
72pub(crate) mod types;
73pub use types::Metrics;
74
75pub(crate) mod handle;
76
77///
78pub mod load_index;
79
80///
81pub mod verify;
82
83mod load_one;
84
85mod metrics;
86
87mod access;
88
89///
90pub mod structure {
91    use std::path::PathBuf;
92
93    use crate::{store::load_index, types::IndexAndPacks, Store};
94
95    /// A record of a structural element of an object database.
96    #[derive(Debug, Clone, PartialEq, Eq)]
97    #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
98    pub enum Record {
99        /// A loose object database.
100        LooseObjectDatabase {
101            /// The root of the object database.
102            objects_directory: PathBuf,
103            /// The amount of object files.
104            num_objects: usize,
105        },
106        /// A pack index file
107        Index {
108            /// The location of the index file,
109            path: PathBuf,
110            /// Whether or not the index is mapped into memory.
111            state: IndexState,
112        },
113        /// A multi-index file
114        MultiIndex {
115            /// The location of the multi-index file,
116            path: PathBuf,
117            /// Whether or not the index is mapped into memory.
118            state: IndexState,
119        },
120        /// An empty slot was encountered, this is possibly happening as the ODB changes during query with
121        /// a file being removed.
122        Empty,
123    }
124
125    #[derive(Debug, Clone, PartialEq, Eq)]
126    #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
127    /// Possible stats of pack indices.
128    pub enum IndexState {
129        /// The index is active in memory because a mapping exists.
130        Loaded,
131        /// The index couldn't be unloaded as it was still in use, but that can happen another time.
132        Disposable,
133        /// The index isn't loaded/memory mapped.
134        Unloaded,
135    }
136
137    impl Store {
138        /// Return information about all files known to us as well as their loading state.
139        ///
140        /// Note that this call is expensive as it gathers additional information about loose object databases.
141        /// Note that it may change as we collect information due to the highly volatile nature of the
142        /// implementation. The likelihood of actual changes is low though as these still depend on something
143        /// changing on disk and somebody reading at the same time.
144        pub fn structure(&self) -> Result<Vec<Record>, load_index::Error> {
145            let index = self.index.load();
146            if !index.is_initialized() {
147                self.consolidate_with_disk_state(true, false /*load one new index*/)?;
148            }
149            let index = self.index.load();
150            let mut res: Vec<_> = index
151                .loose_dbs
152                .iter()
153                .map(|db| Record::LooseObjectDatabase {
154                    objects_directory: db.path.clone(),
155                    num_objects: db.iter().count(),
156                })
157                .collect();
158
159            for slot in index.slot_indices.iter().map(|idx| &self.files[*idx]) {
160                let files = slot.files.load();
161                let record = match &**files {
162                    Some(index) => {
163                        let state = if index.is_disposable() {
164                            IndexState::Disposable
165                        } else if index.index_is_loaded() {
166                            IndexState::Loaded
167                        } else {
168                            IndexState::Unloaded
169                        };
170                        match index {
171                            IndexAndPacks::Index(b) => Record::Index {
172                                path: b.index.path().into(),
173                                state,
174                            },
175                            IndexAndPacks::MultiIndex(b) => Record::MultiIndex {
176                                path: b.multi_index.path().into(),
177                                state,
178                            },
179                        }
180                    }
181                    None => Record::Empty,
182                };
183                res.push(record);
184            }
185            Ok(res)
186        }
187    }
188}