1use flate2::Compression;
22use flate2::read::ZlibDecoder;
23use flate2::write::ZlibEncoder;
24use flate2::{Decompress, FlushDecompress};
25use parking_lot::RwLock;
26use std::sync::Mutex;
27use sley_core::{GitError, MissingObjectContext, ObjectFormat, ObjectId, Result};
28use sley_formats::{Bundle, BundleReference};
29use sley_object::{
30 Commit, EncodedObject, ObjectType, Tag, TreeEntries, parse_framed_object,
31 tree_entry_object_type,
32};
33use sley_pack::{
34 MultiPackIndex, MultiPackIndexOidLookup, PackBitmapIndex, PackBitmapWriter, PackFile,
35 PackIndex, PackIndexByteSource, PackIndexEntry, PackIndexViewData, PackInput,
36 PackReverseIndex, PackStreamIndexBuild, PackWrite, PackWriteOptions, PackWriteSummary,
37};
38use std::collections::{HashMap, HashSet};
39use std::io::{Read, Write};
40use std::path::{Path, PathBuf};
41use std::sync::atomic::{AtomicU64, Ordering};
42use std::sync::{Arc, OnceLock};
43use std::{env, fs};
44
45use crate::{
46 grafted_parents, implied_empty_tree_object, unique_temp_path, with_missing_object_context,
47 ObjectReader, ObjectWriter,
48};
49
50use crate::install::{ObjectPrefixResolution, ObjectStorageInfo};
51use crate::reachability::{loose_object_ids, loose_object_id_set, zero_oid, pack_entry_delta_base, scan_pack_index_offsets, scan_pack_offsets_without_index, remove_file_if_exists, PackDeltaBase, PackIndexOffsetInfo};
52use crate::loose::{LooseObjectStore, collect_loose_fanout_object_ids, collect_loose_object_ids, present_loose_fanouts};
53use crate::registry::{
54 PackRegistryCache, PackDirFingerprint, PackLookup, PackRegistrySnapshot, RegisteredPack, alternate_object_dirs,
55 collect_incremental_midx_object_ids, collect_incremental_midx_prefix_matches,
56 collect_loose_object_ids_with_prefix, collect_multi_pack_index_prefix_matches,
57 collect_pack_index_prefix_matches, collect_packed_object_ids, collect_packed_object_ids_with_prefix,
58 lower_bound_pack_index_entries, object_id_floor_for_hex_prefix, pack_index_fanout_range,
59 object_ids_in_objects_dir, object_ids_with_prefix_in_objects_dir,
60 read_incremental_midx_chain, repository_common_dir, repository_objects_dir,
61 same_registered_pack_set, scan_pack_registry, validate_object_id_prefix, ObjectPresenceChecker,
62};
63
64pub struct ObjectDatabase {
65 pub(crate) format: ObjectFormat,
66 pub(crate) objects: Mutex<HashMap<ObjectId, Arc<EncodedObject>>>,
72 pub(crate) promisor: bool,
73}
74
75impl ObjectDatabase {
76 pub fn new(format: ObjectFormat) -> Self {
77 Self {
78 format,
79 objects: Mutex::new(HashMap::new()),
80 promisor: false,
81 }
82 }
83
84 pub fn with_promisor(mut self, promisor: bool) -> Self {
85 self.promisor = promisor;
86 self
87 }
88
89 pub fn contains(&self, oid: &ObjectId) -> bool {
90 self.objects
91 .lock()
92 .map(|objects| objects.contains_key(oid))
93 .unwrap_or(false)
94 }
95
96 pub fn validate(&self, oid: &ObjectId) -> Result<()> {
97 let object = self.read_object(oid)?;
98 let actual = object.object_id(self.format)?;
99 if &actual == oid {
100 Ok(())
101 } else {
102 Err(GitError::InvalidObject(format!(
103 "object id mismatch: expected {oid}, got {actual}"
104 )))
105 }
106 }
107}
108
109impl ObjectReader for ObjectDatabase {
110 fn read_object(&self, oid: &ObjectId) -> Result<Arc<EncodedObject>> {
111 self.objects
112 .lock()
113 .map_err(|_| GitError::object_not_found_in(*oid, MissingObjectContext::Read))?
114 .get(oid)
115 .map(Arc::clone)
116 .or_else(|| implied_empty_tree_object(self.format, oid))
117 .ok_or_else(|| GitError::object_not_found_in(*oid, MissingObjectContext::Read))
118 }
119}
120
121impl ObjectWriter for ObjectDatabase {
122 fn write_object(&self, object: EncodedObject) -> Result<ObjectId> {
123 let oid = object.object_id(self.format)?;
124 self.objects
125 .lock()
126 .map_err(|_| GitError::Io("object cache lock poisoned".into()))?
127 .entry(oid)
128 .or_insert_with(|| Arc::new(object));
129 Ok(oid)
130 }
131}
132
133#[derive(Debug, Clone, PartialEq, Eq)]
134pub struct Alternate {
135 pub path: std::path::PathBuf,
136}
137
138#[derive(Debug, Clone, PartialEq, Eq)]
139pub struct PartialClonePolicy {
140 pub promisor_remote: Option<String>,
141 pub allow_missing_promised_objects: bool,
142}
143
144pub(crate) type PackBytesCache = Arc<RwLock<HashMap<PathBuf, Arc<PackData>>>>;
148
149#[derive(Debug)]
152pub(crate) enum PackData {
153 #[cfg(feature = "mmap")]
154 Mapped(sley_mmap::MappedFile),
155 Heap(Vec<u8>),
156}
157
158impl std::ops::Deref for PackData {
159 type Target = [u8];
160
161 fn deref(&self) -> &[u8] {
162 match self {
163 #[cfg(feature = "mmap")]
164 Self::Mapped(mapped) => mapped,
165 Self::Heap(bytes) => bytes,
166 }
167 }
168}
169
170#[cfg(feature = "mmap")]
173pub(crate) fn load_pack_data(pack_path: &Path) -> Result<PackData> {
174 match sley_mmap::MappedFile::open_pack(pack_path) {
175 Ok(mapped) => Ok(PackData::Mapped(mapped)),
176 Err(_) => Ok(PackData::Heap(fs::read(pack_path)?)),
177 }
178}
179
180#[cfg(not(feature = "mmap"))]
181pub(crate) fn load_pack_data(pack_path: &Path) -> Result<PackData> {
182 Ok(PackData::Heap(fs::read(pack_path)?))
183}
184
185#[cfg(feature = "mmap")]
186pub(crate) fn load_pack_index_data(index_path: &Path) -> Result<Arc<dyn PackIndexByteSource>> {
187 match sley_mmap::MappedFile::open_pack(index_path) {
188 Ok(mapped) => Ok(Arc::new(mapped)),
189 Err(_) => Ok(Arc::new(fs::read(index_path)?)),
190 }
191}
192
193#[cfg(not(feature = "mmap"))]
194pub(crate) fn load_pack_index_data(index_path: &Path) -> Result<Arc<dyn PackIndexByteSource>> {
195 Ok(Arc::new(fs::read(index_path)?))
196}
197
198#[cfg(feature = "mmap")]
199fn load_multi_pack_index_lookup_data(midx_path: &Path) -> Result<Arc<dyn PackIndexByteSource>> {
200 match sley_mmap::MappedFile::open_multi_pack_index(midx_path) {
201 Ok(mapped) => Ok(Arc::new(mapped)),
202 Err(_) => Ok(Arc::new(fs::read(midx_path)?)),
203 }
204}
205
206#[cfg(not(feature = "mmap"))]
207fn load_multi_pack_index_lookup_data(midx_path: &Path) -> Result<Arc<dyn PackIndexByteSource>> {
208 Ok(Arc::new(fs::read(midx_path)?))
209}
210
211pub(crate) type DecodedObjectCache = Arc<Mutex<LruObjectCache>>;
217
218pub(crate) type PackDeltaCaches = Arc<Mutex<HashMap<PathBuf, Arc<Mutex<LruOffsetCache>>>>>;
223
224pub(crate) type PackHeaderTypeCache = Arc<Mutex<HashMap<u64, (ObjectType, u64)>>>;
234
235pub(crate) type PackHeaderTypeCaches = Arc<Mutex<HashMap<PathBuf, PackHeaderTypeCache>>>;
236
237const DEFAULT_OBJECT_CACHE_BYTES: usize = 96 * 1024 * 1024;
243
244const DEFAULT_DELTA_BASE_CACHE_BYTES: usize = 96 * 1024 * 1024;
248
249pub(crate) fn cached_object_cost(object: &EncodedObject) -> usize {
253 object.body.len().saturating_add(64)
254}
255
256fn cache_budget_from_env(var: &str, default: usize) -> usize {
259 match env::var(var) {
260 Ok(value) => value.trim().parse::<usize>().unwrap_or(default),
261 Err(_) => default,
262 }
263}
264
265pub(crate) fn object_cache_budget() -> usize {
272 static BUDGET: OnceLock<usize> = OnceLock::new();
273 *BUDGET.get_or_init(|| {
274 cache_budget_from_env("SLEY_OBJECT_CACHE_BYTES", DEFAULT_OBJECT_CACHE_BYTES)
275 })
276}
277
278pub(crate) fn delta_base_cache_budget() -> usize {
282 static BUDGET: OnceLock<usize> = OnceLock::new();
283 *BUDGET.get_or_init(|| {
284 cache_budget_from_env(
285 "SLEY_DELTA_BASE_CACHE_BYTES",
286 DEFAULT_DELTA_BASE_CACHE_BYTES,
287 )
288 })
289}
290
291pub(crate) fn verify_reads_enabled() -> bool {
302 static VERIFY: OnceLock<bool> = OnceLock::new();
303 *VERIFY.get_or_init(|| match env::var("SLEY_VERIFY_READS") {
304 Ok(value) => !matches!(value.trim(), "" | "0"),
305 Err(_) => false,
306 })
307}
308
309#[derive(Debug)]
317pub(crate) struct LruCache<K: std::hash::Hash + Eq + Clone> {
318 budget: usize,
319 used: usize,
320 map: HashMap<K, LruEntry<K>>,
321 head: Option<K>,
322 tail: Option<K>,
323}
324
325#[derive(Debug)]
326struct LruEntry<K> {
327 object: Arc<EncodedObject>,
328 prev: Option<K>,
329 next: Option<K>,
330}
331
332impl<K: std::hash::Hash + Eq + Clone> LruCache<K> {
333 pub(crate) fn new(budget: usize) -> Self {
334 Self {
335 budget,
336 used: 0,
337 map: HashMap::new(),
338 head: None,
339 tail: None,
340 }
341 }
342
343 pub(crate) fn get(&mut self, key: &K) -> Option<Arc<EncodedObject>> {
344 let object = Arc::clone(&self.map.get(key)?.object);
345 self.touch(key);
346 Some(object)
347 }
348
349 pub(crate) fn touch(&mut self, key: &K) {
351 if self.tail.as_ref() == Some(key) {
352 return;
353 }
354 if self.map.contains_key(key) {
355 self.detach(key);
356 self.attach_back(key.clone());
357 }
358 }
359
360 pub(crate) fn remove(&mut self, key: &K) {
362 if let Some(entry) = self.map.get(key) {
363 self.used = self.used.saturating_sub(cached_object_cost(&entry.object));
364 }
365 self.detach(key);
366 self.map.remove(key);
367 }
368
369 pub(crate) fn detach(&mut self, key: &K) {
370 let Some((prev, next)) = self.map.get_mut(key).map(|entry| {
371 let prev = entry.prev.take();
372 let next = entry.next.take();
373 (prev, next)
374 }) else {
375 return;
376 };
377
378 match &prev {
379 Some(prev_key) => {
380 if let Some(prev_entry) = self.map.get_mut(prev_key) {
381 prev_entry.next = next.clone();
382 }
383 }
384 None => self.head = next.clone(),
385 }
386 match &next {
387 Some(next_key) => {
388 if let Some(next_entry) = self.map.get_mut(next_key) {
389 next_entry.prev = prev.clone();
390 }
391 }
392 None => self.tail = prev.clone(),
393 }
394 }
395
396 pub(crate) fn attach_back(&mut self, key: K) {
397 let previous_tail = self.tail.replace(key.clone());
398 match previous_tail {
399 Some(tail_key) => {
400 if let Some(tail_entry) = self.map.get_mut(&tail_key) {
401 tail_entry.next = Some(key.clone());
402 }
403 if let Some(entry) = self.map.get_mut(&key) {
404 entry.prev = Some(tail_key);
405 entry.next = None;
406 }
407 }
408 None => {
409 self.head = Some(key.clone());
410 if let Some(entry) = self.map.get_mut(&key) {
411 entry.prev = None;
412 entry.next = None;
413 }
414 }
415 }
416 }
417
418 pub(crate) fn clear(&mut self) {
419 self.map.clear();
420 self.head = None;
421 self.tail = None;
422 self.used = 0;
423 }
424
425 pub(crate) fn put(&mut self, key: K, object: Arc<EncodedObject>) {
426 if self.budget == 0 {
427 return;
428 }
429 let cost = cached_object_cost(&object);
430 if cost > self.budget {
434 self.remove(&key);
435 return;
436 }
437 if let Some(entry) = self.map.get_mut(&key) {
438 let previous = std::mem::replace(&mut entry.object, object);
439 self.used = self
441 .used
442 .saturating_sub(cached_object_cost(&previous))
443 .saturating_add(cost);
444 self.touch(&key);
445 } else {
446 self.used = self.used.saturating_add(cost);
447 self.map.insert(
448 key.clone(),
449 LruEntry {
450 object,
451 prev: None,
452 next: None,
453 },
454 );
455 self.attach_back(key);
456 }
457 while self.used > self.budget {
458 let Some(evicted) = self.head.clone() else {
459 break;
460 };
461 self.remove(&evicted);
462 }
463 }
464}
465
466type LruObjectCache = LruCache<ObjectId>;
468pub(crate) type LruOffsetCache = LruCache<u64>;
470
471struct PackDeltaCacheAdapter<'a>(&'a Arc<Mutex<LruOffsetCache>>);
476
477impl sley_pack::PackDeltaCache for PackDeltaCacheAdapter<'_> {
478 fn get(&self, offset: u64) -> Option<Arc<EncodedObject>> {
479 self.0.lock().ok()?.get(&offset)
480 }
481
482 fn insert(&self, offset: u64, object: Arc<EncodedObject>) {
483 if let Ok(mut cache) = self.0.lock() {
484 cache.put(offset, object);
485 }
486 }
487}
488
489struct PackHeaderTypeCacheAdapter<'a>(&'a PackHeaderTypeCache);
493
494impl sley_pack::HeaderTypeCache for PackHeaderTypeCacheAdapter<'_> {
495 fn get(&self, pack_offset: u64) -> Option<(ObjectType, u64)> {
496 self.0.lock().ok()?.get(&pack_offset).copied()
497 }
498
499 fn put(&mut self, pack_offset: u64, header: (ObjectType, u64)) {
500 if let Ok(mut cache) = self.0.lock() {
501 cache.insert(pack_offset, header);
502 }
503 }
504}
505
506pub(crate) type PackIndexCache = Arc<RwLock<HashMap<PathBuf, Arc<PackIndexViewData>>>>;
511
512pub(crate) type PackReverseIndexCache = Arc<RwLock<HashMap<PathBuf, Option<Arc<PackReverseIndex>>>>>;
515
516pub(crate) type MultiPackIndexCache = Arc<RwLock<HashMap<PathBuf, Arc<MultiPackIndex>>>>;
520
521type MultiPackIndexOidLookupCache = Arc<RwLock<HashMap<PathBuf, Arc<MultiPackIndexOidLookup>>>>;
525
526#[derive(Debug, Clone)]
531pub struct FileObjectDatabase {
532 pub(crate) loose: LooseObjectStore,
533 pub(crate) objects_dir: PathBuf,
534 pub(crate) alternates: Vec<PathBuf>,
535 pub(crate) format: ObjectFormat,
536 pub(crate) pack_bytes: PackBytesCache,
537 pub(crate) pack_indexes: PackIndexCache,
538 pub(crate) pack_reverse_indexes: PackReverseIndexCache,
539 pub(crate) multi_pack_indexes: MultiPackIndexCache,
540 pub(crate) multi_pack_oid_lookups: MultiPackIndexOidLookupCache,
541 pub(crate) pack_registry: PackRegistryCache,
542 pub(crate) decoded: DecodedObjectCache,
543 pub(crate) pack_deltas: PackDeltaCaches,
544 pub(crate) pack_header_types: PackHeaderTypeCaches,
545 pub(crate) promisor_objects: Arc<OnceLock<HashSet<ObjectId>>>,
546 pub(crate) promisor_remote_present: bool,
554 pub(crate) shallow_grafts: Arc<std::sync::OnceLock<HashSet<ObjectId>>>,
558}
559
560fn read_shallow_grafts(shallow_file: &Path, format: ObjectFormat) -> HashSet<ObjectId> {
561 let Ok(contents) = std::fs::read_to_string(shallow_file) else {
562 return HashSet::new();
563 };
564 contents
565 .lines()
566 .filter_map(|line| ObjectId::from_hex(format, line.trim()).ok())
567 .collect()
568}
569
570impl FileObjectDatabase {
571 pub fn object_format(&self) -> ObjectFormat {
573 self.format
574 }
575
576 pub fn objects_dir(&self) -> &Path {
578 &self.objects_dir
579 }
580
581 pub fn new(objects_dir: impl Into<PathBuf>, format: ObjectFormat) -> Self {
582 let objects_dir = objects_dir.into();
583 Self {
584 loose: LooseObjectStore::new(objects_dir.clone(), format),
585 alternates: alternate_object_dirs(&objects_dir),
586 objects_dir,
587 format,
588 pack_bytes: Arc::new(RwLock::new(HashMap::new())),
589 pack_indexes: Arc::new(RwLock::new(HashMap::new())),
590 pack_reverse_indexes: Arc::new(RwLock::new(HashMap::new())),
591 multi_pack_indexes: Arc::new(RwLock::new(HashMap::new())),
592 multi_pack_oid_lookups: Arc::new(RwLock::new(HashMap::new())),
593 pack_registry: Arc::new(Mutex::new(None)),
594 decoded: Arc::new(Mutex::new(LruObjectCache::new(object_cache_budget()))),
595 pack_deltas: Arc::new(Mutex::new(HashMap::new())),
596 pack_header_types: Arc::new(Mutex::new(HashMap::new())),
597 promisor_objects: Arc::new(OnceLock::new()),
598 promisor_remote_present: false,
599 shallow_grafts: Arc::new(std::sync::OnceLock::new()),
600 }
601 }
602
603 pub(crate) fn without_alternates(objects_dir: impl Into<PathBuf>, format: ObjectFormat) -> Self {
604 let objects_dir = objects_dir.into();
605 Self {
606 loose: LooseObjectStore::new(objects_dir.clone(), format),
607 alternates: Vec::new(),
608 objects_dir,
609 format,
610 pack_bytes: Arc::new(RwLock::new(HashMap::new())),
611 pack_indexes: Arc::new(RwLock::new(HashMap::new())),
612 pack_reverse_indexes: Arc::new(RwLock::new(HashMap::new())),
613 multi_pack_indexes: Arc::new(RwLock::new(HashMap::new())),
614 multi_pack_oid_lookups: Arc::new(RwLock::new(HashMap::new())),
615 pack_registry: Arc::new(Mutex::new(None)),
616 decoded: Arc::new(Mutex::new(LruObjectCache::new(object_cache_budget()))),
617 pack_deltas: Arc::new(Mutex::new(HashMap::new())),
618 pack_header_types: Arc::new(Mutex::new(HashMap::new())),
619 promisor_objects: Arc::new(OnceLock::new()),
620 promisor_remote_present: false,
621 shallow_grafts: Arc::new(std::sync::OnceLock::new()),
622 }
623 }
624
625 pub fn from_git_dir(git_dir: impl AsRef<Path>, format: ObjectFormat) -> Self {
626 Self::new(repository_objects_dir(git_dir), format)
627 }
628
629 pub fn with_promisor_remote_present(mut self, present: bool) -> Self {
638 self.promisor_remote_present = present;
639 self
640 }
641
642 pub fn refresh_read_cache(&self) {
647 if let Ok(mut cache) = self.pack_registry.lock() {
648 *cache = None;
649 }
650 self.pack_indexes.write().clear();
651 self.multi_pack_indexes.write().clear();
652 self.multi_pack_oid_lookups.write().clear();
653 self.pack_bytes.write().clear();
654 if let Ok(mut cache) = self.pack_deltas.lock() {
655 cache.clear();
656 }
657 if let Ok(mut cache) = self.pack_header_types.lock() {
658 cache.clear();
659 }
660 if let Ok(mut cache) = self.decoded.lock() {
661 cache.clear();
662 }
663 self.loose.invalidate_cache();
664 }
665
666 pub fn loose(&self) -> &LooseObjectStore {
667 &self.loose
668 }
669
670 pub fn presence_checker(&self) -> ObjectPresenceChecker {
671 ObjectPresenceChecker::new(self.clone())
672 }
673
674 pub fn contains(&self, oid: &ObjectId) -> Result<bool> {
675 if self.loose.exists(oid)? {
676 return Ok(true);
677 }
678 if self.find_pack_containing(oid)?.is_some() {
679 return Ok(true);
680 }
681 for alternate in &self.alternates {
682 if Self::without_alternates(alternate, self.format).contains(oid)? {
683 return Ok(true);
684 }
685 }
686 self.loose.invalidate_cache();
689 self.loose.exists(oid)
690 }
691
692 pub fn object_ids(&self) -> Result<Vec<ObjectId>> {
693 let mut oids = object_ids_in_objects_dir(&self.objects_dir, self.format)?
694 .into_iter()
695 .collect::<HashSet<_>>();
696 for alternate in &self.alternates {
697 oids.extend(Self::without_alternates(alternate, self.format).object_ids()?);
698 }
699 let mut oids = oids.into_iter().collect::<Vec<_>>();
700 oids.sort_by_key(ObjectId::to_hex);
701 Ok(oids)
702 }
703
704 pub fn object_storage_info(&self, oid: &ObjectId) -> Result<Option<ObjectStorageInfo>> {
705 if let Some(disk_size) = self.loose.disk_size(oid)? {
706 return Ok(Some(ObjectStorageInfo {
707 disk_size,
708 deltabase: zero_oid(self.format)?,
709 }));
710 }
711 if let Some(info) = self.packed_object_storage_info(oid)? {
712 return Ok(Some(info));
713 }
714 for alternate in &self.alternates {
715 if let Some(info) =
716 Self::without_alternates(alternate, self.format).object_storage_info(oid)?
717 {
718 return Ok(Some(info));
719 }
720 }
721 self.loose.invalidate_cache();
724 if let Some(disk_size) = self.loose.disk_size(oid)? {
725 return Ok(Some(ObjectStorageInfo {
726 disk_size,
727 deltabase: zero_oid(self.format)?,
728 }));
729 }
730 Ok(None)
731 }
732
733 pub fn resolve_prefix(&self, prefix: &str) -> Result<ObjectPrefixResolution> {
734 let mut matches = self.object_ids_with_prefix(prefix)?;
735 Ok(match matches.len() {
736 0 => ObjectPrefixResolution::Missing,
737 1 => ObjectPrefixResolution::Unique(matches.remove(0)),
738 _ => ObjectPrefixResolution::Ambiguous(matches),
739 })
740 }
741
742 pub fn object_ids_with_prefix(&self, prefix: &str) -> Result<Vec<ObjectId>> {
743 validate_object_id_prefix(self.format, prefix)?;
744 let prefix_bytes = prefix.as_bytes();
745 let mut matches = object_ids_with_prefix_in_objects_dir(
746 &self.objects_dir,
747 self.format,
748 prefix_bytes,
749 )?
750 .into_iter()
751 .collect::<HashSet<_>>();
752 for alternate in &self.alternates {
753 for oid in Self::without_alternates(alternate, self.format)
754 .object_ids_with_prefix(prefix)?
755 {
756 matches.insert(oid);
757 }
758 }
759 let mut matches = matches.into_iter().collect::<Vec<_>>();
760 matches.sort_by_key(ObjectId::to_hex);
761 Ok(matches)
762 }
763
764 pub fn read_object_header(&self, oid: &ObjectId) -> Result<Option<(ObjectType, u64)>> {
774 if implied_empty_tree_object(self.format, oid).is_some() {
775 return Ok(Some((ObjectType::Tree, 0)));
776 }
777 if let Ok(mut cache) = self.decoded.lock()
778 && let Some(object) = cache.get(oid)
779 {
780 return Ok(Some((object.object_type, object.body.len() as u64)));
781 }
782 if let Some(header) = self.loose.read_header(oid)? {
783 return Ok(Some(header));
784 }
785 if let Some(pack_lookup) = self.find_pack_containing(oid)? {
786 let bytes = pack_lookup.pack_bytes(self)?;
787 let type_cache = pack_lookup.header_type_cache(self);
792 let resolve_ref_base = |base: &ObjectId| {
793 self.read_object_header(base)
794 .map(|header| header.map(|(t, _)| t))
795 };
796 let header = match &type_cache {
797 Some(cache) => {
798 let mut adapter = PackHeaderTypeCacheAdapter(cache);
799 sley_pack::read_object_header_at_with_cache(
800 &bytes,
801 pack_lookup.offset,
802 self.format,
803 resolve_ref_base,
804 &mut adapter,
805 )?
806 }
807 None => sley_pack::read_object_header_at(
808 &bytes,
809 pack_lookup.offset,
810 self.format,
811 resolve_ref_base,
812 )?,
813 };
814 return Ok(Some(header));
815 }
816 for alternate in &self.alternates {
817 if let Some(header) =
818 Self::without_alternates(alternate, self.format).read_object_header(oid)?
819 {
820 return Ok(Some(header));
821 }
822 }
823 self.loose.invalidate_cache();
826 if let Some(header) = self.loose.read_header(oid)? {
827 return Ok(Some(header));
828 }
829 Ok(None)
830 }
831
832 pub(crate) fn read_packed_object(&self, oid: &ObjectId) -> Result<Option<Arc<EncodedObject>>> {
833 if let Ok(mut cache) = self.decoded.lock()
836 && let Some(object) = cache.get(oid)
837 {
838 return Ok(Some(object));
839 }
840 let Some(pack_lookup) = self.find_pack_containing(oid)? else {
841 return Ok(None);
842 };
843 self.read_packed_object_at_lookup(oid, &pack_lookup)
844 .map(Some)
845 }
846
847 pub(crate) fn read_packed_object_at_lookup(
848 &self,
849 oid: &ObjectId,
850 pack_lookup: &PackLookup,
851 ) -> Result<Arc<EncodedObject>> {
852 if let Ok(mut cache) = self.decoded.lock()
853 && let Some(object) = cache.get(oid)
854 {
855 return Ok(object);
856 }
857 let bytes = pack_lookup.pack_bytes(self)?;
858 let delta_cache = pack_lookup.delta_cache(self);
863 let delta_adapter = delta_cache.as_ref().map(PackDeltaCacheAdapter);
864 let resolve_ref_base = |base: &ObjectId| self.read_object(base).map(Some);
870 let resolve_ofs_base =
871 |base_offset| self.read_ofs_delta_base_from_other_sources(pack_lookup, base_offset);
872 let object = match &delta_adapter {
873 Some(adapter) => sley_pack::read_object_at_with_cache_and_ofs_base_arc(
874 &bytes,
875 pack_lookup.offset,
876 self.format,
877 resolve_ref_base,
878 resolve_ofs_base,
879 adapter,
880 )?,
881 None => sley_pack::read_object_at_with_ofs_base_arc(
882 &bytes,
883 pack_lookup.offset,
884 self.format,
885 resolve_ref_base,
886 resolve_ofs_base,
887 )?,
888 };
889 if verify_reads_enabled() {
893 let actual = object.object_id(self.format)?;
894 if actual != *oid {
895 return Err(GitError::InvalidObject(format!(
896 "pack object id mismatch: index says {oid}, decoded {actual}"
897 )));
898 }
899 }
900 if let Ok(mut cache) = self.decoded.lock() {
901 cache.put(*oid, Arc::clone(&object));
902 }
903 Ok(object)
904 }
905
906 pub(crate) fn pack_delta_cache(&self, pack_path: &Path) -> Option<Arc<Mutex<LruOffsetCache>>> {
910 let mut caches = self.pack_deltas.lock().ok()?;
911 let cache = caches.entry(pack_path.to_path_buf()).or_insert_with(|| {
912 Arc::new(Mutex::new(LruOffsetCache::new(delta_base_cache_budget())))
913 });
914 Some(Arc::clone(cache))
915 }
916
917 pub(crate) fn pack_header_type_cache(&self, pack_path: &Path) -> Option<PackHeaderTypeCache> {
921 let mut caches = self.pack_header_types.lock().ok()?;
922 let cache = caches
923 .entry(pack_path.to_path_buf())
924 .or_insert_with(|| Arc::new(Mutex::new(HashMap::new())));
925 Some(Arc::clone(cache))
926 }
927
928 pub(crate) fn cached_pack_bytes(&self, pack_path: &Path) -> Result<Arc<PackData>> {
933 if let Some(bytes) = self.pack_bytes.read().get(pack_path)
934 {
935 return Ok(Arc::clone(bytes));
936 }
937 let bytes = Arc::new(load_pack_data(pack_path)?);
938 self.pack_bytes.write().insert(pack_path.to_path_buf(), Arc::clone(&bytes));
939 Ok(bytes)
940 }
941
942 pub(crate) fn cached_pack_index(&self, index_path: &Path) -> Result<Arc<PackIndexViewData>> {
946 if let Some(index) = self.pack_indexes.read().get(index_path)
947 {
948 return Ok(Arc::clone(index));
949 }
950 let index_bytes = load_pack_index_data(index_path)?;
951 let index = Arc::new(PackIndexViewData::parse_trusted_source_without_checksum(
952 index_bytes,
953 self.format,
954 )?);
955 self.pack_indexes.write().insert(index_path.to_path_buf(), Arc::clone(&index));
956 Ok(index)
957 }
958
959 pub(crate) fn cached_pack_reverse_index(
962 &self,
963 index_path: &Path,
964 index: &PackIndexViewData,
965 ) -> Result<Option<Arc<PackReverseIndex>>> {
966 if let Some(cached) = self.pack_reverse_indexes.read().get(index_path)
967 {
968 return Ok(cached.as_ref().map(Arc::clone));
969 }
970 let rev_path = index_path.with_extension("rev");
971 let reverse = if rev_path.exists() {
972 let bytes = fs::read(&rev_path)?;
973 match PackReverseIndex::parse(&bytes, self.format, index.count) {
974 Ok(parsed) if parsed.pack_checksum == index.pack_checksum => {
975 Some(Arc::new(parsed))
976 }
977 _ => None,
978 }
979 } else {
980 None
981 };
982 self.pack_reverse_indexes.write().insert(index_path.to_path_buf(), reverse.as_ref().map(Arc::clone));
983 Ok(reverse)
984 }
985
986 pub(crate) fn cached_multi_pack_index_oid_lookup(
987 &self,
988 midx_path: &Path,
989 ) -> Result<Option<Arc<MultiPackIndexOidLookup>>> {
990 if !midx_path.exists() {
991 return Ok(None);
992 }
993 if let Some(midx) = self.multi_pack_oid_lookups.read().get(midx_path) {
994 return Ok(Some(Arc::clone(midx)));
995 }
996 let bytes = load_multi_pack_index_lookup_data(midx_path)?;
997 let midx = match MultiPackIndexOidLookup::parse(bytes, self.format) {
998 Ok(midx) => Arc::new(midx),
999 Err(GitError::InvalidFormat(message))
1000 if message.starts_with("multi-pack-index hash id ") =>
1001 {
1002 let actual = message
1003 .strip_prefix("multi-pack-index hash id ")
1004 .and_then(|rest| rest.split_whitespace().next())
1005 .unwrap_or("0");
1006 let expected = match self.format {
1007 ObjectFormat::Sha1 => 1,
1008 ObjectFormat::Sha256 => 2,
1009 };
1010 eprintln!(
1011 "error: multi-pack-index hash version {actual} does not match version {expected}"
1012 );
1013 return Ok(None);
1014 }
1015 Err(err) => return Err(err),
1016 };
1017 self.multi_pack_oid_lookups.write().insert(midx_path.to_path_buf(), Arc::clone(&midx));
1018 Ok(Some(midx))
1019 }
1020
1021 pub(crate) fn cached_multi_pack_index(&self, midx_path: &Path) -> Result<Option<Arc<MultiPackIndex>>> {
1022 if !midx_path.exists() {
1023 return Ok(None);
1024 }
1025 if let Some(midx) = self.multi_pack_indexes.read().get(midx_path)
1026 {
1027 return Ok(Some(Arc::clone(midx)));
1028 }
1029 let bytes = load_multi_pack_index_lookup_data(midx_path)?;
1030 let midx = match MultiPackIndex::parse(bytes.as_bytes(), self.format) {
1031 Ok(midx) => Arc::new(midx),
1032 Err(GitError::InvalidFormat(message))
1033 if message.starts_with("multi-pack-index hash id ") =>
1034 {
1035 let actual = message
1036 .strip_prefix("multi-pack-index hash id ")
1037 .and_then(|rest| rest.split_whitespace().next())
1038 .unwrap_or("0");
1039 let expected = match self.format {
1040 ObjectFormat::Sha1 => 1,
1041 ObjectFormat::Sha256 => 2,
1042 };
1043 eprintln!(
1044 "error: multi-pack-index hash version {actual} does not match version {expected}"
1045 );
1046 return Ok(None);
1047 }
1048 Err(err) => return Err(err),
1049 };
1050 self.multi_pack_indexes.write().insert(midx_path.to_path_buf(), Arc::clone(&midx));
1051 Ok(Some(midx))
1052 }
1053
1054 pub(crate) fn cached_pack_registry(
1059 &self,
1060 pack_dir: &Path,
1061 force_rescan: bool,
1062 ) -> Result<Arc<PackRegistrySnapshot>> {
1063 if !force_rescan && let Some(registry) = self.cached_loaded_pack_registry(pack_dir)? {
1064 return Ok(registry);
1065 }
1066 let scanned = Arc::new(scan_pack_registry(pack_dir, self.format)?);
1067 if let Ok(mut cache) = self.pack_registry.lock() {
1068 match cache.as_ref() {
1069 Some(existing)
1070 if existing.fingerprint == scanned.fingerprint
1071 && same_registered_pack_set(&existing.packs, &scanned.packs) =>
1072 {
1073 return Ok(Arc::clone(existing));
1074 }
1075 _ => {
1076 *cache = Some(Arc::clone(&scanned));
1077 }
1078 }
1079 }
1080 Ok(scanned)
1081 }
1082
1083 pub(crate) fn find_in_pack_registry(
1084 &self,
1085 registry: Arc<PackRegistrySnapshot>,
1086 oid: &ObjectId,
1087 ) -> Result<Option<PackLookup>> {
1088 let hinted_pack_index = registry.cached_hint();
1089 if let Some(pack_index) = hinted_pack_index {
1090 let pack = ®istry.packs[pack_index];
1091 match pack.index(self.format) {
1092 Ok(index) => {
1093 if let Some(entry) = index.find(oid) {
1094 return Ok(Some(PackLookup::from_registered(
1095 Arc::clone(pack),
1096 entry.offset,
1097 )));
1098 }
1099 }
1100 Err(_) => {
1101 eprintln!("error: packfile {} index unavailable", pack.pack.display());
1102 }
1103 }
1104 }
1105 for (pack_index, pack) in registry.packs.iter().enumerate() {
1106 if Some(pack_index) == hinted_pack_index {
1107 continue;
1108 }
1109 let index = match pack.index(self.format) {
1110 Ok(index) => index,
1111 Err(_) => {
1112 eprintln!("error: packfile {} index unavailable", pack.pack.display());
1113 continue;
1114 }
1115 };
1116 if let Some(entry) = index.find(oid) {
1117 registry.remember_hint(pack_index);
1118 return Ok(Some(PackLookup::from_registered(
1119 Arc::clone(pack),
1120 entry.offset,
1121 )));
1122 }
1123 }
1124 Ok(None)
1125 }
1126
1127 pub(crate) fn read_packed_object_from_other_packs(
1133 &self,
1134 oid: &ObjectId,
1135 exclude: &PackLookup,
1136 ) -> Result<Option<Arc<EncodedObject>>> {
1137 let pack_dir = self.objects_dir.join("pack");
1138 let Ok(entries) = fs::read_dir(&pack_dir) else {
1139 return Ok(None);
1140 };
1141 let excluded_pack = exclude.pack_path().to_path_buf();
1142 for entry in entries {
1143 let idx_path = entry?.path();
1144 if idx_path.extension().and_then(|ext| ext.to_str()) != Some("idx") {
1145 continue;
1146 }
1147 let pack_path = idx_path.with_extension("pack");
1148 if pack_path == excluded_pack {
1149 continue;
1150 }
1151 let Ok(idx_bytes) = fs::read(&idx_path) else {
1152 continue;
1153 };
1154 let Ok(index) = PackIndex::parse(&idx_bytes, self.format) else {
1155 continue;
1156 };
1157 let Some(entry) = index.find(oid) else {
1158 continue;
1159 };
1160 let candidate = PackLookup::from_path(pack_path, entry.offset);
1161 if let Ok(object) = self.read_packed_object_at_lookup(oid, &candidate) {
1162 return Ok(Some(object));
1163 }
1164 }
1165 Ok(None)
1166 }
1167
1168 pub(crate) fn pack_oid_at_offset(
1169 &self,
1170 pack_lookup: &PackLookup,
1171 offset: u64,
1172 ) -> Result<Option<ObjectId>> {
1173 let index_path = match &pack_lookup.registered {
1174 Some(pack) => pack.idx.clone(),
1175 None => pack_lookup.pack.with_extension("idx"),
1176 };
1177 match pack_lookup.pack_index(self) {
1178 Ok(index) => {
1179 if let Some(reverse) = self.cached_pack_reverse_index(&index_path, &index)? {
1180 Ok(reverse.oid_at_offset(&index, offset))
1181 } else {
1182 Ok(index.oid_at_offset_linear(offset))
1183 }
1184 }
1185 Err(_) => self.midx_oid_for_pack_offset(pack_lookup, offset),
1186 }
1187 }
1188
1189 pub(crate) fn read_ofs_delta_base_from_other_sources(
1190 &self,
1191 pack_lookup: &PackLookup,
1192 base_offset: u64,
1193 ) -> Result<Option<Arc<EncodedObject>>> {
1194 let Some(base_oid) = self.pack_oid_at_offset(pack_lookup, base_offset)? else {
1195 return Ok(None);
1196 };
1197 if let Ok(mut cache) = self.decoded.lock()
1198 && let Some(object) = cache.get(&base_oid)
1199 {
1200 return Ok(Some(object));
1201 }
1202 if let Ok(object) = self.loose.read_object(&base_oid) {
1203 return Ok(Some(object));
1204 }
1205 if let Some(object) = self.read_packed_object_from_other_packs(&base_oid, pack_lookup)? {
1206 return Ok(Some(object));
1207 }
1208 for alternate in &self.alternates {
1209 if let Ok(object) =
1210 Self::without_alternates(alternate, self.format).read_object(&base_oid)
1211 {
1212 return Ok(Some(object));
1213 }
1214 }
1215 Ok(None)
1216 }
1217
1218 pub(crate) fn find_pack_containing(&self, oid: &ObjectId) -> Result<Option<PackLookup>> {
1219 if oid.format() != self.format {
1220 return Err(GitError::InvalidObjectId(format!(
1221 "object {oid} uses {}, store uses {}",
1222 oid.format().name(),
1223 self.format.name()
1224 )));
1225 }
1226 let pack_dir = self.objects_dir.join("pack");
1227 if let Some(midx) = self.cached_loaded_multi_pack_index_oid_lookup()
1232 && let Some(pack_paths) = self.midx_oid_lookup_pack_paths(&pack_dir, &midx, oid)?
1233 {
1234 return Ok(Some(pack_paths));
1235 }
1236 if let Some(registry) = self.cached_loaded_pack_registry(&pack_dir)?
1237 && let Some(pack_paths) = self.find_in_pack_registry(registry, oid)?
1238 {
1239 return Ok(Some(pack_paths));
1240 }
1241
1242 if !pack_dir.exists() {
1243 return Ok(None);
1244 }
1245 if let Some(pack_paths) = self.find_midx_pack_containing(&pack_dir, oid)? {
1246 return Ok(Some(pack_paths));
1247 }
1248 let registry = self.cached_pack_registry(&pack_dir, false)?;
1252 if let Some(pack_paths) = self.find_in_pack_registry(Arc::clone(®istry), oid)? {
1253 return Ok(Some(pack_paths));
1254 }
1255 let refreshed = self.cached_pack_registry(&pack_dir, true)?;
1256 if Arc::ptr_eq(®istry, &refreshed) {
1257 return Ok(None);
1259 }
1260 self.find_in_pack_registry(refreshed, oid)
1261 }
1262
1263 pub(crate) fn packed_object_storage_info(&self, oid: &ObjectId) -> Result<Option<ObjectStorageInfo>> {
1264 let Some(pack_lookup) = self.find_pack_containing(oid)? else {
1265 return Ok(None);
1266 };
1267 let index = pack_lookup.pack_index(self).ok();
1268 let pack = match pack_lookup.pack_bytes(self) {
1269 Ok(pack) => Some(pack),
1270 Err(_err) if index.is_some() => None,
1271 Err(err) => return Err(err),
1272 };
1273 let trailer_offset = pack
1274 .as_ref()
1275 .map(|pack| {
1276 (pack.len() as u64)
1277 .checked_sub(self.format.raw_len() as u64)
1278 .ok_or_else(|| {
1279 GitError::InvalidFormat("pack file shorter than checksum".into())
1280 })
1281 })
1282 .transpose()?;
1283 let delta_base = match &pack {
1284 Some(pack) => pack_entry_delta_base(self.format, pack, pack_lookup.offset)?,
1285 None => None,
1286 };
1287 let delta_base_offset = match &delta_base {
1288 Some(PackDeltaBase::Offset(offset)) => Some(*offset),
1289 Some(PackDeltaBase::Ref(_)) | None => None,
1290 };
1291 let offset_info = if let Some(index) = &index {
1292 scan_pack_index_offsets(index, pack_lookup.offset, trailer_offset, delta_base_offset)?
1293 } else if let Some(pack) = &pack {
1294 let end_offset =
1295 scan_pack_offsets_without_index(self.format, pack, pack_lookup.offset)?
1296 .ok_or_else(|| {
1297 GitError::InvalidFormat(format!(
1298 "pack offset {} not found",
1299 pack_lookup.offset
1300 ))
1301 })?;
1302 let delta_base_oid = match delta_base_offset {
1303 Some(offset) => self
1304 .midx_oid_for_pack_offset(&pack_lookup, offset)?
1305 .ok_or_else(|| {
1306 GitError::InvalidFormat(format!("ofs-delta base offset {offset} not found"))
1307 })?,
1308 None => zero_oid(self.format)?,
1309 };
1310 PackIndexOffsetInfo {
1311 end_offset,
1312 delta_base_oid: delta_base_offset.map(|_| delta_base_oid),
1313 }
1314 } else {
1315 return Err(GitError::InvalidFormat(
1316 "packed object metadata source unavailable".into(),
1317 ));
1318 };
1319 let disk_size = offset_info
1320 .end_offset
1321 .checked_sub(pack_lookup.offset)
1322 .ok_or_else(|| GitError::InvalidFormat("pack index offsets are not sorted".into()))?;
1323 let deltabase = match delta_base {
1324 Some(PackDeltaBase::Offset(_)) => offset_info.delta_base_oid.ok_or_else(|| {
1325 GitError::InvalidFormat("ofs-delta base oid missing from pack index".into())
1331 })?,
1332 Some(PackDeltaBase::Ref(oid)) => oid,
1333 None => zero_oid(self.format)?,
1334 };
1335 Ok(Some(ObjectStorageInfo {
1336 disk_size,
1337 deltabase,
1338 }))
1339 }
1340
1341 pub(crate) fn midx_oid_for_pack_offset(
1342 &self,
1343 pack_lookup: &PackLookup,
1344 offset: u64,
1345 ) -> Result<Option<ObjectId>> {
1346 let pack_dir = self.objects_dir.join("pack");
1347 let midx_path = pack_dir.join("multi-pack-index");
1348 let Some(midx) = self.cached_multi_pack_index(&midx_path)? else {
1349 return Ok(None);
1350 };
1351 let Some(pack_name) = pack_lookup
1352 .pack_path()
1353 .file_name()
1354 .and_then(|name| name.to_str())
1355 else {
1356 return Ok(None);
1357 };
1358 let idx_name = pack_name
1359 .strip_suffix(".pack")
1360 .map(|stem| format!("{stem}.idx"))
1361 .unwrap_or_else(|| pack_name.to_string());
1362 let Some(pack_int_id) = midx
1363 .pack_names
1364 .iter()
1365 .position(|candidate| candidate == &idx_name)
1366 else {
1367 return Ok(None);
1368 };
1369 Ok(midx
1370 .objects
1371 .iter()
1372 .find(|entry| entry.pack_int_id == pack_int_id as u32 && entry.offset == offset)
1373 .map(|entry| entry.oid))
1374 }
1375
1376 pub(crate) fn find_midx_pack_containing(
1377 &self,
1378 pack_dir: &Path,
1379 oid: &ObjectId,
1380 ) -> Result<Option<PackLookup>> {
1381 let midx_path = pack_dir.join("multi-pack-index");
1382 if let Some(midx) = self.cached_multi_pack_index_oid_lookup(&midx_path)?
1383 && let Some(pack_lookup) = self.midx_oid_lookup_pack_paths(pack_dir, &midx, oid)?
1384 {
1385 return Ok(Some(pack_lookup));
1386 }
1387 self.find_incremental_midx_pack_containing(pack_dir, oid)
1388 }
1389
1390 pub(crate) fn midx_oid_lookup_pack_paths(
1391 &self,
1392 pack_dir: &Path,
1393 midx: &MultiPackIndexOidLookup,
1394 oid: &ObjectId,
1395 ) -> Result<Option<PackLookup>> {
1396 let Some(entry) = midx.find(oid)? else {
1397 return Ok(None);
1398 };
1399 let Some(pack_name) = midx.pack_name(entry.pack_int_id) else {
1400 return Err(GitError::InvalidFormat(
1401 "multi-pack-index object points past pack table".into(),
1402 ));
1403 };
1404 let pack_file_name = pack_name
1405 .strip_suffix(".idx")
1406 .map(|stem| format!("{stem}.pack"))
1407 .unwrap_or_else(|| pack_name.to_string());
1408 let pack = pack_dir.join(pack_file_name);
1409 Ok(Some(PackLookup::from_path(pack, entry.offset)))
1410 }
1411
1412 pub(crate) fn find_incremental_midx_pack_containing(
1413 &self,
1414 pack_dir: &Path,
1415 oid: &ObjectId,
1416 ) -> Result<Option<PackLookup>> {
1417 let chain = read_incremental_midx_chain(pack_dir)?;
1418 if chain.is_empty() {
1419 return Ok(None);
1420 }
1421 let midx_dir = pack_dir.join("multi-pack-index.d");
1422 for checksum in chain.iter().rev() {
1423 let path = midx_dir.join(format!("multi-pack-index-{checksum}.midx"));
1424 if !path.exists() {
1425 continue;
1426 }
1427 let bytes = load_multi_pack_index_lookup_data(&path)?;
1428 let midx = match MultiPackIndexOidLookup::parse(bytes, self.format) {
1429 Ok(midx) => midx,
1430 Err(_) => continue,
1431 };
1432 if let Some(pack_lookup) = self.midx_oid_lookup_pack_paths(pack_dir, &midx, oid)? {
1433 return Ok(Some(pack_lookup));
1434 }
1435 }
1436 Ok(None)
1437 }
1438
1439 pub(crate) fn cached_loaded_multi_pack_index_oid_lookup(&self) -> Option<Arc<MultiPackIndexOidLookup>> {
1440 let midx_path = self.objects_dir.join("pack").join("multi-pack-index");
1441 self.multi_pack_oid_lookups.read().get(&midx_path).map(Arc::clone)
1442 }
1443
1444 pub(crate) fn cached_loaded_pack_registry(
1450 &self,
1451 _pack_dir: &Path,
1452 ) -> Result<Option<Arc<PackRegistrySnapshot>>> {
1453 let cache = match self.pack_registry.lock() {
1454 Ok(cache) => cache,
1455 Err(_) => return Ok(None),
1456 };
1457 Ok(cache.as_ref().map(Arc::clone))
1458 }
1459}
1460impl ObjectReader for FileObjectDatabase {
1461 fn is_promised_object(&self, oid: &ObjectId) -> bool {
1462 self.promisor_remote_present && self.promisor_objects().contains(oid)
1468 }
1469
1470 fn has_shallow_grafts(&self) -> bool {
1471 !self
1472 .shallow_grafts
1473 .get_or_init(|| {
1474 let shallow_file = self
1475 .objects_dir
1476 .parent()
1477 .map(|git_dir| git_dir.join("shallow"));
1478 match shallow_file {
1479 Some(path) => read_shallow_grafts(&path, self.format),
1480 None => HashSet::new(),
1481 }
1482 })
1483 .is_empty()
1484 }
1485
1486 fn is_shallow_graft(&self, oid: &ObjectId) -> bool {
1487 self.shallow_grafts
1488 .get_or_init(|| {
1489 let shallow_file = self
1490 .objects_dir
1491 .parent()
1492 .map(|git_dir| git_dir.join("shallow"));
1493 match shallow_file {
1494 Some(path) => read_shallow_grafts(&path, self.format),
1495 None => HashSet::new(),
1496 }
1497 })
1498 .contains(oid)
1499 }
1500
1501 fn read_object(&self, oid: &ObjectId) -> Result<Arc<EncodedObject>> {
1502 if let Some(object) = implied_empty_tree_object(self.format, oid) {
1503 return Ok(object);
1504 }
1505 if let Some(pack_lookup) = self.find_pack_containing(oid)? {
1513 match self.read_packed_object_at_lookup(oid, &pack_lookup) {
1514 Ok(object) => return Ok(object),
1515 Err(GitError::NotFound(_)) => {}
1516 Err(packed_err) => {
1522 if let Ok(object) = self.loose.read_object(oid) {
1523 return Ok(object);
1524 }
1525 if let Some(object) =
1528 self.read_packed_object_from_other_packs(oid, &pack_lookup)?
1529 {
1530 return Ok(object);
1531 }
1532 for alternate in &self.alternates {
1533 if let Ok(object) =
1534 Self::without_alternates(alternate, self.format).read_object(oid)
1535 {
1536 return Ok(object);
1537 }
1538 }
1539 return Err(packed_err);
1540 }
1541 }
1542 }
1543 let loose_err = match self.loose.read_object(oid) {
1544 Ok(object) => return Ok(object),
1545 Err(GitError::NotFound(_)) => None,
1546 Err(err) => Some(err),
1547 };
1548 if let Some(object) = self.read_packed_object(oid)? {
1549 return Ok(object);
1550 }
1551 for alternate in &self.alternates {
1552 match Self::without_alternates(alternate, self.format).read_object(oid) {
1553 Ok(object) => return Ok(object),
1554 Err(GitError::NotFound(_)) => {}
1555 Err(err) => return Err(err),
1556 }
1557 }
1558 self.loose.invalidate_cache();
1564 match self.loose.read_object(oid) {
1565 Ok(object) => return Ok(object),
1566 Err(GitError::NotFound(_)) => {}
1567 Err(err) => return Err(err),
1568 }
1569 if let Some(err) = loose_err {
1573 return Err(err);
1574 }
1575 Err(GitError::object_not_found_in(
1576 *oid,
1577 MissingObjectContext::Read,
1578 ))
1579 }
1580}
1581impl FileObjectDatabase {
1582 fn promisor_objects(&self) -> &HashSet<ObjectId> {
1583 self.promisor_objects.get_or_init(|| {
1584 let mut promised =
1585 promisor_pack_object_ids(&self.objects_dir, self.format).unwrap_or_default();
1586 let mut pending = promised.iter().copied().collect::<Vec<_>>();
1587 while let Some(oid) = pending.pop() {
1588 let Ok(object) = self.read_object(&oid) else {
1589 continue;
1590 };
1591 for link in promisor_object_links(self.format, &object) {
1592 if promised.insert(link) {
1593 pending.push(link);
1594 }
1595 }
1596 }
1597 promised
1598 })
1599 }
1600
1601 fn freshen_existing_object(&self, oid: &ObjectId) -> Result<bool> {
1602 if self.freshen_loose_object(oid)? {
1603 return Ok(true);
1604 }
1605 if self.freshen_packed_object(oid)? {
1606 return Ok(true);
1607 }
1608 for alternate in &self.alternates {
1609 if Self::without_alternates(alternate, self.format).freshen_existing_object(oid)? {
1610 return Ok(true);
1611 }
1612 }
1613 self.loose.invalidate_cache();
1615 self.freshen_loose_object(oid)
1616 }
1617
1618 fn freshen_loose_object(&self, oid: &ObjectId) -> Result<bool> {
1619 let path = self.loose.object_path(oid)?;
1620 freshen_file_mtime(&path)
1621 }
1622
1623 fn freshen_packed_object(&self, oid: &ObjectId) -> Result<bool> {
1624 let Some(pack_lookup) = self.find_pack_containing(oid)? else {
1625 return Ok(false);
1626 };
1627 freshen_file_mtime(pack_lookup.pack_path())
1628 }
1629}
1630pub(crate) fn freshen_file_mtime(path: &Path) -> Result<bool> {
1631 let file = match fs::OpenOptions::new().read(true).open(path) {
1632 Ok(file) => file,
1633 Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(false),
1634 Err(err) => return Err(GitError::Io(err.to_string())),
1635 };
1636 file.set_modified(std::time::SystemTime::now())
1637 .map_err(|err| GitError::Io(err.to_string()))?;
1638 Ok(true)
1639}
1640
1641pub(crate) fn promisor_pack_object_ids(objects_dir: &Path, format: ObjectFormat) -> Result<HashSet<ObjectId>> {
1642 let pack_dir = objects_dir.join("pack");
1643 let mut oids = HashSet::new();
1644 if !pack_dir.exists() {
1645 return Ok(oids);
1646 }
1647 for entry in fs::read_dir(pack_dir)? {
1648 let path = entry?.path();
1649 if path.extension().and_then(|ext| ext.to_str()) != Some("idx") {
1650 continue;
1651 }
1652 if !path.with_extension("pack").exists() || !path.with_extension("promisor").exists() {
1653 continue;
1654 }
1655 let index = PackIndex::parse(&fs::read(path)?, format)?;
1656 oids.extend(index.entries.into_iter().map(|entry| entry.oid));
1657 }
1658 Ok(oids)
1659}
1660
1661pub(crate) fn promisor_object_links(format: ObjectFormat, object: &EncodedObject) -> Vec<ObjectId> {
1662 match object.object_type {
1663 ObjectType::Commit => Commit::parse_ref(format, &object.body)
1664 .map(|commit| {
1665 let mut links = Vec::with_capacity(commit.parents.len() + 1);
1666 links.push(commit.tree);
1667 links.extend(commit.parents);
1668 links
1669 })
1670 .unwrap_or_default(),
1671 ObjectType::Tree => TreeEntries::new(format, &object.body)
1672 .filter_map(|entry| entry.ok().map(|entry| entry.oid))
1673 .collect(),
1674 ObjectType::Tag => Tag::parse_ref(format, &object.body)
1675 .map(|tag| vec![tag.object])
1676 .unwrap_or_default(),
1677 ObjectType::Blob => Vec::new(),
1678 }
1679}
1680impl ObjectWriter for FileObjectDatabase {
1681 fn write_object(&self, object: EncodedObject) -> Result<ObjectId> {
1682 let oid = object.object_id(self.format)?;
1687 if self.freshen_existing_object(&oid)? {
1688 return Ok(oid);
1689 }
1690 self.loose.write_object(object)
1691 }
1692}