Copy repository for a task using the task ID and repository root Creates a task repository by cloning the source repository and overlaying all non-ignored files from the working directory including: - Tracked files with their current working directory content (including unstaged changes) - Staged files (newly added files in the index) - Untracked files (not ignored) - The .tsk directory for proje
(
&self,
task_id: &str,
repo_root: &Path,
source_commit: Option<&str>,
branch_name: &str,
copy_mode: CopyMode,
)
| 106 | /// |
| 107 | /// Returns a [`CopyResult`] with the path and any non-fatal warnings. |
| 108 | pub async fn copy_repo( |
| 109 | &self, |
| 110 | task_id: &str, |
| 111 | repo_root: &Path, |
| 112 | source_commit: Option<&str>, |
| 113 | branch_name: &str, |
| 114 | copy_mode: CopyMode, |
| 115 | ) -> Result<CopyResult, String> { |
| 116 | // Use the task ID directly for the directory name |
| 117 | let task_dir_name = task_id; |
| 118 | let branch_name = branch_name.to_string(); |
| 119 | let mut warnings = Vec::new(); |
| 120 | |
| 121 | // Create the task directory structure in centralized location |
| 122 | let task_dir = self.ctx.tsk_env().task_dir(task_dir_name); |
| 123 | let repo_path = task_dir.join("repo"); |
| 124 | |
| 125 | // Create directories if they don't exist |
| 126 | crate::file_system::create_dir(&task_dir) |
| 127 | .await |
| 128 | .map_err(|e| format!("Failed to create task directory: {e}"))?; |
| 129 | |
| 130 | // Check if the provided path is in a git repository |
| 131 | if !git_operations::is_git_repository(repo_root).await? { |
| 132 | return Err("Not in a git repository".to_string()); |
| 133 | } |
| 134 | |
| 135 | // Use the provided repository root |
| 136 | let current_dir = repo_root.to_path_buf(); |
| 137 | |
| 138 | // Get list of all files that should be copied (only needed for WorkingTree mode): |
| 139 | // 1. All tracked files (from working directory, including unstaged changes) |
| 140 | // 2. All staged files (including newly added files in the index) |
| 141 | // 3. All untracked files (not ignored) |
| 142 | let all_files_to_copy = if matches!(copy_mode, CopyMode::WorkingTree) { |
| 143 | git_operations::get_all_non_ignored_files(¤t_dir).await? |
| 144 | } else { |
| 145 | Vec::new() |
| 146 | }; |
| 147 | |
| 148 | // Clone repository with optimized pack files (no hardlinks) |
| 149 | // This creates an efficient repository copy with 1-2 pack files instead of |
| 150 | // preserving fragmented pack structure from the source repository |
| 151 | git_operations::clone_local(¤t_dir, &repo_path) |
| 152 | .await |
| 153 | .map_err(|e| format!("Failed to clone repository: {e}"))?; |
| 154 | |
| 155 | // Copy .git/modules from source to preserve submodule git data |
| 156 | // This must happen BEFORE branch creation and file overlay so that |
| 157 | // submodule .git files have valid targets to point to |
| 158 | let src_git_common = crate::repo_utils::resolve_git_common_dir(¤t_dir) |
| 159 | .unwrap_or_else(|_| current_dir.join(".git")); |
| 160 | let src_modules = src_git_common.join("modules"); |
| 161 | let dst_modules = repo_path.join(".git/modules"); |
| 162 | |
| 163 | if crate::file_system::exists(&src_modules) |
| 164 | .await |
| 165 | .unwrap_or(false) |