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, pub enable_lfs: bool, } impl ForkRepoParams { pub async fn fork_bare( storage_root: String, source_storage_path: String, params: ForkRepoParams, ) -> GitResult { let target_dir = PathBuf::from(&storage_root) .join("repo") .join(¶ms.namespace) .join(¶ms.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) = ¶ms.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()) } }