git_policy_cli/worktree/
worktree_convert_remove.rs1use crate::fs_beneath::{ObjectId, handle_path, identity_at, identity_of, open_directory_at, remove_at};
17use crate::subvolume_error::MigrateError;
18use crate::subvolume_platform::Platform;
19use crate::subvolume_remove::remove_tree;
20use crate::worktree_convert_compare::first_difference;
21use crate::worktree_convert_journal::is_recorded_object;
22use std::ffi::OsStr;
23use std::os::unix::fs::PermissionsExt;
24use std::path::{Path, PathBuf};
25
26#[derive(Clone, Debug, Eq, PartialEq)]
28pub enum Removal {
29 Removed,
31 Absent,
33 Kept(String),
35 Differs(String),
38}
39
40pub fn remove_recorded_old_tree(platform: &dyn Platform, parent: &std::fs::File, name: &OsStr, old: ObjectId, installed: &Path) -> Result<Removal, MigrateError> {
50 return remove_recorded_old_tree_seamed(platform, parent, name, old, installed, &mut || {});
51}
52
53pub fn remove_recorded_old_tree_seamed(platform: &dyn Platform, parent: &std::fs::File, name: &OsStr, old: ObjectId, installed: &Path, after_pinned: &mut dyn FnMut()) -> Result<Removal, MigrateError> {
56 let shown: PathBuf = PathBuf::from(name);
57 let pinned: std::fs::File = match open_directory_at(parent, name) {
58 Ok(opened) => opened,
59 Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(Removal::Absent),
60 Err(error) => return Ok(Removal::Kept(format!("{} cannot be opened as a directory below its parent ({error}), so it is not the old tree this conversion recorded", shown.display()))),
61 };
62 let found = identity_of(&pinned).map_err(|error: std::io::Error| { return MigrateError::io("examine", shown.as_path(), &error); })?;
63 if !is_recorded_object(&old, &found) {
64 return Ok(Removal::Kept(format!("{} is not the old tree this conversion recorded", shown.display())));
65 }
66 let through: PathBuf = handle_path(&pinned);
67 if platform.is_subvolume_root(through.as_path())? {
68 return Ok(Removal::Kept(format!("{} is a subvolume, so it is not the old tree this conversion recorded", shown.display())));
69 }
70 match first_difference(through.as_path(), installed) {
72 Ok(None) => {}
73 Ok(Some(reason)) => return Ok(Removal::Differs(reason)),
74 Err(error) => return Ok(Removal::Differs(format!("the old tree cannot be compared with the installed worktree ({error})"))),
75 }
76 let metadata: std::fs::Metadata = pinned.metadata().map_err(|error: std::io::Error| { return MigrateError::io("examine", shown.as_path(), &error); })?;
77 if std::os::unix::fs::MetadataExt::mode(&metadata) & 0o700 != 0o700 {
78 pinned.set_permissions(std::fs::Permissions::from_mode(std::os::unix::fs::MetadataExt::mode(&metadata) | 0o700)).map_err(|error: std::io::Error| { return MigrateError::io("make writable", shown.as_path(), &error); })?;
79 }
80 after_pinned();
81 let listing = std::fs::read_dir(&through).map_err(|error: std::io::Error| { return MigrateError::io("list the old tree", shown.as_path(), &error); })?;
82 for item in listing {
83 let entry: std::fs::DirEntry = item.map_err(|error: std::io::Error| { return MigrateError::io("list the old tree", shown.as_path(), &error); })?;
84 remove_tree(entry.path().as_path()).map_err(|error: std::io::Error| { return MigrateError::io("remove the old tree", entry.path().as_path(), &error); })?;
85 }
86 match identity_at(parent, name) {
87 Ok(still) if is_recorded_object(&old, &still) => {}
88 Ok(_) => return Ok(Removal::Kept(format!("{} no longer names the old tree, which is empty and was left where it is", shown.display()))),
89 Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(Removal::Removed),
90 Err(error) => return Err(MigrateError::io("examine", shown.as_path(), &error)),
91 }
92 remove_at(parent, name, true).map_err(|error: std::io::Error| { return MigrateError::io("remove the old tree", shown.as_path(), &error); })?;
93 return Ok(Removal::Removed);
94}