Files
gitks/server/repository_maint.rs
T
zhenyi 998f393ed0 feat(server): add comprehensive Git repository services with test coverage
- Implement ArchiveService for repository archive operations
- Add BlameService for Git blame functionality
- Create BranchService with full branch management capabilities
- Integrate CommitService for commit operations and history
- Add DiffService for generating diffs and patches
- Implement MergeService with conflict resolution features
- Add PackService for Git packfile operations
- Create TagService for Git tag management
- Add TreeService for Git tree operations
- Implement comprehensive repository management functions
- Add repository statistics and health checking capabilities
- Include garbage collection and repacking operations
- Add repository configuration management
- Implement error handling and status conversion utilities
- Add test suite covering all repository operations
- Create utility functions for Git command execution
- Add streaming response support for large data operations
- Implement request resolution and validation helpers
2026-06-04 14:10:21 +08:00

158 lines
4.6 KiB
Rust

use crate::pb::*;
use super::git_cmd;
pub(crate) fn maintenance_response(out: std::process::Output) -> RepositoryMaintenanceResponse {
RepositoryMaintenanceResponse {
ok: out.status.success(),
stdout: String::from_utf8_lossy(&out.stdout).into_owned(),
stderr: String::from_utf8_lossy(&out.stderr).into_owned(),
}
}
fn dir_size(path: &std::path::Path) -> u64 {
let mut total = 0u64;
if let Ok(entries) = std::fs::read_dir(path) {
for entry in entries.flatten() {
let p = entry.path();
if p.is_file() {
total += entry.metadata().map(|m| m.len()).unwrap_or(0);
} else if p.is_dir() {
total += dir_size(&p);
}
}
}
total
}
fn count_refs(gb: &crate::bare::GitBare) -> u64 {
let out = git_cmd(gb, &["for-each-ref", "--format=%(refname)"]).unwrap_or_else(|_| {
std::process::Output {
status: Default::default(),
stdout: Vec::new(),
stderr: Vec::new(),
}
});
String::from_utf8_lossy(&out.stdout)
.lines()
.filter(|l| !l.is_empty())
.count() as u64
}
fn file_len(path: &std::path::Path) -> u64 {
std::fs::metadata(path).map(|m| m.len()).unwrap_or(0)
}
pub(crate) fn get_statistics(gb: &crate::bare::GitBare) -> RepositoryStatistics {
let size_bytes = dir_size(&gb.bare_dir);
let mut loose_object_count: u64 = 0;
let mut packed_object_count: u64 = 0;
let mut packfile_count: u64 = 0;
if let Ok(out) = git_cmd(gb, &["count-objects", "-v"]) {
for line in String::from_utf8_lossy(&out.stdout).lines() {
let line = line.trim();
if let Some(v) = line.strip_prefix("count: ") {
loose_object_count = v.trim().parse().unwrap_or(0);
} else if let Some(v) = line.strip_prefix("in-pack: ") {
packed_object_count = v.trim().parse().unwrap_or(0);
} else if let Some(v) = line.strip_prefix("packs: ") {
packfile_count = v.trim().parse().unwrap_or(0);
}
}
}
let reference_count = count_refs(gb);
let commit_graph_size_bytes = file_len(&gb.bare_dir.join("objects/info/commit-graph"));
let multi_pack_index_size_bytes = file_len(&gb.bare_dir.join("objects/pack/multi-pack-index"));
RepositoryStatistics {
size_bytes,
loose_object_count,
packed_object_count,
packfile_count,
reference_count,
commit_graph_size_bytes,
multi_pack_index_size_bytes,
}
}
pub(crate) fn check_health(
gb: &crate::bare::GitBare,
connectivity_only: bool,
) -> Result<RepositoryHealthResponse, tonic::Status> {
let mut args: Vec<&str> = vec!["fsck"];
if connectivity_only {
args.push("--connectivity-only");
}
let out = git_cmd(gb, &args)?;
let text = String::from_utf8_lossy(&out.stdout);
let mut warnings = Vec::new();
let mut errors = Vec::new();
for line in text.lines() {
if line.starts_with("error:") {
errors.push(line.to_string());
} else if line.starts_with("warning:") {
warnings.push(line.to_string());
}
}
Ok(RepositoryHealthResponse {
ok: out.status.success(),
warnings,
errors,
statistics: None,
})
}
pub(crate) fn run_gc(
gb: &crate::bare::GitBare,
prune: bool,
aggressive: bool,
) -> Result<RepositoryMaintenanceResponse, tonic::Status> {
let mut args: Vec<&str> = vec!["gc"];
if prune {
args.push("--prune=now");
}
if aggressive {
args.push("--aggressive");
}
let out = git_cmd(gb, &args)?;
Ok(maintenance_response(out))
}
pub(crate) fn run_repack(
gb: &crate::bare::GitBare,
full: bool,
write_bitmaps: bool,
write_multi_pack_index: bool,
) -> Result<RepositoryMaintenanceResponse, tonic::Status> {
let mut args: Vec<&str> = vec!["repack", "-d"];
if full {
args.push("-a");
}
if write_bitmaps {
args.push("--write-bitmap-index");
}
if write_multi_pack_index {
args.push("--write-midx");
}
let out = git_cmd(gb, &args)?;
Ok(maintenance_response(out))
}
pub(crate) fn run_commit_graph_write(
gb: &crate::bare::GitBare,
split: bool,
replace: bool,
) -> Result<RepositoryMaintenanceResponse, tonic::Status> {
let mut args: Vec<&str> = vec!["commit-graph", "write"];
if split {
args.push("--split");
}
if !replace {
args.push("--append");
}
let out = git_cmd(gb, &args)?;
Ok(maintenance_response(out))
}