use crate::git::GitError;
use crate::util::log_cmd;
use std::path::Path;
use std::process::Command;
pub struct GcResult {
pub size_before: u64,
pub size_after: u64,
pub success: bool,
}
pub fn dir_size(path: &Path) -> u64 {
if !path.exists() {
return 0;
}
let mut total = 0u64;
if let Ok(entries) = std::fs::read_dir(path) {
for entry in entries.flatten() {
let meta = match entry.metadata() {
Ok(m) => m,
Err(_) => continue,
};
if meta.is_dir() {
total += dir_size(&entry.path());
} else {
total += meta.len();
}
}
}
total
}
pub fn git_dir_size(repo_path: &Path) -> u64 {
dir_size(&repo_path.join(".git"))
}
pub fn run_git_gc(repo_path: &Path, aggressive: bool) -> Result<GcResult, GitError> {
let size_before = git_dir_size(repo_path);
let mut args = vec!["gc"];
if aggressive {
args.push("--aggressive");
}
let mut cmd = Command::new("git");
cmd.args(&args).current_dir(repo_path);
log_cmd(&cmd);
let output = cmd
.output()
.map_err(|e| GitError::OperationFailed(format!("failed to run git gc: {}", e)))?;
let success = output.status.success();
let size_after = git_dir_size(repo_path);
Ok(GcResult {
size_before,
size_after,
success,
})
}
pub fn format_bytes(bytes: u64) -> String {
const KB: u64 = 1024;
const MB: u64 = 1024 * KB;
const GB: u64 = 1024 * MB;
if bytes >= GB {
format!("{:.1} GB", bytes as f64 / GB as f64)
} else if bytes >= MB {
format!("{:.1} MB", bytes as f64 / MB as f64)
} else if bytes >= KB {
format!("{:.1} KB", bytes as f64 / KB as f64)
} else {
format!("{} B", bytes)
}
}