feat(api): extend commit and diff services with new functionality
- Add FindCommit, ListCommitsByOid, CommitIsAncestor RPCs to CommitService - Add CheckObjectsExist, CommitsByMessage, GetCommitStats RPCs to CommitService - Add LastCommitForPath, CountCommits, CountDivergingCommits RPCs to CommitService - Add RawDiff, RawPatch, FindChangedPaths RPCs to DiffService - Add FindMergeBase, WriteRef, SearchFilesByContent RPCs to RepositoryService - Add SearchFilesByName, ObjectsSize, RepositorySize RPCs to RepositoryService - Add FindLicense, OptimizeRepository, GetRawChanges RPCs to RepositoryService - Add FetchRemote, CreateRepositoryFromURL RPCs to RepositoryService - Implement server handlers for all new RPC methods - Add new modules for commit counting, finding, and querying features - Add new modules for diff changed paths and raw operations - Add new modules for refs and remote operations - Remove unnecessary comments from various source files - Update proto definitions with new message types and service methods
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
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()?;
|
||||
|
||||
// Run commit-graph write if needed
|
||||
if stats.commit_graph_size_bytes == 0 || strategy == OptimizeStrategy::Aggressive {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Repack if many loose objects or packfiles
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Prune if aggressive
|
||||
if strategy == OptimizeStrategy::Aggressive {
|
||||
if 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 => {
|
||||
// Just run commit-graph write incrementally
|
||||
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> {
|
||||
// Count loose objects
|
||||
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);
|
||||
|
||||
// Count packfiles
|
||||
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);
|
||||
|
||||
// Check commit-graph
|
||||
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(),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user