Extract restore_path

This commit is contained in:
Josh Hansen 2023-10-01 22:02:27 -07:00
parent e1a3df5954
commit f3d6b07650

View file

@ -15,6 +15,7 @@ use crate::{
XATTR_HEAD,
};
#[derive(Copy, Clone)]
pub enum NewFileHandling {
Keep,
Delete,
@ -28,6 +29,121 @@ pub enum RestoreErr {
PathNotContainedByTable(PathBuf),
}
/**
* Return a path to its state as of the commit with uuid specified in `commit`
*/
pub fn reset_path(
path: &PathBuf,
commit: &str,
recursive: bool,
verbose: bool,
new_file_handling: NewFileHandling,
) -> Result<()> {
let max_depth = if recursive { usize::MAX } else { 0 };
let info =
containing_table_info(path)?.ok_or(RestoreErr::PathNotContainedByTable(path.clone()))?;
let table_path = info.path_abs();
let snapshot_path = table_snapshot_path(table_path, commit);
for entry in WalkDir::new(path)
.max_depth(max_depth)
.into_iter()
.filter_entry(|e| !is_hidden(e))
{
let entry = entry?;
let path = entry.path().to_path_buf();
let abs_path = path.absolutize().unwrap().to_path_buf();
let rel_path = abs_path.relativize(table_path)?;
let snapshot_file_path = {
let mut p = snapshot_path.clone();
p.push(&rel_path);
p
}
.absolutize()
.unwrap()
.to_path_buf();
if abs_path.exists() {
if snapshot_file_path.exists() {
// Both paths exist
// Now handle files vs directories
if snapshot_file_path.is_dir() {
if abs_path.is_dir() {
// do nothing - both paths are directories
} else {
// Replace file with directory
remove_file(&abs_path)?;
let working_dest_dir = &abs_path;
for entry in WalkDir::new(&snapshot_file_path).into_iter() {
let entry = entry?;
let snapshot_rel = entry
.path()
.to_path_buf()
.relativize(&snapshot_file_path)
.unwrap();
let working = snapshot_rel.resolve_curdir(working_dest_dir).unwrap();
reflink_or_copy(entry.path(), &working)?;
}
if verbose {
println!("r{}", path.display());
}
}
} else {
// Snapshot is file
// Copy over working either way
if abs_path.is_dir() {
remove_dir(&abs_path)?;
// } else {
// remove_file(&abs_path)?;
}
reflink_or_copy(&snapshot_file_path, abs_path)?;
if verbose {
println!("r{}", path.display());
}
}
} else {
// working exists; snapshot doesn't
match new_file_handling {
NewFileHandling::Delete => {
remove_file(abs_path)?;
if verbose {
println!("-{}", path.display());
}
}
NewFileHandling::Keep => { /* do nothing, obvvvvvsly */ }
}
}
} else {
if snapshot_file_path.exists() {
// snapshot exists; working doesn't
reflink_or_copy(&snapshot_file_path, abs_path)?;
if verbose {
println!("r{}", path.display());
}
} else {
// neither working nor snapshot exists
eprintln!(
"WTF? {} exists neither in working directory nor in most recent commit",
rel_path.display()
);
}
}
}
Ok(())
}
/**
* Return files to their state as of the most recent commit.
*/
@ -37,7 +153,6 @@ pub fn restore(
verbose: bool,
new_file_handling: NewFileHandling,
) -> Result<()> {
let max_depth = if recursive { usize::MAX } else { 0 };
for path in paths {
let info = containing_table_info(path)?
.ok_or(RestoreErr::PathNotContainedByTable(path.clone()))?;
@ -48,103 +163,7 @@ pub fn restore(
let uuid = String::from_utf8(uuid_bytes)?;
let snapshot_path = table_snapshot_path(table_path, uuid);
for entry in WalkDir::new(path)
.max_depth(max_depth)
.into_iter()
.filter_entry(|e| !is_hidden(e))
{
let entry = entry?;
let path = entry.path().to_path_buf();
let abs_path = path.absolutize().unwrap().to_path_buf();
let rel_path = abs_path.relativize(table_path)?;
let snapshot_file_path = {
let mut p = snapshot_path.clone();
p.push(&rel_path);
p
}
.absolutize()
.unwrap()
.to_path_buf();
if abs_path.exists() {
if snapshot_file_path.exists() {
// Both paths exist
// Now handle files vs directories
if snapshot_file_path.is_dir() {
if abs_path.is_dir() {
// do nothing - both paths are directories
} else {
// Replace file with directory
remove_file(&abs_path)?;
let working_dest_dir = &abs_path;
for entry in WalkDir::new(&snapshot_file_path).into_iter() {
let entry = entry?;
let snapshot_rel = entry
.path()
.to_path_buf()
.relativize(&snapshot_file_path)
.unwrap();
let working =
snapshot_rel.resolve_curdir(working_dest_dir).unwrap();
reflink_or_copy(entry.path(), &working)?;
}
if verbose {
println!("r{}", path.display());
}
}
} else {
// Snapshot is file
// Copy over working either way
if abs_path.is_dir() {
remove_dir(&abs_path)?;
// } else {
// remove_file(&abs_path)?;
}
reflink_or_copy(&snapshot_file_path, abs_path)?;
if verbose {
println!("r{}", path.display());
}
}
} else {
// working exists; snapshot doesn't
match new_file_handling {
NewFileHandling::Delete => {
remove_file(abs_path)?;
if verbose {
println!("-{}", path.display());
}
}
NewFileHandling::Keep => { /* do nothing, obvvvvvsly */ }
}
}
} else {
if snapshot_file_path.exists() {
// snapshot exists; working doesn't
reflink_or_copy(&snapshot_file_path, abs_path)?;
if verbose {
println!("r{}", path.display());
}
} else {
// neither working nor snapshot exists
eprintln!(
"WTF? {} exists neither in working directory nor in most recent commit",
rel_path.display()
);
}
}
}
reset_path(path, uuid.as_str(), recursive, verbose, new_file_handling)?;
}
Ok(())