git_odb/store_impls/loose/
verify.rs1use std::{
2 sync::atomic::{AtomicBool, Ordering},
3 time::Instant,
4};
5
6use git_features::progress::Progress;
7
8use crate::{loose::Store, Write};
9
10pub mod integrity {
12 #[derive(Debug, thiserror::Error)]
14 #[allow(missing_docs)]
15 pub enum Error {
16 #[error("{kind} object {id} could not be decoded")]
17 ObjectDecode {
18 source: git_object::decode::Error,
19 kind: git_object::Kind,
20 id: git_hash::ObjectId,
21 },
22 #[error("{kind} object {expected} wasn't re-encoded without change - new hash is {actual}")]
23 ObjectHashMismatch {
24 kind: git_object::Kind,
25 actual: git_hash::ObjectId,
26 expected: git_hash::ObjectId,
27 },
28 #[error("Objects were deleted during iteration - try again")]
29 Retry,
30 #[error("Interrupted")]
31 Interrupted,
32 }
33
34 #[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Clone)]
36 #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
37 pub struct Statistics {
38 pub num_objects: usize,
40 }
41
42 #[derive(Debug, Copy, Clone)]
46 pub enum ProgressId {
47 LooseObjects,
49 }
50
51 impl From<ProgressId> for git_features::progress::Id {
52 fn from(v: ProgressId) -> Self {
53 match v {
54 ProgressId::LooseObjects => *b"VILO",
55 }
56 }
57 }
58}
59
60impl Store {
61 pub fn verify_integrity(
63 &self,
64 mut progress: impl Progress,
65 should_interrupt: &AtomicBool,
66 ) -> Result<integrity::Statistics, integrity::Error> {
67 let mut buf = Vec::new();
68 let sink = crate::sink(self.object_hash);
69
70 let mut num_objects = 0;
71 let start = Instant::now();
72 let mut progress = progress.add_child_with_id("Validating", integrity::ProgressId::LooseObjects.into());
73 progress.init(None, git_features::progress::count("loose objects"));
74 for id in self.iter().filter_map(Result::ok) {
75 let object = self
76 .try_find(id, &mut buf)
77 .map_err(|_| integrity::Error::Retry)?
78 .ok_or(integrity::Error::Retry)?;
79 let actual_id = sink.write_buf(object.kind, object.data).expect("sink never fails");
80 if actual_id != id {
81 return Err(integrity::Error::ObjectHashMismatch {
82 kind: object.kind,
83 actual: actual_id,
84 expected: id,
85 });
86 }
87 object.decode().map_err(|err| integrity::Error::ObjectDecode {
88 source: err,
89 kind: object.kind,
90 id,
91 })?;
92
93 progress.inc();
94 num_objects += 1;
95 if should_interrupt.load(Ordering::SeqCst) {
96 return Err(integrity::Error::Interrupted);
97 }
98 }
99 progress.show_throughput(start);
100
101 Ok(integrity::Statistics { num_objects })
102 }
103}