feat(gateway): implement remote service forwarding for distributed git operations

- Add remote client functions for archive, blame, and branch services
- Implement fallback logic to forward requests to remote storage nodes
- Add logging for forwarding operations with route details
- Update Cargo.lock with new dependencies including ractor cluster libraries
- Extend .gitignore with IDE and build system files
- Remove outdated comments from bare repository implementation
This commit is contained in:
zhenyi
2026-06-08 01:21:20 +08:00
parent 5b740eecd7
commit 5c99b27421
22 changed files with 2015 additions and 98 deletions
-10
View File
@@ -13,8 +13,6 @@ impl GitBare {
Self { bare_dir }
}
/// Open the gix repository. Callers should open once per logical operation
/// and reuse the handle for all gix lookups within that operation.
pub fn gix_repo(&self) -> GitResult<gix::Repository> {
tracing::debug!(repo = %self.bare_dir.display(), "opening gix repository");
gix::open(&self.bare_dir).map_err(|e| {
@@ -39,7 +37,6 @@ impl GitBare {
}
PathBuf::from(p)
} else if !relative_path.is_empty() {
// relative_path alone is rejected unless absolute
return Err(GitError::InvalidArgument(
"relative_path requires storage_path to be set".into(),
));
@@ -47,14 +44,11 @@ impl GitBare {
return Err(GitError::InvalidArgument("empty repository path".into()));
};
// Join relative_path if provided
let bare_dir = if !relative_path.is_empty() && !storage_path.is_empty() {
let candidate = base.join(relative_path);
// Canonicalize to resolve any `..` / symlinks, then check still under base
let canonical = candidate
.canonicalize()
.unwrap_or_else(|_| candidate.clone());
// Path traversal check: canonical resolved dir must start with base
let base_canon = base.canonicalize().unwrap_or_else(|_| base.clone());
if !canonical.starts_with(&base_canon) {
tracing::warn!(
@@ -73,7 +67,6 @@ impl GitBare {
return Err(GitError::InvalidArgument("empty repository path".into()));
};
// Validate bare_dir exists, is a directory, and is readable
if !bare_dir.exists() {
tracing::warn!(path = %bare_dir.display(), "repository not found");
return Err(GitError::RepoNotFound);
@@ -85,10 +78,8 @@ impl GitBare {
)));
}
// Accept either bare repos (HEAD file) or non-bare (HEAD + .git)
let head_path = bare_dir.join("HEAD");
if !head_path.exists() {
// Maybe it's a non-bare repo
let git_dir = bare_dir.join(".git");
if git_dir.is_dir() && git_dir.join("HEAD").exists() {
tracing::debug!(path = %git_dir.display(), "resolved non-bare repo via .git subdir");
@@ -100,7 +91,6 @@ impl GitBare {
Ok(Self { bare_dir })
}
/// Detect the repository's object format (SHA-1 or SHA-256).
pub fn object_format(&self) -> crate::pb::ObjectFormat {
let repo = self.gix_repo().ok();
let kind = repo