Files
gitks/pack/index_pack.rs
T
zhenyi dcb0fb74c5 feat(core): implement Git repository operations with gRPC services
- Add advertise_refs functionality for Git protocol communication
- Implement archive service with TAR/ZIP format support and streaming
- Create blame service for Git file annotation with line tracking
- Add branch management including create, delete, rename and compare operations
- Implement merge checking with conflict detection and fast-forward handling
- Add cherry-pick functionality for applying commits between branches
- Integrate gix library for Git repository operations and object handling
- Add comprehensive test suite covering all Git operations
- Implement proper error handling and repository validation
- Add pagination support for large result sets
- Create protobuf definitions for all Git operations and data structures
- Add build system for gRPC code generation and dependency management
2026-06-04 13:05:38 +08:00

139 lines
4.7 KiB
Rust

use std::io::Write;
use crate::bare::GitBare;
use crate::error::{GitError, GitResult};
use crate::pb::{IndexPackRequest, IndexPackResponse};
impl GitBare {
/// Index a pack file from streamed input.
///
/// Client-streaming → unary response.
/// Collects all input chunks into a single pack, then runs `git index-pack`.
pub fn index_pack(&self, inputs: Vec<IndexPackRequest>) -> GitResult<IndexPackResponse> {
// Reassemble all chunks into a single pack data buffer
let mut pack_data = Vec::new();
let mut strict = false;
let mut keep = false;
for input in &inputs {
pack_data.extend_from_slice(&input.data);
if input.strict {
strict = true;
}
if input.keep {
keep = true;
}
}
if pack_data.is_empty() {
return Err(GitError::InvalidArgument("empty pack data".into()));
}
let pack_dir = self.bare_dir.join("objects").join("pack");
std::fs::create_dir_all(&pack_dir).map_err(GitError::Io)?;
// Write pack data to a unique temp file in the pack directory.
let mut tmp_file = tempfile::Builder::new()
.prefix("tmp_index_pack_")
.tempfile_in(&pack_dir)
.map_err(GitError::Io)?;
tmp_file.write_all(&pack_data).map_err(GitError::Io)?;
let tmp_path = tmp_file.path().to_path_buf();
let mut args = vec![
"--git-dir".to_string(),
self.bare_dir.to_string_lossy().into_owned(),
"index-pack".to_string(),
];
if strict {
args.push("--strict".into());
}
if keep {
args.push("--keep".into());
}
args.push(tmp_path.to_string_lossy().into_owned());
let result = duct::cmd("git", &args)
.stdout_capture()
.stderr_capture()
.unchecked()
.run()?;
drop(tmp_file);
if !result.status.success() {
return Err(GitError::CommandFailed {
status_code: result.status.code(),
stderr: String::from_utf8_lossy(&result.stderr).into_owned(),
});
}
// Parse the output to extract the pack hash
let output = String::from_utf8_lossy(&result.stdout);
let stderr = String::from_utf8_lossy(&result.stderr);
let all_output = format!("{output}\n{stderr}");
// git index-pack outputs the .idx and .pack filenames
// e.g. "... pack-<hex>.pack ... pack-<hex>.idx"
let pack_hash = all_output
.lines()
.filter_map(|line| {
// Look for the hash after "pack-" and before ".idx" or ".pack"
let trimmed = line.trim();
if let Some(idx) = trimmed.find("pack-") {
let rest = &trimmed[idx + 5..];
if let Some(end) = rest.find('.') {
let hex = &rest[..end];
if hex.len() == 40 || hex.len() == 64 {
return Some(hex.to_string());
}
}
}
None
})
.next();
// Try to get object count from .idx if it exists
let mut object_count = 0u64;
if let Some(ref hash) = pack_hash {
let idx_path = pack_dir.join(format!("pack-{}.idx", hash));
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;
}
}
}
Ok(IndexPackResponse {
pack_hash: pack_hash.map(|h| self.oid_to_pb(h)),
object_count,
stderr: stderr.into_owned(),
})
}
}