gitdataai/lib/git/cmd/fork.rs
2026-05-30 01:38:40 +08:00

112 lines
3.7 KiB
Rust

use std::path::PathBuf;
use crate::{
bare::GitBare,
errors::{GitError, GitResult},
};
pub struct ForkRepoParams {
pub namespace: String,
pub repo_name: String,
pub default_branch: String,
pub description: Option<String>,
pub enable_lfs: bool,
}
impl ForkRepoParams {
pub async fn fork_bare(
storage_root: String,
source_storage_path: String,
params: ForkRepoParams,
) -> GitResult<String> {
let target_dir = PathBuf::from(&storage_root)
.join("repo")
.join(&params.namespace)
.join(&params.repo_name);
if target_dir.exists() {
return Err(GitError::Internal(format!(
"repository directory already exists: {}",
target_dir.display()
)));
}
let source_dir = PathBuf::from(&source_storage_path);
if !source_dir.exists() {
return Err(GitError::Internal(format!(
"source repository does not exist: {}",
source_dir.display()
)));
}
let output = duct::cmd(
"git",
&[
"clone",
"--bare",
source_dir.to_string_lossy().as_ref(),
target_dir.to_string_lossy().as_ref(),
],
)
.stdout_capture()
.stderr_capture()
.env("GIT_CONFIG_NOSYSTEM", "1")
.env("GIT_TERMINAL_PROMPT", "0")
.unchecked()
.run()?;
if !output.status.success() {
std::fs::remove_dir_all(&target_dir).ok();
return Err(GitError::CommandFailed {
status_code: output.status.code(),
stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
});
}
let bare = GitBare {
bare_dir: target_dir.clone(),
};
let symref_output = bare.git_command_trusted(vec![
"symbolic-ref".to_string(),
"HEAD".to_string(),
format!("refs/heads/{}", params.default_branch),
])?;
if !symref_output.success {
return Err(GitError::CommandFailed {
status_code: symref_output.status_code,
stderr: symref_output.stderr_lossy(),
});
}
let remote_output = bare.git_command_trusted(vec![
"remote".to_string(),
"add".to_string(),
"upstream".to_string(),
source_dir.to_string_lossy().to_string(),
])?;
if !remote_output.success {
tracing::warn!(
"failed to add upstream remote: {}",
remote_output.stderr_lossy()
);
}
if let Some(desc) = &params.description {
let desc_path = target_dir.join("description");
std::fs::write(&desc_path, desc)?;
}
if params.enable_lfs {
let gitattributes_path = target_dir.join("info").join("attributes");
if let Some(parent) = gitattributes_path.parent() {
std::fs::create_dir_all(parent)?;
}
let lfs_attributes = "*.psd filter=lfs diff=lfs merge=lfs -text\n\
*.zip filter=lfs diff=lfs merge=lfs -text\n\
*.tar filter=lfs diff=lfs merge=lfs -text\n\
*.gz filter=lfs diff=lfs merge=lfs -text\n\
*.mp4 filter=lfs diff=lfs merge=lfs -text\n\
*.mov filter=lfs diff=lfs merge=lfs -text\n";
std::fs::write(&gitattributes_path, lfs_attributes)?;
}
Ok(target_dir.to_string_lossy().to_string())
}
}