mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-05-15 04:10:10 +02:00
77 lines
2.3 KiB
Rust
77 lines
2.3 KiB
Rust
use crate::error::Result;
|
|
use crate::repository::open_repo;
|
|
use log::info;
|
|
use std::fs;
|
|
use std::path::{Component, Path};
|
|
|
|
pub fn git_restore(dir: &Path, rela_path: &Path) -> Result<()> {
|
|
let repo = open_repo(dir)?;
|
|
validate_relative_path(rela_path)?;
|
|
|
|
let status = repo.status_file(rela_path).ok();
|
|
let is_untracked = status
|
|
.is_some_and(|s| s.contains(git2::Status::WT_NEW) || s.contains(git2::Status::INDEX_NEW));
|
|
|
|
info!("Restoring file {rela_path:?} in {dir:?}");
|
|
if is_untracked {
|
|
let mut index = repo.index()?;
|
|
let _ = index.remove_path(rela_path);
|
|
index.write()?;
|
|
|
|
let path = repo.workdir().unwrap_or(dir).join(rela_path);
|
|
if path.is_dir() {
|
|
fs::remove_dir_all(path)?;
|
|
} else if path.exists() {
|
|
fs::remove_file(path)?;
|
|
}
|
|
return Ok(());
|
|
}
|
|
|
|
let head = repo.head()?;
|
|
let commit = head.peel_to_commit()?;
|
|
repo.reset_default(Some(commit.as_object()), &[rela_path])?;
|
|
|
|
let mut checkout = git2::build::CheckoutBuilder::new();
|
|
checkout.force().path(rela_path);
|
|
repo.checkout_head(Some(&mut checkout))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn git_restore_file_from_commit(dir: &Path, commit_oid: &str, rela_path: &Path) -> Result<()> {
|
|
let repo = open_repo(dir)?;
|
|
validate_relative_path(rela_path)?;
|
|
|
|
let oid = git2::Oid::from_str(commit_oid)?;
|
|
let commit = repo.find_commit(oid)?;
|
|
let tree = commit.tree()?;
|
|
let path = repo.workdir().unwrap_or(dir).join(rela_path);
|
|
|
|
info!("Restoring file {rela_path:?} from commit {commit_oid} in {dir:?}");
|
|
if tree.get_path(rela_path).is_err() {
|
|
if path.is_dir() {
|
|
fs::remove_dir_all(path)?;
|
|
} else if path.exists() {
|
|
fs::remove_file(path)?;
|
|
}
|
|
return Ok(());
|
|
}
|
|
|
|
let mut checkout = git2::build::CheckoutBuilder::new();
|
|
checkout.force().path(rela_path);
|
|
repo.checkout_tree(commit.as_object(), Some(&mut checkout))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn validate_relative_path(path: &Path) -> Result<()> {
|
|
let is_safe = !path.as_os_str().is_empty()
|
|
&& !path.is_absolute()
|
|
&& path.components().all(|c| matches!(c, Component::Normal(_)));
|
|
if is_safe {
|
|
Ok(())
|
|
} else {
|
|
Err(crate::error::Error::GenericError(format!("Invalid restore path {}", path.display())))
|
|
}
|
|
}
|