167 lines · 5.9 KB · Rust
RawDownload
use std::fs; #[test]fn removes_both_roots_and_does_not_follow_symlinks() -> crate::Result {    let tmp = gix_testtools::tempfile::tempdir()?;    let worktrees_dir = tmp.path().join("repo.git/worktrees");    let git_dir = worktrees_dir.join("linked");    let work_dir = tmp.path().join("linked");    let outside = tmp.path().join("outside");    fs::create_dir_all(work_dir.join("deep/nested"))?;    fs::create_dir_all(&git_dir)?;    fs::create_dir(&outside)?;    fs::write(work_dir.join("deep/nested/file"), b"content")?;    fs::write(git_dir.join("HEAD"), b"ref: refs/heads/topic\n")?;    fs::write(outside.join("keep"), b"outside")?;     let link = work_dir.join("link");    if let Err(err) = gix_fs::symlink::create(&outside, &link) {        #[cfg(windows)]        if err.kind() == std::io::ErrorKind::PermissionDenied {            return Ok(());        }        return Err(err.into());    }     gix_worktree::remove::remove(&work_dir, &git_dir, gix_features::progress::Discard)?;     assert!(!work_dir.exists(), "the checkout was removed");    assert!(!git_dir.exists(), "the private Git directory was removed");    assert!(!worktrees_dir.exists(), "an empty worktrees directory was removed");    assert_eq!(        fs::read(outside.join("keep"))?,        b"outside",        "directory symlinks are not followed"    );    Ok(())} #[test]#[cfg(unix)]fn symlink_roots_with_or_without_a_trailing_separator_are_unlinked() -> crate::Result {    for trailing_separator in [false, true] {        let tmp = gix_testtools::tempfile::tempdir()?;        let work_dir = tmp.path().join("linked");        let git_dir = tmp.path().join("repo.git/worktrees/linked");        let outside = tmp.path().join("outside");        fs::create_dir_all(&git_dir)?;        fs::create_dir(&outside)?;        fs::write(outside.join("keep"), b"outside")?;        gix_fs::symlink::create(&outside, &work_dir)?;        let root = if trailing_separator {            work_dir.join("")        } else {            work_dir.clone()        };         gix_worktree::remove::remove(&root, &git_dir, gix_features::progress::Discard)?;         assert!(!work_dir.exists(), "the symlink itself was removed");        assert!(!git_dir.exists(), "the private Git directory was removed");        assert_eq!(            fs::read(outside.join("keep"))?,            b"outside",            "a root symlink is not followed, even with a trailing separator"        );    }    Ok(())} #[test]fn missing_roots_are_already_removed() -> crate::Result {    let tmp = gix_testtools::tempfile::tempdir()?;    gix_worktree::remove::remove(        tmp.path().join("missing-worktree"),        tmp.path().join("repo.git/worktrees/missing"),        gix_features::progress::Discard,    )?;    let non_directory = tmp.path().join("file");    fs::write(&non_directory, b"not a directory")?;    gix_worktree::remove::remove(        non_directory.join("missing-worktree"),        tmp.path().join("repo.git/worktrees/missing"),        gix_features::progress::Discard,    )?;    Ok(())} #[test]fn only_the_conventional_empty_worktrees_parent_is_removed() -> crate::Result {    let tmp = gix_testtools::tempfile::tempdir()?;    let parent = tmp.path().join("custom-parent");    let git_dir = parent.join("linked");    fs::create_dir_all(&git_dir)?;     gix_worktree::remove::remove(        tmp.path().join("missing-worktree"),        &git_dir,        gix_features::progress::Discard,    )?;     assert!(parent.exists(), "an arbitrary parent directory is retained");    Ok(())} #[test]#[cfg(unix)]fn administrative_data_is_removed_after_checkout_removal_fails() -> crate::Result {    use std::os::unix::fs::PermissionsExt;     let tmp = gix_testtools::tempfile::tempdir()?;    let work_dir = tmp.path().join("linked");    let git_dir = tmp.path().join("repo.git/worktrees/linked");    fs::create_dir(&work_dir)?;    fs::write(work_dir.join("protected"), b"content")?;    fs::set_permissions(&work_dir, fs::Permissions::from_mode(0o500))?;    // Root can bypass these permissions, so first check that the fixture can fail.    match fs::remove_file(work_dir.join("protected")) {        Ok(()) => {            fs::set_permissions(&work_dir, fs::Permissions::from_mode(0o700))?;            return Ok(());        }        Err(err) if err.kind() == std::io::ErrorKind::PermissionDenied => {}        Err(err) => {            fs::set_permissions(&work_dir, fs::Permissions::from_mode(0o700))?;            return Err(err.into());        }    }    fs::create_dir_all(&git_dir)?;    fs::write(git_dir.join("HEAD"), b"ref: refs/heads/topic\n")?;     let result = gix_worktree::remove::remove(&work_dir, &git_dir, gix_features::progress::Discard);    if work_dir.exists() {        fs::set_permissions(&work_dir, fs::Permissions::from_mode(0o700))?;    }    let err = result.expect_err("a protected checkout cannot be removed");     assert!(        matches!(err, gix_worktree::remove::Error::Worktree(_)),        "only the checkout failed"    );    assert!(        !git_dir.exists(),        "administrative data is removed despite checkout failure"    );    Ok(())} #[test]#[cfg(windows)]fn readonly_files_do_not_prevent_removal() -> crate::Result {    let tmp = gix_testtools::tempfile::tempdir()?;    let work_dir = tmp.path().join("linked");    let git_dir = tmp.path().join("repo.git/worktrees/linked");    fs::create_dir(&work_dir)?;    fs::create_dir_all(&git_dir)?;    let readonly = work_dir.join("readonly");    fs::write(&readonly, b"content")?;    let mut permissions = fs::metadata(&readonly)?.permissions();    permissions.set_readonly(true);    fs::set_permissions(&readonly, permissions)?;     gix_worktree::remove::remove(&work_dir, &git_dir, gix_features::progress::Discard)?;     assert!(!work_dir.exists(), "the checkout was removed");    assert!(!git_dir.exists(), "the private Git directory was removed");    Ok(())}