use super::*;
use crate::fs_btrfs_ioctl::subvolume_create_at;
use crate::fs_btrfs_test_support::{btrfs_fixture, removal_allowed};
use crate::test_support::{age, git, git_context, remove, repository};
fn clean_subvolume_worktree(root: &Path) -> (PathBuf, PathBuf) {
let repo: PathBuf = repository(root, "repo");
std::fs::write(repo.join("tracked.txt"), b"tracked\n").expect("tracked");
git(repo.as_path(), &["add", "tracked.txt"]);
git(repo.as_path(), &["commit", "--quiet", "--message=tracked"]);
let worktree: PathBuf = root.join("wt");
subvolume_create_at(worktree.as_path()).expect("subvolume");
git(repo.as_path(), &["worktree", "add", "--quiet", "-b", "topic", "../wt"]);
let canonical: PathBuf = std::fs::canonicalize(worktree.as_path()).expect("canonical");
age(canonical.join("tracked.txt").as_path());
git(canonical.as_path(), &["update-index", "--refresh"]);
return (repo, canonical);
}
#[test]
fn btrfs_a_clean_subvolume_worktree_is_destroyed_where_the_mount_allows_it() {
let Some(root) = btrfs_fixture("remove-clean") else { return };
let (repo, worktree) = clean_subvolume_worktree(root.as_path());
let removed: bool = remove_subvolume_worktree(&git_context(), &RemovePlan { target: worktree.clone() });
assert_eq!(removed, removal_allowed(root.as_path()));
if removed {
assert!(!worktree.exists());
assert!(!repo.join(".git/worktrees/wt").exists(), "the registration is gone too");
let listing: String = String::from_utf8_lossy(&git(repo.as_path(), &["worktree", "list", "--porcelain"]).stdout).into_owned();
assert!(!listing.contains("wt"), "{listing}");
} else {
assert!(worktree.join("tracked.txt").is_file(), "a refusal changes nothing");
}
remove(root.as_path());
}
#[test]
fn btrfs_a_worktree_git_would_refuse_or_that_is_no_subvolume_is_left_alone() {
let Some(root) = btrfs_fixture("remove-refused") else { return };
let (repo, worktree) = clean_subvolume_worktree(root.as_path());
let plan: RemovePlan = RemovePlan { target: worktree.clone() };
std::fs::write(worktree.join("tracked.txt"), b"TRACKED\n").expect("modify");
assert!(!remove_subvolume_worktree(&git_context(), &plan), "a modified file");
std::fs::write(worktree.join("tracked.txt"), b"tracked\n").expect("restore the bytes");
age(worktree.join("tracked.txt").as_path());
git(worktree.as_path(), &["update-index", "--refresh"]);
std::fs::write(worktree.join("untracked.txt"), b"u").expect("untracked");
assert!(!remove_subvolume_worktree(&git_context(), &plan), "an untracked file");
std::fs::remove_file(worktree.join("untracked.txt")).expect("remove it");
git(repo.as_path(), &["worktree", "lock", "../wt"]);
assert!(!remove_subvolume_worktree(&git_context(), &plan), "a locked worktree");
git(repo.as_path(), &["worktree", "unlock", "../wt"]);
assert!(worktree.join("tracked.txt").is_file(), "nothing was removed");
let ordinary: PathBuf = root.join("plain");
git(repo.as_path(), &["worktree", "add", "--quiet", "-b", "plain", "../plain"]);
let canonical: PathBuf = std::fs::canonicalize(ordinary.as_path()).expect("canonical");
age(canonical.join("tracked.txt").as_path());
git(canonical.as_path(), &["update-index", "--refresh"]);
assert!(!remove_subvolume_worktree(&git_context(), &RemovePlan { target: canonical.clone() }), "an ordinary directory");
assert!(canonical.join("tracked.txt").is_file());
assert!(!remove_subvolume_worktree(&git_context(), &RemovePlan { target: repo.clone() }), "the main checkout");
remove(root.as_path());
}