git_perf/
size.rs

1use crate::git::git_interop::is_shallow_repository;
2use crate::git::size_ops::{get_notes_size, get_repo_stats, NotesSizeInfo, RepoStats};
3use anyhow::Result;
4use git_perf_cli_types::SizeFormat;
5use human_repr::HumanCount;
6
7/// Calculate and display measurement storage size
8pub fn calculate_measurement_size(
9    detailed: bool,
10    format: SizeFormat,
11    disk_size: bool,
12    include_objects: bool,
13) -> Result<()> {
14    // 0. Check for shallow repository
15    let is_shallow = is_shallow_repository().unwrap_or(false);
16
17    // 1. Get notes size information
18    let notes_info = get_notes_size(detailed, disk_size)?;
19
20    // 2. Optionally get repository statistics
21    let repo_stats = if include_objects {
22        Some(get_repo_stats()?)
23    } else {
24        None
25    };
26
27    // 3. Display results
28    display_size_report(
29        &notes_info,
30        repo_stats.as_ref(),
31        format,
32        disk_size,
33        is_shallow,
34    )?;
35
36    Ok(())
37}
38
39/// Display size report to stdout
40fn display_size_report(
41    info: &NotesSizeInfo,
42    repo_stats: Option<&RepoStats>,
43    format: SizeFormat,
44    disk_size: bool,
45    is_shallow: bool,
46) -> Result<()> {
47    let size_type = if disk_size {
48        "on-disk (compressed)"
49    } else {
50        "logical (uncompressed)"
51    };
52
53    println!("Live Measurement Size Report");
54    println!("============================");
55    println!();
56
57    // Display prominent warning for shallow clones
58    if is_shallow {
59        println!("⚠️  Shallow clone detected - measurement counts may be incomplete (see FAQ)");
60        println!();
61    }
62
63    println!("Number of commits with measurements: {}", info.note_count);
64    println!(
65        "Total measurement data size ({}): {}",
66        size_type,
67        format_size(info.total_bytes, format)
68    );
69
70    // Show repository context if requested
71    if let Some(stats) = repo_stats {
72        println!();
73        println!("Repository Statistics (for context):");
74        println!("-------------------------------------");
75        println!(
76            "  Loose objects: {} ({})",
77            stats.loose_objects,
78            format_size(stats.loose_size, format)
79        );
80        println!(
81            "  Packed objects: {} ({})",
82            stats.packed_objects,
83            format_size(stats.pack_size, format)
84        );
85        println!(
86            "  Total repository size: {}",
87            format_size(stats.loose_size + stats.pack_size, format)
88        );
89    }
90
91    // Show detailed breakdown if requested
92    if let Some(by_name) = &info.by_measurement {
93        println!();
94        println!("Breakdown by Measurement Name ({}):", size_type);
95        println!("------------------------------");
96
97        // Sort by size descending
98        let mut sorted: Vec<_> = by_name.iter().collect();
99        sorted.sort_by(|a, b| b.1.total_bytes.cmp(&a.1.total_bytes));
100
101        for (name, size_info) in sorted {
102            println!(
103                "  {} ({} occurrences): {}",
104                name,
105                size_info.count,
106                format_size(size_info.total_bytes, format)
107            );
108        }
109    }
110
111    Ok(())
112}
113
114/// Format size according to requested format
115fn format_size(bytes: u64, format: SizeFormat) -> String {
116    match format {
117        SizeFormat::Bytes => bytes.to_string(),
118        SizeFormat::Human => bytes.human_count_bytes().to_string(),
119    }
120}