Files

203 lines
6.4 KiB
Rust

//! Copyright (c) 2022-2026 GitDataAi All rights reserved.
use crate::bare::GitBare;
use crate::error::GitResult;
use crate::pb::*;
impl GitBare {
/// Run heuristic optimization based on repo state.
pub fn optimize_repository(
&self,
request: OptimizeRepositoryRequest,
) -> GitResult<OptimizeRepositoryResponse> {
let strategy =
OptimizeStrategy::try_from(request.strategy).unwrap_or(OptimizeStrategy::Heuristic);
let mut stdout_all = String::new();
let mut stderr_all = String::new();
match strategy {
OptimizeStrategy::Heuristic | OptimizeStrategy::Aggressive => {
let stats = self.get_repository_statistics()?;
if (stats.commit_graph_size_bytes == 0 || strategy == OptimizeStrategy::Aggressive)
&& let Ok(resp) = write_commit_graph(self, false, false)
{
if !resp.ok {
stderr_all.push_str(&resp.stderr);
}
stdout_all.push_str(&resp.stdout);
}
let repack_needed = stats.loose_object_count > 1000 || stats.packfile_count > 10;
if repack_needed || strategy == OptimizeStrategy::Aggressive {
let full = strategy == OptimizeStrategy::Aggressive;
if let Ok(resp) = run_repack(self, full, true, true) {
if !resp.ok {
stderr_all.push_str(&resp.stderr);
}
stdout_all.push_str(&resp.stdout);
}
}
if strategy == OptimizeStrategy::Aggressive
&& let Ok(resp) = run_gc(self, true, true)
{
if !resp.ok {
stderr_all.push_str(&resp.stderr);
}
stdout_all.push_str(&resp.stdout);
}
}
OptimizeStrategy::Incremental => {
if let Ok(resp) = write_commit_graph(self, false, false) {
if !resp.ok {
stderr_all.push_str(&resp.stderr);
}
stdout_all.push_str(&resp.stdout);
}
}
OptimizeStrategy::Unspecified => {}
}
Ok(OptimizeRepositoryResponse {
ok: stderr_all.is_empty(),
stdout: stdout_all,
stderr: stderr_all,
})
}
fn get_repository_statistics(&self) -> GitResult<RepositoryStatistics> {
let loose = std::fs::read_dir(self.bare_dir.join("objects"))
.map(|d| {
d.filter_map(|e| e.ok())
.filter(|e| {
e.file_type().map(|t| t.is_dir()).unwrap_or(false)
&& e.file_name().to_string_lossy().len() == 2
})
.count() as u64
})
.unwrap_or(0);
let pack_dir = self.bare_dir.join("objects").join("pack");
let pack_count = std::fs::read_dir(&pack_dir)
.map(|d| d.filter_map(|e| e.ok()).count() as u64)
.unwrap_or(0);
let cg_size = std::fs::metadata(
self.bare_dir
.join("objects")
.join("info")
.join("commit-graph"),
)
.map(|m| m.len())
.unwrap_or(0);
Ok(RepositoryStatistics {
size_bytes: 0,
loose_object_count: loose,
packed_object_count: 0,
packfile_count: pack_count,
reference_count: 0,
commit_graph_size_bytes: cg_size,
multi_pack_index_size_bytes: 0,
})
}
}
fn write_commit_graph(
gb: &GitBare,
_split: bool,
_replace: bool,
) -> GitResult<RepositoryMaintenanceResponse> {
let out = std::process::Command::new("git")
.args([
"--git-dir",
&gb.bare_dir.to_string_lossy(),
"commit-graph",
"write",
"--reachable",
])
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.output()
.map_err(|e| crate::error::GitError::CommandFailed {
status_code: None,
stderr: e.to_string(),
})?;
Ok(RepositoryMaintenanceResponse {
ok: out.status.success(),
stdout: String::from_utf8_lossy(&out.stdout).into_owned(),
stderr: String::from_utf8_lossy(&out.stderr).into_owned(),
})
}
fn run_repack(
gb: &GitBare,
full: bool,
bitmaps: bool,
_midx: bool,
) -> GitResult<RepositoryMaintenanceResponse> {
let mut args = vec![
"--git-dir".to_string(),
gb.bare_dir.to_string_lossy().into_owned(),
"repack".to_string(),
];
if full {
args.push("-ad".to_string());
} else {
args.push("-d".to_string());
}
if bitmaps {
args.push("--write-bitmap-index".to_string());
}
let out = std::process::Command::new("git")
.args(&args)
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.output()
.map_err(|e| crate::error::GitError::CommandFailed {
status_code: None,
stderr: e.to_string(),
})?;
Ok(RepositoryMaintenanceResponse {
ok: out.status.success(),
stdout: String::from_utf8_lossy(&out.stdout).into_owned(),
stderr: String::from_utf8_lossy(&out.stderr).into_owned(),
})
}
fn run_gc(gb: &GitBare, prune: bool, aggressive: bool) -> GitResult<RepositoryMaintenanceResponse> {
let mut args = vec![
"--git-dir".to_string(),
gb.bare_dir.to_string_lossy().into_owned(),
"gc".to_string(),
];
if prune {
args.push("--prune=now".to_string());
}
if aggressive {
args.push("--aggressive".to_string());
}
let out = std::process::Command::new("git")
.args(&args)
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.output()
.map_err(|e| crate::error::GitError::CommandFailed {
status_code: None,
stderr: e.to_string(),
})?;
Ok(RepositoryMaintenanceResponse {
ok: out.status.success(),
stdout: String::from_utf8_lossy(&out.stdout).into_owned(),
stderr: String::from_utf8_lossy(&out.stderr).into_owned(),
})
}