use anyhow::Result;
use git2::Repository;
use std::path::Path;
fn expand_pathspecs(repo: &Repository, pathspecs: &[String]) -> Result<Vec<String>> {
let index = repo.index()?;
let mut expanded = Vec::new();
for spec in pathspecs {
if spec == "." {
return Ok(Vec::new()); }
let prefix = if spec.ends_with('/') {
spec.clone()
} else {
format!("{}/", spec)
};
let mut matched_dir = false;
for entry in index.iter() {
let entry_path = String::from_utf8_lossy(&entry.path);
if entry_path.starts_with(&prefix) {
expanded.push(entry_path.into_owned());
matched_dir = true;
}
}
if !matched_dir {
expanded.push(spec.clone());
}
}
Ok(expanded)
}
pub fn execute(
path: &Path,
pathspecs: &[String],
staged: bool,
source: Option<&str>,
) -> Result<()> {
let repo = crate::ops::open_repo(path)?;
let expanded = expand_pathspecs(&repo, pathspecs)?;
let restore_all = expanded.is_empty();
if staged {
let tree = if let Some(src) = source {
let obj = repo.revparse_single(src)?;
obj.peel_to_tree()?
} else {
repo.head()?.peel_to_tree()?
};
let mut index = repo.index()?;
if restore_all {
repo.reset(tree.as_object(), git2::ResetType::Mixed, None)?;
println!("Updated index to match HEAD");
} else {
for spec in &expanded {
let p = Path::new(spec);
if let Ok(entry) = tree.get_path(p) {
let idx_entry = git2::IndexEntry {
ctime: git2::IndexTime::new(0, 0),
mtime: git2::IndexTime::new(0, 0),
dev: 0,
ino: 0,
mode: entry.filemode() as u32,
uid: 0,
gid: 0,
file_size: 0,
id: entry.id(),
flags: 0,
flags_extended: 0,
path: spec.as_bytes().to_vec(),
};
index.add(&idx_entry)?;
} else {
index.remove_path(p)?;
}
println!("Updated '{}' in index", spec);
}
index.write()?;
}
} else {
let mut checkout = git2::build::CheckoutBuilder::new();
checkout.force();
if !restore_all {
for spec in &expanded {
checkout.path(spec);
}
}
if let Some(src) = source {
let obj = repo.revparse_single(src)?;
let tree = obj.peel_to_tree()?;
repo.checkout_tree(tree.as_object(), Some(&mut checkout))?;
} else {
repo.checkout_index(None, Some(&mut checkout))?;
}
if restore_all {
println!("Updated all tracked files");
} else {
for spec in &expanded {
println!("Updated '{}'", spec);
}
}
}
Ok(())
}