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}