Recursively copy LFS objects from source to destination, skipping files that already exist. LFS objects are content-addressed, so existing files are guaranteed to have identical content.
(src: &Path, dst: &Path)
| 47 | /// Recursively copy LFS objects from source to destination, skipping files that already exist. |
| 48 | /// LFS objects are content-addressed, so existing files are guaranteed to have identical content. |
| 49 | async fn copy_lfs_objects(src: &Path, dst: &Path) -> Result<(), String> { |
| 50 | if !src.exists() { |
| 51 | return Ok(()); |
| 52 | } |
| 53 | |
| 54 | let mut entries = tokio::fs::read_dir(src) |
| 55 | .await |
| 56 | .map_err(|e| format!("Failed to read LFS objects dir: {e}"))?; |
| 57 | |
| 58 | while let Ok(Some(entry)) = entries.next_entry().await { |
| 59 | let src_path = entry.path(); |
| 60 | let file_name = entry.file_name(); |
| 61 | let dst_path = dst.join(&file_name); |
| 62 | |
| 63 | let meta = tokio::fs::symlink_metadata(&src_path) |
| 64 | .await |
| 65 | .map_err(|e| format!("Failed to stat {}: {e}", src_path.display()))?; |
| 66 | |
| 67 | if meta.is_dir() { |
| 68 | Box::pin(copy_lfs_objects(&src_path, &dst_path)).await?; |
| 69 | } else if meta.is_file() && !dst_path.exists() { |
| 70 | if let Some(parent) = dst_path.parent() { |
| 71 | crate::file_system::create_dir(parent) |
| 72 | .await |
| 73 | .map_err(|e| format!("Failed to create LFS dir: {e}"))?; |
| 74 | } |
| 75 | crate::file_system::copy_file(&src_path, &dst_path) |
| 76 | .await |
| 77 | .map_err(|e| format!("Failed to copy LFS object: {e}"))?; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | Ok(()) |
| 82 | } |
| 83 | |
| 84 | impl RepoManager { |
| 85 | /// Creates a new RepoManager from the application context. |
no test coverage detected