git_policy_cli/worktree/
worktree_convert_compare.rs1use std::os::unix::fs::MetadataExt;
16use std::path::{Path, PathBuf};
17
18pub fn first_difference(old: &Path, installed: &Path) -> std::io::Result<Option<String>> {
29 let device: u64 = std::fs::metadata(old)?.dev();
31 let mut pending: Vec<PathBuf> = vec![PathBuf::new()];
32 while let Some(relative) = pending.pop() {
33 for item in std::fs::read_dir(old.join(&relative))? {
34 let entry: std::fs::DirEntry = item?;
35 let inside: PathBuf = relative.join(entry.file_name());
36 let before: std::fs::Metadata = std::fs::symlink_metadata(old.join(&inside))?;
37 if before.dev() != device {
38 return Ok(Some(format!("{} is a subvolume or a mount point inside the old tree, which this command does not delete", inside.display())));
39 }
40 let after: std::fs::Metadata = match std::fs::symlink_metadata(installed.join(&inside)) {
41 Ok(found) => found,
42 Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(Some(format!("{} is missing from the installed worktree", inside.display()))),
43 Err(error) => return Err(error),
44 };
45 if before.file_type() != after.file_type() {
46 return Ok(Some(format!("{} is not the same kind of entry in the installed worktree", inside.display())));
47 }
48 if before.file_type().is_dir() {
49 pending.push(inside);
50 } else if before.file_type().is_symlink() {
51 if std::fs::read_link(old.join(&inside))? != std::fs::read_link(installed.join(&inside))? {
52 return Ok(Some(format!("the link {} points elsewhere in the installed worktree", inside.display())));
53 }
54 } else if before.file_type().is_file() && (before.size() != after.size() || before.mtime() != after.mtime() || before.mtime_nsec() != after.mtime_nsec()) {
55 return Ok(Some(format!("{} differs from the installed worktree (size or modification time)", inside.display())));
56 }
57 }
58 }
59 return Ok(None);
60}
61
62#[cfg(test)]
64#[path = "worktree_convert_compare_tests.rs"]
65mod tests;