Skip to content
Clone
785c606
114 lines 3.8 KB Rust
//! remote usecase: remote/mirror operations.
use crate::GitRepository;
use crate::remote::impls::RemoteOps;
use crate::remote::types::{
FetchResult, ImportResult, MirrorType, PushResult, RemoteAuth, RemoteConfig,
};
use crate::validate;
impl RemoteOps for GitRepository {
fn remote_clone_import(
&self,
src: &str,
_config: &RemoteConfig,
) -> Result<ImportResult, crate::Error> {
// SSRF / path-traversal guard: only allow http(s)/ssh remote URLs,
// never local paths or file://.
validate::allowed_remote_scheme(src)?;
let repo = self.repo()?;
let path = repo.path().to_path_buf();
// Import into the current bare repository via `git fetch` from the
// remote (avoids a shell; src is a validated URL).
crate::gitcli::run_remote(
&path,
&[
"fetch",
"--quiet",
src,
"+refs/heads/*:refs/remotes/origin/*",
],
&RemoteAuth::None,
)?;
let default_branch = crate::gitcli::run(
&path,
&["symbolic-ref", "--short", "refs/remotes/origin/HEAD"],
)
.unwrap_or_else(|_| "main".to_string());
let commit_count =
crate::gitcli::run(&path, &["rev-list", "--count", "refs/remotes/origin/*"])
.ok()
.and_then(|s| s.trim().parse::<u64>().ok())
.unwrap_or(0);
tracing::info!(src = %src, commit_count, "remote clone/import completed");
Ok(ImportResult {
default_branch,
commit_count,
})
}
fn remote_fetch(&self, config: &RemoteConfig) -> Result<FetchResult, crate::Error> {
validate::allowed_remote_scheme(&config.url)?;
let repo = self.repo()?;
let path = repo.path().to_path_buf();
let refspec = config
.refspec
.as_deref()
.unwrap_or("+refs/heads/*:refs/remotes/origin/*");
crate::gitcli::run_remote(
&path,
&["fetch", "--quiet", &config.url, refspec],
&config.auth,
)?;
tracing::info!(url = %config.url, "remote fetch completed");
Ok(FetchResult {
updated: vec![],
new: vec![],
})
}
fn remote_push(
&self,
config: &RemoteConfig,
refspec: Option<&str>,
) -> Result<PushResult, crate::Error> {
validate::allowed_remote_scheme(&config.url)?;
let repo = self.repo()?;
let path = repo.path().to_path_buf();
let rs = refspec.unwrap_or("+refs/heads/*:refs/heads/*");
crate::gitcli::run_remote(&path, &["push", "--quiet", &config.url, rs], &config.auth)?;
tracing::info!(url = %config.url, "remote push completed");
Ok(PushResult {
pushed: vec![],
rejected: vec![],
})
}
fn remote_set_mirror(
&self,
mirror_type: MirrorType,
config: &RemoteConfig,
) -> Result<(), crate::Error> {
validate::allowed_remote_scheme(&config.url)?;
let repo = self.repo()?;
let path = repo.path().to_path_buf();
let mirror_flag = match mirror_type {
MirrorType::Pull => "--mirror=fetch",
MirrorType::Push => "--mirror=push",
};
crate::gitcli::run(
&path,
&["remote", "add", mirror_flag, "mirror", &config.url],
)?;
tracing::info!(url = %config.url, mirror = ?mirror_type, "mirror configured");
Ok(())
}
fn remote_remove_remote(&self, name: &str) -> Result<(), crate::Error> {
let repo = self.repo()?;
let path = repo.path().to_path_buf();
crate::gitcli::run(&path, &["remote", "remove", name])?;
tracing::info!(name, "remote removed");
Ok(())
}
}