Skip to main content

changeset_git/repository/
files.rs

1use std::path::Path;
2
3use crate::{GitError, Result};
4
5use super::Repository;
6
7impl Repository {
8    /// # Errors
9    ///
10    /// Returns [`GitError::FileDelete`] if the file cannot be deleted.
11    pub fn delete_file(&self, path: &Path) -> Result<()> {
12        let absolute_path = if path.is_absolute() {
13            path.to_path_buf()
14        } else {
15            self.root().join(path)
16        };
17
18        std::fs::remove_file(&absolute_path).map_err(|source| GitError::FileDelete {
19            path: absolute_path,
20            source,
21        })?;
22
23        let relative_path = self.to_relative_path(path);
24
25        let mut index = self.inner.index()?;
26        index.remove_path(&relative_path)?;
27        index.write()?;
28
29        Ok(())
30    }
31
32    /// # Errors
33    ///
34    /// Returns [`GitError::FileDelete`] if any file cannot be deleted.
35    pub fn delete_files(&self, paths: &[&Path]) -> Result<()> {
36        for path in paths {
37            self.delete_file(path)?;
38        }
39        Ok(())
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use super::super::tests::setup_test_repo;
46    use crate::GitError;
47    use std::fs;
48    use std::path::Path;
49
50    #[test]
51    fn delete_single_file() -> anyhow::Result<()> {
52        let (dir, repo) = setup_test_repo()?;
53
54        fs::write(dir.path().join("file.txt"), "content")?;
55        repo.stage_files(&[Path::new("file.txt")])?;
56
57        let sig = git2::Signature::now("Test", "test@example.com")?;
58        let mut index = repo.inner.index()?;
59        let tree_id = index.write_tree()?;
60        let tree = repo.inner.find_tree(tree_id)?;
61        let parent = repo.inner.head()?.peel_to_commit()?;
62        repo.inner
63            .commit(Some("HEAD"), &sig, &sig, "Add file", &tree, &[&parent])?;
64
65        repo.delete_file(Path::new("file.txt"))?;
66
67        assert!(!dir.path().join("file.txt").exists());
68
69        let index = repo.inner.index()?;
70        assert!(index.get_path(Path::new("file.txt"), 0).is_none());
71
72        Ok(())
73    }
74
75    #[test]
76    fn delete_multiple_files() -> anyhow::Result<()> {
77        let (dir, repo) = setup_test_repo()?;
78
79        fs::write(dir.path().join("file1.txt"), "content1")?;
80        fs::write(dir.path().join("file2.txt"), "content2")?;
81        repo.stage_files(&[Path::new("file1.txt"), Path::new("file2.txt")])?;
82
83        let sig = git2::Signature::now("Test", "test@example.com")?;
84        let mut index = repo.inner.index()?;
85        let tree_id = index.write_tree()?;
86        let tree = repo.inner.find_tree(tree_id)?;
87        let parent = repo.inner.head()?.peel_to_commit()?;
88        repo.inner
89            .commit(Some("HEAD"), &sig, &sig, "Add files", &tree, &[&parent])?;
90
91        repo.delete_files(&[Path::new("file1.txt"), Path::new("file2.txt")])?;
92
93        assert!(!dir.path().join("file1.txt").exists());
94        assert!(!dir.path().join("file2.txt").exists());
95
96        Ok(())
97    }
98
99    #[test]
100    fn delete_nonexistent_file_fails() -> anyhow::Result<()> {
101        let (_dir, repo) = setup_test_repo()?;
102
103        let result = repo.delete_file(Path::new("nonexistent.txt"));
104        assert!(matches!(result, Err(GitError::FileDelete { .. })));
105
106        Ok(())
107    }
108}