commit_workflows/
commit_workflows.rs

1//! Commit Workflows Example
2//!
3//! This example demonstrates commit operations and Hash type usage:
4//! - Basic commits with commit()
5//! - Commits with custom authors using commit_with_author()
6//! - Hash type methods (full hash, short hash, display)
7//! - Error handling for empty commits
8//! - Complete git workflows from init to commit
9//!
10//! Run with: cargo run --example commit_workflows
11
12use rustic_git::{Hash, Repository, Result};
13use std::{env, fs};
14
15fn main() -> Result<()> {
16    println!("Rustic Git - Commit Workflows Example\n");
17
18    let repo_path = env::temp_dir().join("rustic_git_commit_example");
19
20    // Clean up any previous run
21    if repo_path.exists() {
22        fs::remove_dir_all(&repo_path).expect("Failed to clean up previous example");
23    }
24
25    // Initialize repository
26    println!("Setting up repository for commit demonstrations...");
27    let repo = Repository::init(&repo_path, false)?;
28    println!("Repository initialized\n");
29
30    println!("=== Basic Commit Operations ===\n");
31
32    // Create initial files
33    println!("Creating initial project files...");
34    fs::create_dir_all(repo_path.join("src"))?;
35
36    fs::write(
37        repo_path.join("README.md"),
38        "# Commit Demo Project\n\nThis project demonstrates commit workflows with rustic-git.\n",
39    )?;
40
41    fs::write(
42        repo_path.join("src/main.rs"),
43        r#"fn main() {
44    println!("Hello, Commit Demo!");
45}
46"#,
47    )?;
48
49    fs::write(
50        repo_path.join("Cargo.toml"),
51        r#"[package]
52name = "commit-demo"
53version = "0.1.0"
54edition = "2021"
55"#,
56    )?;
57
58    println!("Created README.md, src/main.rs, and Cargo.toml");
59
60    // Stage and commit with basic commit()
61    println!("\nStaging files for first commit...");
62    repo.add_all()?;
63
64    println!("Creating first commit with basic commit() method:");
65    let first_hash = repo.commit("Initial commit: Add project structure")?;
66
67    println!("First commit created!");
68    display_hash_info(&first_hash, "First commit");
69    println!();
70
71    println!("=== Hash Type Demonstrations ===\n");
72
73    println!("Hash type methods and usage:");
74
75    // Demonstrate different ways to work with Hash
76    let hash_as_string: String = first_hash.to_string();
77    let hash_as_str: &str = first_hash.as_str();
78    let short_hash: &str = first_hash.short();
79
80    println!("   Hash conversions:");
81    println!("      as_str(): '{}'", hash_as_str);
82    println!("      short(): '{}'", short_hash);
83    println!("      to_string(): '{}'", hash_as_string);
84    println!("      Display: '{}'", first_hash);
85
86    // Demonstrate Hash equality and cloning
87    let cloned_hash = first_hash.clone();
88    println!("\n   Hash operations:");
89    println!("      Original == Clone: {}", first_hash == cloned_hash);
90    println!(
91        "      Hash length: {} characters",
92        first_hash.as_str().len()
93    );
94    println!(
95        "      Short hash length: {} characters",
96        first_hash.short().len()
97    );
98
99    // Create Hash from different sources for demonstration
100    let hash_from_string: Hash = "1234567890abcdef".to_string().into();
101    let hash_from_str: Hash = "fedcba0987654321".into();
102
103    println!("      Hash from String: {}", hash_from_string.short());
104    println!("      Hash from &str: {}", hash_from_str.short());
105    println!();
106
107    println!("=== Commits with Custom Authors ===\n");
108
109    // Create more files to commit with custom author
110    println!("Adding features for custom author commit...");
111    fs::create_dir_all(repo_path.join("tests"))?;
112
113    fs::write(
114        repo_path.join("src/lib.rs"),
115        r#"//! Commit demo library
116
117pub fn greet(name: &str) -> String {
118    format!("Hello, {}! This is a commit demo.", name)
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    fn test_greet() {
127        assert_eq!(greet("Alice"), "Hello, Alice! This is a commit demo.");
128    }
129}
130"#,
131    )?;
132
133    fs::write(
134        repo_path.join("tests/integration_test.rs"),
135        r#"use commit_demo::greet;
136
137#[test]
138fn test_integration() {
139    let result = greet("Integration");
140    assert!(result.contains("Integration"));
141    assert!(result.contains("commit demo"));
142}
143"#,
144    )?;
145
146    println!("Created src/lib.rs and tests/integration_test.rs");
147
148    // Stage and commit with custom author
149    repo.add_all()?;
150    println!("\nCreating commit with custom author:");
151    let second_hash = repo.commit_with_author(
152        "Add library code and tests\n\n- Implement greet function with proper documentation\n- Add unit tests and integration tests\n- Prepare for version 0.2.0 release",
153        "Jane Developer <jane.dev@example.com>"
154    )?;
155
156    println!("Commit with custom author created!");
157    display_hash_info(&second_hash, "Second commit (custom author)");
158    println!();
159
160    println!("=== Multiple Commit Workflow ===\n");
161
162    // Demonstrate a series of commits
163    let mut commit_hashes = vec![first_hash, second_hash];
164
165    // Commit 3: Update version
166    println!("Step 1: Update version information...");
167    fs::write(
168        repo_path.join("Cargo.toml"),
169        r#"[package]
170name = "commit-demo"
171version = "0.2.0"
172edition = "2021"
173description = "A demo project for commit workflows"
174"#,
175    )?;
176
177    repo.add(&["Cargo.toml"])?;
178    let third_hash = repo.commit("Bump version to 0.2.0 and add description")?;
179    commit_hashes.push(third_hash);
180
181    // Commit 4: Add documentation
182    println!("Step 2: Add documentation...");
183    fs::write(
184        repo_path.join("CHANGELOG.md"),
185        r#"# Changelog
186
187## [0.2.0] - 2024-01-01
188
189### Added
190- Library functionality with greet function
191- Comprehensive test suite
192- Project documentation
193
194## [0.1.0] - 2024-01-01
195
196### Added
197- Initial project structure
198- Basic Cargo configuration
199"#,
200    )?;
201
202    repo.add(&["CHANGELOG.md"])?;
203    let fourth_hash = repo.commit_with_author(
204        "docs: Add CHANGELOG with version history",
205        "Doc Writer <docs@example.com>",
206    )?;
207    commit_hashes.push(fourth_hash);
208
209    // Commit 5: Final polish
210    println!("Step 3: Final polish...");
211    fs::write(
212        repo_path.join("README.md"),
213        r#"# Commit Demo Project
214
215This project demonstrates commit workflows with rustic-git.
216
217## Features
218
219- Clean, type-safe Git operations
220- Comprehensive commit history
221- Multiple author support
222- Hash management utilities
223
224## Usage
225
226```rust
227use commit_demo::greet;
228
229fn main() {
230    println!("{}", greet("World"));
231}
232```
233
234## Version
235
236Current version: 0.2.0
237
238See CHANGELOG.md for version history.
239"#,
240    )?;
241
242    repo.add(&["README.md"])?;
243    let fifth_hash = repo.commit("docs: Enhance README with usage examples and features")?;
244    commit_hashes.push(fifth_hash);
245
246    println!("\nComplete commit history created!");
247
248    // Display all commits
249    println!("\n=== Commit History Summary ===\n");
250
251    for (i, hash) in commit_hashes.iter().enumerate() {
252        println!("{}. Commit {}", i + 1, i + 1);
253        display_hash_info(hash, &format!("Commit {}", i + 1));
254        println!();
255    }
256
257    // Compare hashes
258    println!("Hash comparisons:");
259    println!(
260        "   First commit == Last commit: {}",
261        commit_hashes[0] == commit_hashes[4]
262    );
263    println!("   All hashes unique: {}", all_unique(&commit_hashes));
264
265    // Show short hashes for all commits
266    println!("\nAll commit short hashes:");
267    for (i, hash) in commit_hashes.iter().enumerate() {
268        println!("   {}: {}", i + 1, hash.short());
269    }
270    println!();
271
272    println!("=== Error Handling for Commits ===\n");
273
274    // Try to commit with nothing staged (should fail)
275    println!("Testing commit with no staged changes:");
276    match repo.commit("This should fail - no changes") {
277        Ok(_hash) => println!("   Unexpectedly succeeded with empty commit"),
278        Err(e) => {
279            println!("   Expected error for empty commit: {:?}", e);
280            println!("   This is normal behavior - Git requires changes to commit");
281        }
282    }
283
284    // Try commit with custom author but no changes (should also fail)
285    println!("\nTesting custom author commit with no changes:");
286    match repo.commit_with_author("This should also fail", "Test Author <test@example.com>") {
287        Ok(_hash) => println!("   Unexpectedly succeeded with empty custom author commit"),
288        Err(e) => {
289            println!(
290                "   Expected error for empty commit with custom author: {:?}",
291                e
292            );
293        }
294    }
295
296    // Test commit with empty message (Git might handle this differently)
297    println!("\nTesting commit with empty message:");
298
299    // Create a change to commit
300    fs::write(repo_path.join("temp_for_empty_message.txt"), "temp content")?;
301    repo.add(&["temp_for_empty_message.txt"])?;
302
303    match repo.commit("") {
304        Ok(hash) => {
305            println!("   Commit with empty message succeeded: {}", hash.short());
306            println!("   Some Git configurations allow empty commit messages");
307        }
308        Err(e) => {
309            println!("   Empty commit message rejected: {:?}", e);
310        }
311    }
312
313    println!();
314
315    println!("=== Final Repository State ===\n");
316
317    let final_status = repo.status()?;
318    if final_status.is_clean() {
319        println!("Repository is clean - all changes committed!");
320    } else {
321        println!(
322            "Repository has {} uncommitted changes",
323            final_status.entries.len()
324        );
325    }
326
327    println!("\nWorkflow summary:");
328    println!("   Total commits created: {}", commit_hashes.len());
329    println!("   Hash examples demonstrated: [OK]");
330    println!("   Custom author commits: [OK]");
331    println!("   Error handling tested: [OK]");
332
333    // Clean up
334    println!("\nCleaning up example repository...");
335    fs::remove_dir_all(&repo_path)?;
336    println!("Commit workflows example completed!");
337
338    Ok(())
339}
340
341/// Display comprehensive information about a Hash
342fn display_hash_info(hash: &Hash, context: &str) {
343    println!("   {}:", context);
344    println!("      Full hash: {}", hash);
345    println!("      Short hash: {}", hash.short());
346    println!("      Hash length: {} chars", hash.as_str().len());
347
348    // Show first and last few characters for visual reference
349    let full = hash.as_str();
350    if full.len() >= 10 {
351        println!(
352            "      Pattern: {}...{}",
353            &full[..5],
354            &full[full.len() - 5..]
355        );
356    }
357}
358
359/// Check if all hashes in a vector are unique
360fn all_unique(hashes: &[Hash]) -> bool {
361    let mut seen = std::collections::HashSet::new();
362    for hash in hashes {
363        if !seen.insert(hash.as_str()) {
364            return false;
365        }
366    }
367    true
368}