//! Copyright (c) 2022-2026 GitDataAi All rights reserved. use crate::bare::GitBare; use crate::error::GitError; use crate::paginate; use crate::pb::{ListPackfilesRequest, ListPackfilesResponse, PackfileInfo}; impl GitBare { pub fn list_packfiles( &self, request: ListPackfilesRequest, ) -> crate::error::GitResult { let pack_dir = self.bare_dir.join("objects").join("pack"); let mut packfiles = Vec::new(); if pack_dir.exists() { for entry in std::fs::read_dir(&pack_dir).map_err(GitError::Io)? { let entry = entry.map_err(GitError::Io)?; let name = entry.file_name().to_string_lossy().into_owned(); if !name.ends_with(".pack") { continue; } let metadata = entry.metadata().map_err(GitError::Io)?; let base_name = name.trim_end_matches(".pack"); let idx_name = format!("{base_name}.idx"); let bmp_name = format!("{base_name}.bitmap"); let rev_name = format!("{base_name}.rev"); let keep_name = format!("{base_name}.keep"); let pack_hash = base_name .strip_prefix("pack-") .filter(|hex| !hex.is_empty()) .map(|hex| self.oid_to_pb(hex)); let mut object_count = 0u64; if let Some(hash_str) = base_name.strip_prefix("pack-") { let idx_path = pack_dir.join(format!("pack-{hash_str}.idx")); if idx_path.exists() { let verify = duct::cmd( "git", [ "--git-dir", self.bare_dir.to_string_lossy().as_ref(), "verify-pack", "-v", idx_path.to_string_lossy().as_ref(), ], ) .stdout_capture() .stderr_capture() .unchecked() .run(); if let Ok(v) = verify { let out = String::from_utf8_lossy(&v.stdout); object_count = out .lines() .filter(|l| { let parts: Vec<&str> = l.split_whitespace().collect(); parts.len() >= 3 && parts .first() .map(|s| s.len() == 40 || s.len() == 64) .unwrap_or(false) }) .count() as u64; } } } packfiles.push(PackfileInfo { name, pack_hash, size_bytes: metadata.len(), index_size_bytes: pack_dir .join(&idx_name) .metadata() .map(|m| m.len()) .unwrap_or(0), object_count, has_bitmap: pack_dir.join(&bmp_name).exists(), has_rev_index: pack_dir.join(&rev_name).exists(), kept: pack_dir.join(&keep_name).exists(), }); } } packfiles.sort_by(|a, b| a.name.cmp(&b.name)); let (packfiles, page_info) = paginate::paginate(&packfiles, request.pagination.as_ref()); Ok(ListPackfilesResponse { packfiles, page_info: Some(page_info), }) } }