1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
use std::{path::PathBuf, sync::atomic::AtomicBool};

use git_repository as git;
use git_repository::Progress;

use crate::{pack, OutputFormat};

/// A general purpose context for many operations provided here
pub struct Context {
    /// If set, provide statistics to `out` in the given format
    pub output_statistics: Option<OutputFormat>,
    /// If set, don't use more than this amount of threads.
    /// Otherwise, usually use as many threads as there are logical cores.
    /// A value of 0 is interpreted as no-limit
    pub thread_limit: Option<usize>,
    pub verify_mode: pack::verify::Mode,
    pub algorithm: pack::verify::Algorithm,
}

pub const PROGRESS_RANGE: std::ops::RangeInclusive<u8> = 1..=3;

pub fn integrity(
    repo: PathBuf,
    mut out: impl std::io::Write,
    progress: impl Progress,
    should_interrupt: &AtomicBool,
    Context {
        output_statistics,
        thread_limit,
        verify_mode,
        algorithm,
    }: Context,
) -> anyhow::Result<()> {
    let repo = git_repository::open(repo)?;
    #[cfg_attr(not(feature = "serde1"), allow(unused))]
    let mut outcome = repo.objects.store_ref().verify_integrity(
        progress,
        should_interrupt,
        git_repository::odb::pack::index::verify::integrity::Options {
            verify_mode,
            traversal: algorithm.into(),
            thread_limit,
            // TODO: a way to get the pack cache from a handle
            make_pack_lookup_cache: || git_repository::odb::pack::cache::Never,
        },
    )?;
    // TODO: make this work for indices in multiple workspaces, once we have workspace support
    if let Some(index) = repo.load_index().transpose()? {
        index.verify_integrity()?;
        index.verify_entries()?;
        index.verify_extensions(true, {
            use git::odb::FindExt;
            let objects = repo.objects;
            move |oid, buf: &mut Vec<u8>| objects.find_tree_iter(oid, buf).ok()
        })?;
        outcome.progress.info(format!("Index at '{}' OK", index.path.display()));
    }
    match output_statistics {
        Some(OutputFormat::Human) => writeln!(out, "Human output is currently unsupported, use JSON instead")?,
        #[cfg(feature = "serde1")]
        Some(OutputFormat::Json) => {
            serde_json::to_writer_pretty(
                out,
                &serde_json::json!({
                    "index_statistics" : outcome.index_statistics,
                    "loose_object-stores" : outcome.loose_object_stores
                }),
            )?;
        }
        None => {}
    }
    Ok(())
}