rustic_git/commands/
add.rs

1use std::path::Path;
2
3use crate::utils::git;
4use crate::{Repository, Result};
5
6impl Repository {
7    /// Add specific files or paths to the staging area.
8    ///
9    /// # Arguments
10    ///
11    /// * `paths` - The file paths to add to the staging area
12    ///
13    /// # Returns
14    ///
15    /// A `Result` indicating success or a `GitError` if the operation fails.
16    pub fn add<P: AsRef<Path>>(&self, paths: &[P]) -> Result<()> {
17        Self::ensure_git()?;
18
19        if paths.is_empty() {
20            return Ok(());
21        }
22
23        let mut args = vec!["add"];
24        let path_strings: Vec<String> = paths
25            .iter()
26            .map(|p| p.as_ref().to_string_lossy().to_string())
27            .collect();
28
29        for path_str in &path_strings {
30            args.push(path_str);
31        }
32
33        let _stdout = git(&args, Some(self.repo_path()))?;
34        Ok(())
35    }
36
37    /// Add all changes to the staging area (equivalent to `git add .`).
38    ///
39    /// # Returns
40    ///
41    /// A `Result` indicating success or a `GitError` if the operation fails.
42    pub fn add_all(&self) -> Result<()> {
43        Self::ensure_git()?;
44        let _stdout = git(&["add", "."], Some(self.repo_path()))?;
45        Ok(())
46    }
47
48    /// Add all tracked files that have been modified (equivalent to `git add -u`).
49    ///
50    /// # Returns
51    ///
52    /// A `Result` indicating success or a `GitError` if the operation fails.
53    pub fn add_update(&self) -> Result<()> {
54        Self::ensure_git()?;
55        let _stdout = git(&["add", "-u"], Some(self.repo_path()))?;
56        Ok(())
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63    use std::env;
64    use std::fs;
65    use std::path::{Path, PathBuf};
66
67    fn create_test_repo(path: &PathBuf) -> Repository {
68        // Clean up if exists
69        if path.exists() {
70            fs::remove_dir_all(path).unwrap();
71        }
72
73        Repository::init(path, false).unwrap()
74    }
75
76    fn create_test_file(repo_path: &Path, filename: &str, content: &str) {
77        let file_path = repo_path.join(filename);
78        fs::write(file_path, content).unwrap();
79    }
80
81    #[test]
82    fn test_add_specific_files() {
83        let test_path = env::temp_dir().join("test_add_repo");
84        let repo = create_test_repo(&test_path);
85
86        // Create some test files
87        create_test_file(&test_path, "file1.txt", "content 1");
88        create_test_file(&test_path, "file2.txt", "content 2");
89
90        // Add specific files
91        let result = repo.add(&["file1.txt"]);
92        assert!(result.is_ok());
93
94        // Verify file1.txt is staged by checking status
95        let status = repo.status().unwrap();
96        let added_files: Vec<_> = status
97            .staged_files()
98            .map(|entry| entry.path.to_str().unwrap())
99            .collect();
100
101        assert!(added_files.contains(&"file1.txt"));
102
103        // Clean up
104        fs::remove_dir_all(&test_path).unwrap();
105    }
106
107    #[test]
108    fn test_add_multiple_files() {
109        let test_path = env::temp_dir().join("test_add_multiple_repo");
110        let repo = create_test_repo(&test_path);
111
112        // Create test files
113        create_test_file(&test_path, "file1.txt", "content 1");
114        create_test_file(&test_path, "file2.txt", "content 2");
115        create_test_file(&test_path, "file3.txt", "content 3");
116
117        // Add multiple files
118        let result = repo.add(&["file1.txt", "file2.txt"]);
119        assert!(result.is_ok());
120
121        // Verify files are staged
122        let status = repo.status().unwrap();
123        let added_files: Vec<_> = status
124            .staged_files()
125            .map(|entry| entry.path.to_str().unwrap())
126            .collect();
127
128        assert!(added_files.contains(&"file1.txt"));
129        assert!(added_files.contains(&"file2.txt"));
130        assert_eq!(added_files.len(), 2);
131
132        // Clean up
133        fs::remove_dir_all(&test_path).unwrap();
134    }
135
136    #[test]
137    fn test_add_all() {
138        let test_path = env::temp_dir().join("test_add_all_repo");
139        let repo = create_test_repo(&test_path);
140
141        // Create test files
142        create_test_file(&test_path, "file1.txt", "content 1");
143        create_test_file(&test_path, "file2.txt", "content 2");
144        fs::create_dir(test_path.join("subdir")).unwrap();
145        create_test_file(&test_path, "subdir/file3.txt", "content 3");
146
147        // Add all files
148        let result = repo.add_all();
149        assert!(result.is_ok());
150
151        // Verify all files are staged
152        let status = repo.status().unwrap();
153        let added_files: Vec<_> = status
154            .staged_files()
155            .map(|entry| entry.path.to_str().unwrap())
156            .collect();
157
158        assert!(added_files.contains(&"file1.txt"));
159        assert!(added_files.contains(&"file2.txt"));
160        assert!(added_files.contains(&"subdir/file3.txt"));
161
162        // Clean up
163        fs::remove_dir_all(&test_path).unwrap();
164    }
165
166    #[test]
167    fn test_add_empty_paths() {
168        let test_path = env::temp_dir().join("test_add_empty_repo");
169        let repo = create_test_repo(&test_path);
170
171        // Adding empty paths should succeed without error
172        let result = repo.add::<&str>(&[]);
173        assert!(result.is_ok());
174
175        // Clean up
176        fs::remove_dir_all(&test_path).unwrap();
177    }
178
179    #[test]
180    fn test_add_nonexistent_file() {
181        let test_path = env::temp_dir().join("test_add_nonexistent_repo");
182        let repo = create_test_repo(&test_path);
183
184        // Adding non-existent file should fail
185        let result = repo.add(&["nonexistent.txt"]);
186        assert!(result.is_err());
187
188        // Clean up
189        fs::remove_dir_all(&test_path).unwrap();
190    }
191}