mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-06-12 01:14:27 +02:00
Add live git status indicators (#458)
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
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())))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user