f5b4ea5
//! pack usecase: pack ingestion and indexing.
use crate::GitRepository;
use crate::pack::impls::PackOps;
use crate::pack::types::{HotPackResult, IndexResult, IngestPackResult, PackStats, RepackResult};
fn count_loose_objects(repo: &gix::Repository) -> u64 {
let mut count = 0u64;
let dir = repo.path().join("objects");
let Ok(read) = std::fs::read_dir(dir) else {
return 0;
};
for shelf in read.flatten() {
let Ok(entries) = std::fs::read_dir(shelf.path()) else {
continue;
};
for entry in entries.flatten() {
if entry.path().is_file() {
count += 1;
}
}
}
count
}
impl PackOps for GitRepository {
fn pack_ingest_pack(&self, data: &[u8]) -> Result<IngestPackResult, crate::Error> {
let repo = self.repo()?;
let path = repo.path().to_path_buf();
// Index and install the pack into the object database via `git index-pack`.
let out = duct::cmd("git", &["index-pack", "--stdin"])
.env("GIT_DIR", &path)
.env("GIT_TERMINAL_PROMPT", "0")
.stdin_bytes(data)
.read()
.map_err(|e| crate::Error::Other(format!("git index-pack failed: {e}")))?;
let object_count = out.lines().count() as u64;
tracing::info!(
object_count,
size = data.len(),
"pack ingested via git index-pack"
);
Ok(IngestPackResult {
object_count,
size: data.len() as i64,
delta_count: 0,
})
}
fn pack_index(&self, pack_id: &str) -> Result<IndexResult, crate::Error> {
let repo = self.repo()?;
let path = repo.path().to_path_buf();
let pack_dir = path.join("objects/pack");
let pack_file = pack_dir.join(format!("{pack_id}.pack"));
if !pack_file.exists() {
return Err(crate::Error::NotExist(format!("pack {pack_id}")));
}
let pack_file = pack_file.to_string_lossy().to_string();
let out = crate::gitcli::run(&path, &["index-pack", &pack_file])?;
let object_count = out.lines().count() as u64;
tracing::info!(pack_id, object_count, "pack indexed");
Ok(IndexResult {
pack_id: pack_id.into(),
object_count,
})
}
fn pack_repack(&self) -> Result<RepackResult, crate::Error> {
let repo = self.repo()?;
let path = repo.path().to_path_buf();
crate::gitcli::run(&path, &["repack", "-a", "-d", "-q"])?;
tracing::info!("repack completed via git repack");
Ok(RepackResult {
packs: vec![],
pruned: 0,
})
}
fn pack_stats(&self) -> Result<PackStats, crate::Error> {
let tip = self.tip_id()?;
if let Some(hit) = self.cache_get_rev::<PackStats>("pack", "stats", "stats", tip) {
return Ok(hit);
}
let repo = self.repo()?;
let loose = count_loose_objects(&repo);
let v = PackStats {
objects: loose,
size: 0,
delta_count: 0,
};
self.cache_put_rev("pack", "stats", "stats", tip, &v);
Ok(v)
}
fn pack_cache_hot_pack(&self, pack_id: &str) -> Result<HotPackResult, crate::Error> {
// Hot-pack caching is a bespoke feature; fall back to a repack so the
// object data is consolidated, then report the pack id.
let repo = self.repo()?;
let path = repo.path().to_path_buf();
crate::gitcli::run(&path, &["repack", "-a", "-d", "-q"])?;
tracing::info!(pack_id, "hot pack cache performed via repack");
Ok(HotPackResult {
cached: true,
pack_id: pack_id.into(),
})
}
}