merge_operations/
merge_operations.rs1use rustic_git::{FastForwardMode, MergeOptions, MergeStatus, Repository, Result};
2use std::{env, fs};
3
4fn main() -> Result<()> {
15 println!("=== Merge Operations Demo ===\n");
16
17 let temp_dir = env::temp_dir().join("rustic_git_merge_demo");
19
20 if temp_dir.exists() {
22 fs::remove_dir_all(&temp_dir)?;
23 }
24
25 println!("Working in temporary directory: {:?}\n", temp_dir);
26
27 let repo = Repository::init(&temp_dir, false)?;
29
30 repo.config().set_user("Example User", "example@test.com")?;
32
33 demonstrate_fast_forward_merge(&repo, &temp_dir)?;
34 demonstrate_no_fast_forward_merge(&repo, &temp_dir)?;
35 demonstrate_merge_conflicts(&repo, &temp_dir)?;
36 demonstrate_merge_status_and_abort(&repo, &temp_dir)?;
37
38 println!("\n=== Merge Operations Demo Complete ===");
39
40 fs::remove_dir_all(&temp_dir)?;
42 Ok(())
43}
44
45fn demonstrate_fast_forward_merge(repo: &Repository, temp_dir: &std::path::Path) -> Result<()> {
46 println!("--- Demonstrating Fast-Forward Merge ---\n");
47
48 println!("1. Creating initial commit on master...");
50 let file1_path = temp_dir.join("README.md");
51 fs::write(&file1_path, "# Project\n\nInitial content")?;
52 repo.add(&["README.md"])?;
53 let initial_commit = repo.commit("Initial commit")?;
54 println!(" Created commit: {}", initial_commit);
55
56 println!("\n2. Creating feature branch and adding commits...");
58 repo.checkout_new("feature/fast-forward", None)?;
59
60 let file2_path = temp_dir.join("feature.txt");
61 fs::write(&file2_path, "New feature implementation")?;
62 repo.add(&["feature.txt"])?;
63 let feature_commit = repo.commit("Add new feature")?;
64 println!(" Feature commit: {}", feature_commit);
65
66 println!("\n3. Switching back to master...");
68 let branches = repo.branches()?;
69 let master_branch = branches.find("master").unwrap();
70 repo.checkout(master_branch)?;
71 println!(" Switched to master");
72
73 println!("\n4. Performing fast-forward merge...");
75 let merge_status = repo.merge("feature/fast-forward")?;
76
77 match merge_status {
78 MergeStatus::FastForward(hash) => {
79 println!(" ✓ Fast-forward merge completed!");
80 println!(" New HEAD: {}", hash);
81 println!(" Both files are now present on master");
82 }
83 _ => println!(" Unexpected merge result: {:?}", merge_status),
84 }
85
86 println!(" Files in repository:");
87 for file in ["README.md", "feature.txt"] {
88 if temp_dir.join(file).exists() {
89 println!(" ✓ {}", file);
90 }
91 }
92
93 Ok(())
94}
95
96fn demonstrate_no_fast_forward_merge(repo: &Repository, temp_dir: &std::path::Path) -> Result<()> {
97 println!("\n--- Demonstrating No-Fast-Forward Merge ---\n");
98
99 println!("1. Adding commit to master...");
101 let readme_path = temp_dir.join("README.md");
102 fs::write(
103 &readme_path,
104 "# Project\n\nInitial content\n\n## Updates\nAdded documentation",
105 )?;
106 repo.add(&["README.md"])?;
107 let master_commit = repo.commit("Update documentation")?;
108 println!(" Master commit: {}", master_commit);
109
110 println!("\n2. Creating another feature branch...");
112 repo.checkout_new("feature/no-ff", None)?;
113
114 let config_path = temp_dir.join("config.yaml");
115 fs::write(&config_path, "app:\n name: example\n version: 1.0")?;
116 repo.add(&["config.yaml"])?;
117 let config_commit = repo.commit("Add configuration file")?;
118 println!(" Config commit: {}", config_commit);
119
120 println!("\n3. Switching back to master...");
122 let branches = repo.branches()?;
123 let master_branch = branches.find("master").unwrap();
124 repo.checkout(master_branch)?;
125
126 println!("\n4. Performing no-fast-forward merge...");
128 let options = MergeOptions::new()
129 .with_fast_forward(FastForwardMode::Never)
130 .with_message("Merge feature/no-ff into master".to_string());
131
132 let merge_status = repo.merge_with_options("feature/no-ff", options)?;
133
134 match merge_status {
135 MergeStatus::Success(hash) => {
136 println!(" ✓ Merge commit created!");
137 println!(" Merge commit: {}", hash);
138 println!(" Created explicit merge commit preserving branch history");
139 }
140 _ => println!(" Unexpected merge result: {:?}", merge_status),
141 }
142
143 println!("\n5. Recent commit history:");
145 let commits = repo.recent_commits(3)?;
146 for (i, commit) in commits.iter().enumerate() {
147 println!(
148 " {}: {} - {}",
149 i + 1,
150 commit.hash.short(),
151 commit.message.subject
152 );
153 }
154
155 Ok(())
156}
157
158fn demonstrate_merge_conflicts(repo: &Repository, temp_dir: &std::path::Path) -> Result<()> {
159 println!("\n--- Demonstrating Merge Conflicts ---\n");
160
161 println!("1. Creating branch with conflicting changes...");
163 repo.checkout_new("feature/conflict", None)?;
164
165 let readme_path = temp_dir.join("README.md");
167 fs::write(
168 &readme_path,
169 "# Project\n\nFeature branch changes\n\n## Updates\nAdded documentation",
170 )?;
171 repo.add(&["README.md"])?;
172 let feature_commit = repo.commit("Update README from feature branch")?;
173 println!(" Feature commit: {}", feature_commit);
174
175 println!("\n2. Making conflicting change on master...");
177 let branches = repo.branches()?;
178 let master_branch = branches.find("master").unwrap();
179 repo.checkout(master_branch)?;
180
181 fs::write(
182 &readme_path,
183 "# Project\n\nMaster branch changes\n\n## Updates\nAdded documentation",
184 )?;
185 repo.add(&["README.md"])?;
186 let master_conflict_commit = repo.commit("Update README from master")?;
187 println!(" Master commit: {}", master_conflict_commit);
188
189 println!("\n3. Attempting merge (will have conflicts)...");
191 let merge_status = repo.merge("feature/conflict")?;
192
193 match merge_status {
194 MergeStatus::Conflicts(files) => {
195 println!(" ⚠️ Merge conflicts detected!");
196 println!(" Conflicted files:");
197 for file in &files {
198 println!(" - {}", file.display());
199 }
200
201 if repo.merge_in_progress()? {
203 println!(" ✓ Merge in progress status detected");
204 }
205
206 println!("\n4. Conflict markers in README.md:");
208 let content = fs::read_to_string(&readme_path)?;
209 for (i, line) in content.lines().enumerate() {
210 if line.starts_with("<<<<<<< ")
211 || line.starts_with("======= ")
212 || line.starts_with(">>>>>>> ")
213 {
214 println!(" {}: {} <-- conflict marker", i + 1, line);
215 } else {
216 println!(" {}: {}", i + 1, line);
217 }
218 }
219
220 println!("\n5. Aborting merge...");
222 repo.abort_merge()?;
223 println!(" ✓ Merge aborted successfully");
224
225 if !repo.merge_in_progress()? {
227 println!(" ✓ Repository is back to clean state");
228 }
229 }
230 _ => println!(" Unexpected merge result: {:?}", merge_status),
231 }
232
233 Ok(())
234}
235
236fn demonstrate_merge_status_and_abort(repo: &Repository, temp_dir: &std::path::Path) -> Result<()> {
237 println!("\n--- Demonstrating Merge Status and Options ---\n");
238
239 println!("1. Creating simple feature branch...");
241 repo.checkout_new("feature/simple", None)?;
242
243 let simple_path = temp_dir.join("simple.txt");
244 fs::write(&simple_path, "Simple feature content")?;
245 repo.add(&["simple.txt"])?;
246 repo.commit("Add simple feature")?;
247
248 let branches = repo.branches()?;
250 let master_branch = branches.find("master").unwrap();
251 repo.checkout(master_branch)?;
252
253 println!("\n2. Testing merge with custom options...");
255 let options = MergeOptions::new()
256 .with_fast_forward(FastForwardMode::Auto)
257 .with_message("Integrate simple feature".to_string());
258
259 let merge_status = repo.merge_with_options("feature/simple", options)?;
260
261 match merge_status {
262 MergeStatus::FastForward(hash) => {
263 println!(" ✓ Fast-forward merge completed: {}", hash);
264 }
265 MergeStatus::Success(hash) => {
266 println!(" ✓ Merge commit created: {}", hash);
267 }
268 MergeStatus::UpToDate => {
269 println!(" ✓ Already up to date");
270 }
271 MergeStatus::Conflicts(_) => {
272 println!(" ⚠️ Unexpected conflicts");
273 }
274 }
275
276 println!("\n3. Final repository state:");
278 let status = repo.status()?;
279 println!(
280 " Working directory clean: {}",
281 status.staged_files().count() == 0 && status.unstaged_files().count() == 0
282 );
283
284 let commits = repo.recent_commits(5)?;
285 println!(" Recent commits:");
286 for (i, commit) in commits.iter().enumerate() {
287 println!(
288 " {}: {} - {}",
289 i + 1,
290 commit.hash.short(),
291 commit.message.subject
292 );
293 }
294
295 Ok(())
296}