git_gardener/commands/
init.rs1use crate::config::Config;
2use crate::error::{GitGardenerError, Result};
3use crate::git::GitWorktree;
4use std::fs;
5use std::path::Path;
6
7pub struct InitCommand {
8 pub force: bool,
9}
10
11impl InitCommand {
12 pub fn new(force: bool) -> Self {
13 Self { force }
14 }
15
16 pub fn execute(&self) -> Result<()> {
17 let git_worktree = GitWorktree::new()?;
18 let repo_root = git_worktree.get_repository_root()?;
19
20 let config_path = repo_root.join(".gardener.yml");
21 let gardener_dir = repo_root.join(".gardener");
22 let gitignore_path = repo_root.join(".gitignore");
23
24 if config_path.exists() && !self.force {
26 return Err(GitGardenerError::Custom(
27 "git-gardener is already initialized. Use --force to reinitialize.".to_string()
28 ));
29 }
30
31 if !gardener_dir.exists() {
33 fs::create_dir_all(&gardener_dir)?;
34 println!("✓ Created .gardener directory");
35 }
36
37 self.update_gitignore(&gitignore_path)?;
39
40 let config = Config::default();
42 config.save_to_file(&config_path)?;
43 println!("✓ Created .gardener.yml configuration file");
44
45 println!("git-gardener initialized successfully!");
46 Ok(())
47 }
48
49 fn update_gitignore(&self, gitignore_path: &Path) -> Result<()> {
50 let gardener_entry = ".gardener/";
51
52 if gitignore_path.exists() {
54 let content = fs::read_to_string(gitignore_path)?;
55
56 if content.lines().any(|line| line.trim() == gardener_entry) {
58 return Ok(());
59 }
60
61 let new_content = if content.ends_with('\n') {
63 format!("{}{}\n", content, gardener_entry)
64 } else {
65 format!("{}\n{}\n", content, gardener_entry)
66 };
67
68 fs::write(gitignore_path, new_content)?;
69 } else {
70 fs::write(gitignore_path, format!("{}\n", gardener_entry))?;
72 }
73
74 println!("✓ Updated .gitignore");
75 Ok(())
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use tempfile::tempdir;
83 use std::fs;
84 use std::process::Command;
85
86 fn setup_git_repo() -> tempfile::TempDir {
87 let temp_dir = tempdir().unwrap();
88 let repo_path = temp_dir.path();
89
90 Command::new("git")
92 .args(&["init"])
93 .current_dir(repo_path)
94 .output()
95 .expect("Failed to init git repo");
96
97 Command::new("git")
99 .args(&["config", "user.name", "Test User"])
100 .current_dir(repo_path)
101 .output()
102 .unwrap();
103
104 Command::new("git")
105 .args(&["config", "user.email", "test@example.com"])
106 .current_dir(repo_path)
107 .output()
108 .unwrap();
109
110 fs::write(repo_path.join("README.md"), "# Test Repo").unwrap();
112
113 Command::new("git")
114 .args(&["add", "."])
115 .current_dir(repo_path)
116 .output()
117 .unwrap();
118
119 Command::new("git")
120 .args(&["commit", "-m", "Initial commit"])
121 .current_dir(repo_path)
122 .output()
123 .unwrap();
124
125 temp_dir
126 }
127
128 #[test]
129 fn test_init_command_new_creates_instance() {
130 let cmd = InitCommand::new(false);
132 assert_eq!(cmd.force, false);
133
134 let cmd = InitCommand::new(true);
135 assert_eq!(cmd.force, true);
136 }
137
138 #[test]
139 fn test_init_command_fails_without_git_repo() {
140 let temp_dir = tempdir().unwrap();
142 std::env::set_current_dir(temp_dir.path()).unwrap();
143
144 let cmd = InitCommand::new(false);
145 let result = cmd.execute();
146
147 assert!(result.is_err());
148 assert!(matches!(result.unwrap_err(), GitGardenerError::NotInRepository));
149 }
150
151 #[test]
152 fn test_init_command_creates_gardener_directory() {
153 let temp_dir = setup_git_repo();
155 std::env::set_current_dir(temp_dir.path()).unwrap();
156
157 let cmd = InitCommand::new(false);
158 let result = cmd.execute();
159
160 assert!(result.is_ok());
161
162 let gardener_dir = temp_dir.path().join(".gardener");
164 assert!(gardener_dir.exists());
165 assert!(gardener_dir.is_dir());
166 }
167
168 #[test]
169 fn test_init_command_creates_config_file() {
170 let temp_dir = setup_git_repo();
172 std::env::set_current_dir(temp_dir.path()).unwrap();
173
174 let cmd = InitCommand::new(false);
175 let result = cmd.execute();
176
177 assert!(result.is_ok());
178
179 let config_path = temp_dir.path().join(".gardener.yml");
181 assert!(config_path.exists());
182
183 let config = Config::load_from_file(&config_path).unwrap();
185 assert_eq!(config.version, "1.0");
186 }
187
188 #[test]
189 fn test_init_command_updates_gitignore() {
190 let temp_dir = setup_git_repo();
192 std::env::set_current_dir(temp_dir.path()).unwrap();
193
194 let cmd = InitCommand::new(false);
195 let result = cmd.execute();
196
197 assert!(result.is_ok());
198
199 let gitignore_path = temp_dir.path().join(".gitignore");
201 assert!(gitignore_path.exists());
202
203 let content = fs::read_to_string(gitignore_path).unwrap();
204 assert!(content.contains(".gardener/"));
205 }
206
207 #[test]
208 fn test_init_command_updates_existing_gitignore() {
209 let temp_dir = setup_git_repo();
211 std::env::set_current_dir(temp_dir.path()).unwrap();
212
213 let gitignore_path = temp_dir.path().join(".gitignore");
215 fs::write(&gitignore_path, "*.log\n/target/\n").unwrap();
216
217 let cmd = InitCommand::new(false);
218 let result = cmd.execute();
219
220 assert!(result.is_ok());
221
222 let content = fs::read_to_string(gitignore_path).unwrap();
223 assert!(content.contains("*.log"));
224 assert!(content.contains("/target/"));
225 assert!(content.contains(".gardener/"));
226 }
227
228 #[test]
229 fn test_init_command_fails_when_already_initialized() {
230 let temp_dir = setup_git_repo();
232 std::env::set_current_dir(temp_dir.path()).unwrap();
233
234 let cmd = InitCommand::new(false);
236 let result = cmd.execute();
237 assert!(result.is_ok());
238
239 let cmd = InitCommand::new(false);
241 let result = cmd.execute();
242
243 assert!(result.is_err());
244 assert!(matches!(result.unwrap_err(), GitGardenerError::Custom(_)));
245 }
246
247 #[test]
248 fn test_init_command_force_reinitializes() {
249 let temp_dir = setup_git_repo();
251 std::env::set_current_dir(temp_dir.path()).unwrap();
252
253 let cmd = InitCommand::new(false);
255 let result = cmd.execute();
256 assert!(result.is_ok());
257
258 let cmd = InitCommand::new(true);
260 let result = cmd.execute();
261
262 assert!(result.is_ok());
263 }
264}