Restore a single file according to its status. - `Added` (tracked but not recorded): undo tracking. The file stays on disk and becomes untracked, so we never destroy content the user created. This is what makes `restore ` honest about the "new file" change reported by `status`. - Otherwise (`Modified` / `Deleted`): restore the file's content from the pristine state. Returns the outcome
(
&self,
repo: &Repository,
repo_root: &Path,
path: &Path,
status: FileStatus,
)
| 249 | /// |
| 250 | /// Returns the outcome so the caller can report it accurately. |
| 251 | fn restore_file( |
| 252 | &self, |
| 253 | repo: &Repository, |
| 254 | repo_root: &Path, |
| 255 | path: &Path, |
| 256 | status: FileStatus, |
| 257 | ) -> CliResult<RestoreOutcome> { |
| 258 | if status == FileStatus::Added { |
| 259 | // Undo the `add`: stop tracking, but keep the file on disk. |
| 260 | repo.remove(path, TrackingOptions::default().with_recursive(false)) |
| 261 | .map_err(CliError::Repository)?; |
| 262 | return Ok(RestoreOutcome::Untracked); |
| 263 | } |
| 264 | |
| 265 | // Restore content from pristine. |
| 266 | let content = repo.get_file_content(path).map_err(|e| { |
| 267 | CliError::Internal(anyhow::anyhow!("Failed to get file content: {}", e)) |
| 268 | })?; |
| 269 | |
| 270 | let full_path = repo_root.join(path); |
| 271 | |
| 272 | match content { |
| 273 | Some(bytes) => { |
| 274 | // Ensure parent directory exists (a Deleted file may have had |
| 275 | // its directory removed too). |
| 276 | if let Some(parent) = full_path.parent() { |
| 277 | std::fs::create_dir_all(parent).map_err(|e| { |
| 278 | CliError::Internal(anyhow::anyhow!("Failed to create directory: {}", e)) |
| 279 | })?; |
| 280 | } |
| 281 | |
| 282 | // Write content |
| 283 | std::fs::write(&full_path, bytes).map_err(|e| { |
| 284 | CliError::Internal(anyhow::anyhow!("Failed to write file: {}", e)) |
| 285 | })?; |
| 286 | |
| 287 | Ok(RestoreOutcome::Restored) |
| 288 | } |
| 289 | None => { |
| 290 | // No pristine content available; nothing safe to do. |
| 291 | Ok(RestoreOutcome::Skipped) |
| 292 | } |
| 293 | } |
| 294 | } |
| 295 | |
| 296 | /// Check if a path matches any of the specified file filters. |
| 297 | fn matches_filter(&self, path: &str) -> bool { |