Hash

Struct Hash 

Source
pub struct Hash(pub String);
Expand description

Represents a Git object hash (commit, tree, blob, etc.).

Tuple Fields§

§0: String

Implementations§

Source§

impl Hash

Source

pub fn as_str(&self) -> &str

Get the hash as a string slice.

Examples found in repository?
examples/commit_workflows.rs (line 77)
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}
More examples
Hide additional examples
examples/commit_history.rs (line 298)
5fn main() -> Result<()> {
6    let test_path = env::temp_dir().join("rustic_git_commit_history_example");
7
8    // Clean up if exists
9    if test_path.exists() {
10        fs::remove_dir_all(&test_path).unwrap();
11    }
12
13    // Create a test repository
14    let repo = Repository::init(&test_path, false)?;
15    println!("Created repository at: {}", test_path.display());
16
17    // Create several commits to build history
18    println!("\n=== Building Commit History ===");
19
20    // First commit
21    fs::write(
22        test_path.join("README.md"),
23        "# Commit History Demo\n\nA demonstration of rustic-git log functionality.",
24    )
25    .unwrap();
26    repo.add(&["README.md"])?;
27    let commit1 = repo.commit("Initial commit - add README")?;
28    println!("Created commit 1: {} - Initial commit", commit1.short());
29
30    // Second commit
31    fs::create_dir_all(test_path.join("src")).unwrap();
32    fs::write(
33        test_path.join("src/main.rs"),
34        "fn main() {\n    println!(\"Hello, world!\");\n}",
35    )
36    .unwrap();
37    repo.add(&["src/main.rs"])?;
38    let commit2 = repo.commit("Add main.rs with Hello World")?;
39    println!("Created commit 2: {} - Add main.rs", commit2.short());
40
41    // Third commit
42    fs::write(
43        test_path.join("src/lib.rs"),
44        "pub fn greet(name: &str) -> String {\n    format!(\"Hello, {}!\", name)\n}",
45    )
46    .unwrap();
47    repo.add(&["src/lib.rs"])?;
48    let commit3 = repo.commit("Add library module with greet function")?;
49    println!("Created commit 3: {} - Add lib.rs", commit3.short());
50
51    // Fourth commit
52    fs::write(
53        test_path.join("Cargo.toml"),
54        "[package]\nname = \"demo\"\nversion = \"0.1.0\"\nedition = \"2021\"",
55    )
56    .unwrap();
57    repo.add(&["Cargo.toml"])?;
58    let commit4 = repo.commit("Add Cargo.toml configuration")?;
59    println!("Created commit 4: {} - Add Cargo.toml", commit4.short());
60
61    // Fifth commit - bug fix
62    fs::write(
63        test_path.join("src/main.rs"),
64        "fn main() {\n    println!(\"Hello, rustic-git!\");\n}",
65    )
66    .unwrap();
67    repo.add(&["src/main.rs"])?;
68    let commit5 = repo.commit("Fix greeting message in main")?;
69    println!("Created commit 5: {} - Fix greeting", commit5.short());
70
71    // Sixth commit - documentation
72    fs::write(test_path.join("README.md"), "# Commit History Demo\n\nA demonstration of rustic-git log functionality.\n\n## Features\n\n- Greeting functionality\n- Command line interface\n").unwrap();
73    repo.add(&["README.md"])?;
74    let commit6 = repo.commit("Update README with features section")?;
75    println!("Created commit 6: {} - Update README", commit6.short());
76
77    println!("Built commit history with 6 commits");
78
79    // Basic log operations
80    println!("\n=== Basic Log Operations ===");
81
82    let all_commits = repo.log()?;
83    println!("Total commits in repository: {}", all_commits.len());
84
85    println!("\nAll commits (most recent first):");
86    for (i, commit) in all_commits.iter().enumerate() {
87        println!("  {}. {}", i + 1, commit);
88    }
89
90    // Recent commits
91    println!("\n=== Recent Commits ===");
92    let recent = repo.recent_commits(3)?;
93    println!("Last 3 commits:");
94    for commit in recent.iter() {
95        println!("  {} - {}", commit.hash.short(), commit.message.subject);
96        if let Some(body) = &commit.message.body {
97            println!("    Body: {}", body);
98        }
99    }
100
101    // Advanced filtering with LogOptions
102    println!("\n=== Advanced Filtering ===");
103
104    // Filter by message content
105    let fix_commits = all_commits.with_message_containing("fix");
106    println!("Commits with 'fix' in message:");
107    for commit in fix_commits {
108        println!("  {} - {}", commit.hash.short(), commit.message.subject);
109    }
110
111    // Filter by date (recent commits)
112    let now = Utc::now();
113    let recent_commits = all_commits.since(now - Duration::minutes(5));
114    println!("\nCommits from last 5 minutes: {}", recent_commits.count());
115
116    // Using LogOptions for advanced queries
117    println!("\n=== LogOptions Advanced Queries ===");
118
119    // Get commits with grep
120    let opts = LogOptions::new().max_count(10).grep("README".to_string());
121    let readme_commits = repo.log_with_options(&opts)?;
122    println!("Commits mentioning 'README': {}", readme_commits.len());
123    for commit in readme_commits.iter() {
124        println!("  {} - {}", commit.hash.short(), commit.message.subject);
125    }
126
127    // Get commits affecting specific paths
128    println!("\n=== Path-Specific History ===");
129    let src_commits = repo.log_for_paths(&["src/"])?;
130    println!("Commits affecting src/ directory: {}", src_commits.len());
131    for commit in src_commits.iter() {
132        println!("  {} - {}", commit.hash.short(), commit.message.subject);
133    }
134
135    // Show detailed commit information
136    println!("\n=== Detailed Commit Information ===");
137
138    let commit_details = repo.show_commit(&commit3)?;
139    println!("Detailed info for commit {}:", commit3.short());
140    println!("  Author: {}", commit_details.commit.author);
141    println!("  Committer: {}", commit_details.commit.committer);
142    println!(
143        "  Timestamp: {}",
144        commit_details
145            .commit
146            .timestamp
147            .format("%Y-%m-%d %H:%M:%S UTC")
148    );
149    println!("  Message: {}", commit_details.commit.message.subject);
150    println!("  Parents: {}", commit_details.commit.parents.len());
151    for parent in commit_details.commit.parents.iter() {
152        println!("    - {}", parent.short());
153    }
154    println!("  Files changed: {}", commit_details.files_changed.len());
155    for file in &commit_details.files_changed {
156        println!("    - {}", file.display());
157    }
158    println!(
159        "  Changes: +{} -{}",
160        commit_details.insertions, commit_details.deletions
161    );
162
163    // Commit analysis
164    println!("\n=== Commit Analysis ===");
165
166    let merge_commits: Vec<_> = all_commits.merges_only().collect();
167    let regular_commits: Vec<_> = all_commits.no_merges().collect();
168
169    println!("Repository statistics:");
170    println!("  Total commits: {}", all_commits.len());
171    println!("  Merge commits: {}", merge_commits.len());
172    println!("  Regular commits: {}", regular_commits.len());
173
174    if let Some(first_commit) = all_commits.first() {
175        println!(
176            "  Most recent: {} ({})",
177            first_commit.hash.short(),
178            first_commit.message.subject
179        );
180    }
181
182    if let Some(last_commit) = all_commits.last() {
183        println!(
184            "  Oldest: {} ({})",
185            last_commit.hash.short(),
186            last_commit.message.subject
187        );
188    }
189
190    // Search operations
191    println!("\n=== Search Operations ===");
192
193    // Find by hash
194    if let Some(found) = all_commits.find_by_hash(&commit2) {
195        println!("Found commit by full hash: {}", found.message.subject);
196    }
197
198    // Find by short hash
199    if let Some(found) = all_commits.find_by_short_hash(commit4.short()) {
200        println!("Found commit by short hash: {}", found.message.subject);
201    }
202
203    // Commit range operations
204    println!("\n=== Commit Range Operations ===");
205
206    let range_commits = repo.log_range(&commit2, &commit5)?;
207    println!(
208        "Commits in range {}..{}: {}",
209        commit2.short(),
210        commit5.short(),
211        range_commits.len()
212    );
213    for commit in range_commits.iter() {
214        println!("  {} - {}", commit.hash.short(), commit.message.subject);
215    }
216
217    // Advanced LogOptions demonstration
218    println!("\n=== Advanced LogOptions Usage ===");
219
220    let advanced_opts = LogOptions::new()
221        .max_count(5)
222        .no_merges(true)
223        .paths(vec!["src/main.rs".into()]);
224
225    let filtered_commits = repo.log_with_options(&advanced_opts)?;
226    println!(
227        "Non-merge commits affecting src/main.rs (max 5): {}",
228        filtered_commits.len()
229    );
230    for commit in filtered_commits.iter() {
231        println!("  {} - {}", commit.hash.short(), commit.message.subject);
232    }
233
234    // Commit message analysis
235    println!("\n=== Commit Message Analysis ===");
236
237    let total_commits = all_commits.len();
238    let commits_with_body: Vec<_> = all_commits
239        .iter()
240        .filter(|c| c.message.body.is_some())
241        .collect();
242
243    println!("Message statistics:");
244    println!("  Total commits: {}", total_commits);
245    println!("  Commits with body text: {}", commits_with_body.len());
246    println!(
247        "  Commits with subject only: {}",
248        total_commits - commits_with_body.len()
249    );
250
251    // Display commit types by analyzing subjects
252    let fix_count = all_commits
253        .iter()
254        .filter(|c| c.message.subject.to_lowercase().contains("fix"))
255        .count();
256    let add_count = all_commits
257        .iter()
258        .filter(|c| c.message.subject.to_lowercase().contains("add"))
259        .count();
260    let update_count = all_commits
261        .iter()
262        .filter(|c| c.message.subject.to_lowercase().contains("update"))
263        .count();
264
265    println!("  Commit types:");
266    println!("    - Fix commits: {}", fix_count);
267    println!("    - Add commits: {}", add_count);
268    println!("    - Update commits: {}", update_count);
269    println!(
270        "    - Other commits: {}",
271        total_commits - fix_count - add_count - update_count
272    );
273
274    // Timeline view
275    println!("\n=== Timeline View ===");
276
277    println!("Commit timeline (oldest to newest):");
278    let commits: Vec<_> = all_commits.iter().collect();
279    for commit in commits.iter().rev() {
280        let commit_type = if commit.is_merge() { "MERGE" } else { "COMMIT" };
281        println!(
282            "  {} {} {} - {}",
283            commit.timestamp.format("%H:%M:%S"),
284            commit_type,
285            commit.hash.short(),
286            commit.message.subject
287        );
288    }
289
290    // Summary
291    println!("\n=== Summary ===");
292
293    println!("Commit history demonstration completed!");
294    println!("  Repository: {}", test_path.display());
295    println!("  Total commits analyzed: {}", all_commits.len());
296    println!("  Hash examples:");
297    for commit in all_commits.iter().take(3) {
298        println!("    - Full: {}", commit.hash.as_str());
299        println!("      Short: {}", commit.hash.short());
300    }
301
302    // Clean up
303    fs::remove_dir_all(&test_path).unwrap();
304    println!("\nCleaned up test repository");
305
306    Ok(())
307}
Source

pub fn short(&self) -> &str

Get the short version of the hash (first 7 characters).

Examples found in repository?
examples/error_handling.rs (line 165)
156fn demonstrate_git_command_errors(repo_path: &std::path::Path) -> Result<()> {
157    println!("Git Command Error Scenarios:\n");
158
159    let repo = Repository::open(repo_path)?;
160
161    // 1. Empty commit (no staged changes)
162    println!("1. Attempting commit with no staged changes:");
163    match repo.commit("Empty commit attempt") {
164        Ok(hash) => {
165            println!("   Unexpectedly succeeded: {}", hash.short());
166            println!("   Some Git configurations allow empty commits");
167        }
168        Err(GitError::CommandFailed(msg)) => {
169            println!("   CommandFailed caught: {}", msg);
170            println!("   Git requires changes to commit (normal behavior)");
171        }
172        Err(GitError::IoError(msg)) => {
173            println!("   IoError caught: {}", msg);
174        }
175    }
176
177    // 2. Commit with problematic message
178    println!("\n2. Testing commit message edge cases:");
179
180    // Stage a file for testing
181    fs::write(
182        repo_path.join("commit_test.txt"),
183        "Content for commit testing",
184    )?;
185    repo.add(&["commit_test.txt"])?;
186
187    // Very long commit message
188    let very_long_message = "A ".repeat(1000) + "very long commit message";
189    match repo.commit(&very_long_message) {
190        Ok(hash) => {
191            println!("   Long commit message succeeded: {}", hash.short());
192            println!("   Git handled the long message fine");
193        }
194        Err(GitError::CommandFailed(msg)) => {
195            println!("   Long commit message failed: {}", msg);
196        }
197        Err(GitError::IoError(msg)) => {
198            println!("   IoError with long message: {}", msg);
199        }
200    }
201
202    println!();
203    Ok(())
204}
205
206/// Demonstrate error recovery patterns
207fn demonstrate_error_recovery_patterns(repo_path: &std::path::Path) -> Result<()> {
208    println!("Error Recovery Patterns:\n");
209
210    let repo = Repository::open(repo_path)?;
211
212    // Pattern 1: Retry with different approach
213    println!("1. Retry Pattern - Graceful degradation:");
214
215    // Try to add specific files, fall back to add_all on failure
216    let files_to_add = ["missing1.txt", "missing2.txt", "missing3.txt"];
217
218    println!("   Attempting to add specific files...");
219    match repo.add(&files_to_add) {
220        Ok(_) => println!("      Specific files added successfully"),
221        Err(e) => {
222            println!("      Specific files failed: {:?}", e);
223            println!("      Falling back to add_all()...");
224
225            match repo.add_all() {
226                Ok(_) => {
227                    let status = repo.status()?;
228                    println!(
229                        "      add_all() succeeded, {} files staged",
230                        status.entries.len()
231                    );
232                }
233                Err(fallback_error) => {
234                    println!("      Fallback also failed: {:?}", fallback_error);
235                }
236            }
237        }
238    }
239
240    // Pattern 2: Partial success handling
241    println!("\n2. Partial Success Pattern:");
242
243    // Create some files with known issues
244    fs::write(repo_path.join("good1.txt"), "Good file 1")?;
245    fs::write(repo_path.join("good2.txt"), "Good file 2")?;
246    // Don't create bad1.txt - it will be missing
247
248    let mixed_files = ["good1.txt", "bad1.txt", "good2.txt"];
249
250    println!("   Attempting to add mixed valid/invalid files...");
251    match repo.add(&mixed_files) {
252        Ok(_) => println!("      All files added (unexpected success)"),
253        Err(GitError::CommandFailed(msg)) => {
254            println!("      Batch add failed: {}", msg);
255            println!("      Recovery: Adding files individually...");
256
257            let mut successful_adds = 0;
258            let mut failed_adds = 0;
259
260            for file in &mixed_files {
261                match repo.add(&[file]) {
262                    Ok(_) => {
263                        successful_adds += 1;
264                        println!("         Added: {}", file);
265                    }
266                    Err(_) => {
267                        failed_adds += 1;
268                        println!("         Failed: {}", file);
269                    }
270                }
271            }
272
273            println!(
274                "      Results: {} succeeded, {} failed",
275                successful_adds, failed_adds
276            );
277        }
278        Err(GitError::IoError(msg)) => {
279            println!("      IoError during batch add: {}", msg);
280        }
281    }
282
283    // Pattern 3: Status checking before operations
284    println!("\n3. Preventive Pattern - Check before operation:");
285
286    println!("   Checking repository status before commit...");
287    let status = repo.status()?;
288
289    if status.is_clean() {
290        println!("      Repository is clean - no commit needed");
291    } else {
292        println!("      Repository has {} changes", status.entries.len());
293
294        // Show what would be committed
295        for entry in &status.entries {
296            println!(
297                "         Index {:?}, Worktree {:?}: {}",
298                entry.index_status,
299                entry.worktree_status,
300                entry.path.display()
301            );
302        }
303
304        // Safe commit since we know there are changes
305        match repo.commit("Commit after status check") {
306            Ok(hash) => println!("      Safe commit succeeded: {}", hash.short()),
307            Err(e) => println!("      Even safe commit failed: {:?}", e),
308        }
309    }
310
311    println!();
312    Ok(())
313}
314
315/// Demonstrate error propagation strategies
316fn demonstrate_error_propagation_strategies(base_path: &std::path::Path) -> Result<()> {
317    println!("Error Propagation Strategies:\n");
318
319    // Strategy 1: Early return with ?
320    println!("1. Early Return Strategy (using ?):");
321    match workflow_with_early_return(base_path) {
322        Ok(message) => println!("      Workflow completed: {}", message),
323        Err(e) => println!("      Workflow failed early: {:?}", e),
324    }
325
326    // Strategy 2: Collect all errors
327    println!("\n2. Error Collection Strategy:");
328    let results = workflow_with_error_collection(base_path);
329
330    let successful = results.iter().filter(|r| r.is_ok()).count();
331    let failed = results.iter().filter(|r| r.is_err()).count();
332
333    println!(
334        "      Operations: {} succeeded, {} failed",
335        successful, failed
336    );
337
338    for (i, result) in results.iter().enumerate() {
339        match result {
340            Ok(msg) => println!("         Step {}: {}", i + 1, msg),
341            Err(e) => println!("         Step {}: {:?}", i + 1, e),
342        }
343    }
344
345    // Strategy 3: Error context enrichment
346    println!("\n3. Error Context Strategy:");
347    match workflow_with_context(base_path) {
348        Ok(message) => println!("      Contextual workflow: {}", message),
349        Err(e) => println!("      Contextual workflow failed: {:?}", e),
350    }
351
352    println!();
353    Ok(())
354}
355
356/// Workflow that returns early on first error
357fn workflow_with_early_return(base_path: &std::path::Path) -> Result<String> {
358    let repo_path = base_path.join("early_return_test");
359
360    // This will propagate any error immediately
361    let repo = Repository::init(&repo_path, false)?;
362
363    fs::write(repo_path.join("file1.txt"), "Content 1")?;
364    repo.add(&["file1.txt"])?;
365
366    let hash = repo.commit("Early return workflow commit")?;
367
368    // Clean up
369    fs::remove_dir_all(&repo_path)?;
370
371    Ok(format!("Completed with commit {}", hash.short()))
372}
373
374/// Workflow that collects all errors instead of failing fast
375fn workflow_with_error_collection(base_path: &std::path::Path) -> Vec<Result<String>> {
376    let repo_path = base_path.join("error_collection_test");
377    let mut results = Vec::new();
378
379    // Step 1: Initialize repo
380    results.push(Repository::init(&repo_path, false).map(|_| "Repository initialized".to_string()));
381
382    // Step 2: Add files (some may fail)
383    let files_to_create = ["good.txt", "another_good.txt"];
384
385    for file in &files_to_create {
386        results.push(
387            fs::write(repo_path.join(file), "Content")
388                .map_err(GitError::from)
389                .map(|_| format!("Created {}", file)),
390        );
391    }
392
393    // Step 3: Try to add files (continue even if repo init failed)
394    if let Ok(repo) = Repository::open(&repo_path) {
395        results.push(
396            repo.add(&files_to_create)
397                .map(|_| "Files added to staging".to_string()),
398        );
399
400        results.push(
401            repo.commit("Error collection workflow")
402                .map(|hash| format!("Committed: {}", hash.short())),
403        );
404    } else {
405        results.push(Err(GitError::CommandFailed(
406            "Could not open repo for adding files".to_string(),
407        )));
408        results.push(Err(GitError::CommandFailed(
409            "Could not open repo for commit".to_string(),
410        )));
411    }
412
413    // Cleanup (don't add to results as it's not part of main workflow)
414    let _ = fs::remove_dir_all(&repo_path);
415
416    results
417}
418
419/// Workflow with enhanced error context
420fn workflow_with_context(base_path: &std::path::Path) -> Result<String> {
421    let repo_path = base_path.join("context_test");
422
423    // Add context to errors
424    let repo = Repository::init(&repo_path, false).inspect_err(|_e| {
425        eprintln!(
426            "Context: Failed to initialize repository at {}",
427            repo_path.display()
428        );
429    })?;
430
431    // Create file with context
432    fs::write(repo_path.join("context_file.txt"), "Content with context").map_err(|e| {
433        eprintln!("Context: Failed to create context_file.txt");
434        GitError::from(e)
435    })?;
436
437    // Add with context
438    repo.add(&["context_file.txt"]).inspect_err(|_e| {
439        eprintln!("Context: Failed to stage context_file.txt");
440    })?;
441
442    // Commit with context
443    let hash = repo.commit("Context workflow commit").inspect_err(|_e| {
444        eprintln!("Context: Failed to create commit");
445    })?;
446
447    // Clean up
448    fs::remove_dir_all(&repo_path)?;
449
450    Ok(format!("Context workflow completed: {}", hash.short()))
451}
More examples
Hide additional examples
examples/merge_operations.rs (line 150)
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    // Add a commit to master to prevent fast-forward
100    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    // Create another feature branch
111    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    // Switch back to master
121    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    // Perform no-fast-forward merge
127    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    // Show the commit history
144    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    // Create conflicting branch
162    println!("1. Creating branch with conflicting changes...");
163    repo.checkout_new("feature/conflict", None)?;
164
165    // Modify the same file differently
166    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    // Switch back to master and make conflicting change
176    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    // Attempt merge (will have conflicts)
190    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            // Check merge in progress
202            if repo.merge_in_progress()? {
203                println!("   ✓ Merge in progress status detected");
204            }
205
206            // Show conflict markers in file
207            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            // Abort the merge
221            println!("\n5. Aborting merge...");
222            repo.abort_merge()?;
223            println!("   ✓ Merge aborted successfully");
224
225            // Verify merge is no longer in progress
226            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    // Create a simple feature branch
240    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    // Switch back to master
249    let branches = repo.branches()?;
250    let master_branch = branches.find("master").unwrap();
251    repo.checkout(master_branch)?;
252
253    // Test merge with different options
254    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    // Show final repository state
277    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}
examples/basic_usage.rs (line 113)
16fn main() -> Result<()> {
17    println!("Rustic Git - Basic Usage Example\n");
18
19    // Use a temporary directory for this example
20    let repo_path = env::temp_dir().join("rustic_git_basic_example");
21
22    // Clean up any previous run
23    if repo_path.exists() {
24        fs::remove_dir_all(&repo_path).expect("Failed to clean up previous example");
25    }
26
27    println!("Initializing new repository at: {}", repo_path.display());
28
29    // Initialize a new repository
30    let repo = Repository::init(&repo_path, false)?;
31    println!("Repository initialized successfully\n");
32
33    // Create some example files
34    println!("Creating example files...");
35    fs::create_dir_all(repo_path.join("src"))?;
36
37    fs::write(
38        repo_path.join("README.md"),
39        "# My Awesome Project\n\nThis is a demo project for rustic-git!\n",
40    )?;
41
42    fs::write(
43        repo_path.join("src/main.rs"),
44        r#"fn main() {
45    println!("Hello from rustic-git example!");
46}
47"#,
48    )?;
49
50    fs::write(
51        repo_path.join("src/lib.rs"),
52        "// Library code goes here\npub fn hello() -> &'static str {\n    \"Hello, World!\"\n}\n",
53    )?;
54
55    println!("Created 3 files: README.md, src/main.rs, src/lib.rs\n");
56
57    // Check repository status
58    println!("Checking repository status...");
59    let status = repo.status()?;
60
61    if status.is_clean() {
62        println!("   Repository is clean (no changes)");
63    } else {
64        println!("   Repository has changes:");
65        println!("   Unstaged files: {}", status.unstaged_files().count());
66        println!("   Untracked files: {}", status.untracked_entries().count());
67
68        // Show untracked files
69        for entry in status.untracked_entries() {
70            println!("      - {}", entry.path.display());
71        }
72    }
73    println!();
74
75    // Stage specific files first
76    println!("Staging files...");
77
78    // Stage README.md first
79    repo.add(&["README.md"])?;
80    println!("Staged README.md");
81
82    // Stage all remaining files
83    repo.add_all()?;
84    println!("Staged all remaining files");
85
86    // Check status after staging
87    let status_after_staging = repo.status()?;
88    println!("\nStatus after staging:");
89    if status_after_staging.is_clean() {
90        println!("   Repository is clean (all changes staged)");
91    } else {
92        println!(
93            "   Files staged for commit: {}",
94            status_after_staging.entries.len()
95        );
96        for entry in &status_after_staging.entries {
97            println!(
98                "      Index {:?}, Worktree {:?}: {}",
99                entry.index_status,
100                entry.worktree_status,
101                entry.path.display()
102            );
103        }
104    }
105    println!();
106
107    // Create a commit
108    println!("Creating commit...");
109    let hash = repo.commit("Initial commit: Add project structure and basic files")?;
110
111    println!("Commit created successfully!");
112    println!("   Full hash: {}", hash);
113    println!("   Short hash: {}", hash.short());
114    println!();
115
116    // Verify final status
117    println!("Final repository status:");
118    let final_status = repo.status()?;
119    if final_status.is_clean() {
120        println!("   Repository is clean - all changes committed!");
121    } else {
122        println!("   Repository still has uncommitted changes");
123    }
124    println!();
125
126    // Clean up
127    println!("Cleaning up example repository...");
128    fs::remove_dir_all(&repo_path)?;
129    println!("Example completed successfully!");
130
131    Ok(())
132}
examples/branch_operations.rs (line 34)
4fn main() -> Result<()> {
5    let test_path = env::temp_dir().join("rustic_git_branch_example");
6
7    // Clean up if exists
8    if test_path.exists() {
9        fs::remove_dir_all(&test_path).unwrap();
10    }
11
12    // Create a test repository
13    let repo = Repository::init(&test_path, false)?;
14    println!("Created repository at: {}", test_path.display());
15
16    // Create initial commit so we have a valid HEAD
17    fs::write(test_path.join("README.md"), "# Branch Operations Demo\n").unwrap();
18    repo.add(&["README.md"])?;
19    repo.commit("Initial commit")?;
20    println!("Created initial commit");
21
22    // List all branches
23    let branches = repo.branches()?;
24    println!("\n=== Initial Branches ===");
25    for branch in branches.iter() {
26        println!("  {}", branch);
27    }
28
29    // Get current branch
30    if let Some(current) = repo.current_branch()? {
31        println!(
32            "\nCurrent branch: {} ({})",
33            current.name,
34            current.commit_hash.short()
35        );
36    }
37
38    // Create new branches
39    println!("\n=== Creating Branches ===");
40    let feature_branch = repo.create_branch("feature/new-api", None)?;
41    println!("Created branch: {}", feature_branch.name);
42
43    let bugfix_branch = repo.create_branch("bugfix/issue-123", Some("HEAD"))?;
44    println!("Created branch: {}", bugfix_branch.name);
45
46    // List branches again
47    let branches = repo.branches()?;
48    println!("\n=== After Creating Branches ===");
49    for branch in branches.local() {
50        println!("  {} (local)", branch);
51    }
52
53    // Create and checkout a new branch
54    println!("\n=== Creating and Checking Out Branch ===");
55    let dev_branch = repo.checkout_new("develop", None)?;
56    println!("Created and checked out: {}", dev_branch.name);
57
58    // Make a commit on the new branch
59    fs::write(test_path.join("feature.txt"), "New feature code\n").unwrap();
60    repo.add(&["feature.txt"])?;
61    repo.commit("Add new feature")?;
62    println!("Made commit on develop branch");
63
64    // Show current branch after checkout
65    if let Some(current) = repo.current_branch()? {
66        println!(
67            "Now on branch: {} ({})",
68            current.name,
69            current.commit_hash.short()
70        );
71    }
72
73    // Switch back to master branch
74    let main_branch = branches.find("master").unwrap().clone();
75    repo.checkout(&main_branch)?;
76    println!("\nSwitched back to master branch");
77
78    // List all branches with details
79    let final_branches = repo.branches()?;
80    println!("\n=== Final Branch List ===");
81    println!("Total branches: {}", final_branches.len());
82    println!("Local branches: {}", final_branches.local_count());
83
84    for branch in final_branches.iter() {
85        let marker = if branch.is_current { "*" } else { " " };
86        let branch_type = if branch.is_local() { "local" } else { "remote" };
87        println!(
88            "  {}{} ({}) {}",
89            marker,
90            branch.name,
91            branch_type,
92            branch.commit_hash.short()
93        );
94
95        if let Some(upstream) = &branch.upstream {
96            println!("    └── tracks: {}", upstream);
97        }
98    }
99
100    // Demonstrate branch searching
101    println!("\n=== Branch Search Examples ===");
102
103    if let Some(branch) = final_branches.find("develop") {
104        println!("Found branch by name: {}", branch.name);
105    }
106
107    if let Some(branch) = final_branches.find_by_short_name("new-api") {
108        println!("Found branch by short name: {}", branch.name);
109    }
110
111    // Demonstrate branch filtering
112    println!("\n=== Branch Filtering ===");
113
114    println!("Local branches:");
115    for branch in final_branches.local() {
116        println!("  - {}", branch.name);
117    }
118
119    if final_branches.remote_count() > 0 {
120        println!("Remote branches:");
121        for branch in final_branches.remote() {
122            println!("  - {}", branch.name);
123        }
124    }
125
126    // Delete a branch (switch away first if it's current)
127    println!("\n=== Branch Deletion ===");
128    let bugfix = final_branches.find("bugfix/issue-123").unwrap().clone();
129    repo.delete_branch(&bugfix, false)?;
130    println!("Deleted branch: {}", bugfix.name);
131
132    // Show final state
133    let final_branches = repo.branches()?;
134    println!("\nFinal branch count: {}", final_branches.len());
135
136    // Clean up
137    fs::remove_dir_all(&test_path).unwrap();
138    println!("\nCleaned up test repository");
139
140    Ok(())
141}
examples/config_operations.rs (line 92)
14fn main() -> Result<()> {
15    println!("Rustic Git - Repository Configuration Operations Example\n");
16
17    // Use a temporary directory for this example
18    let repo_path = env::temp_dir().join("rustic_git_config_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    println!("Initializing new repository at: {}", repo_path.display());
26
27    // Initialize a new repository
28    let repo = Repository::init(&repo_path, false)?;
29
30    // ==================== USER CONFIGURATION ====================
31
32    println!("\n[CONFIG] Configuring git user settings...");
33
34    // Set user configuration (convenience method)
35    repo.config()
36        .set_user("Alice Developer", "alice@example.com")?;
37    println!("Set user configuration");
38
39    // Verify user configuration
40    let (name, email) = repo.config().get_user()?;
41    println!("Current user: {} <{}>", name, email);
42
43    // ==================== GENERAL CONFIGURATION ====================
44
45    println!("\n[CONFIG] Setting repository configuration values...");
46
47    // Set various git configuration values
48    repo.config().set("core.autocrlf", "false")?;
49    repo.config().set("core.ignorecase", "true")?;
50    repo.config().set("pull.rebase", "true")?;
51    repo.config().set("push.default", "simple")?;
52    repo.config().set("branch.autosetupmerge", "always")?;
53
54    println!("Set core configuration values");
55
56    // Get and display configuration values
57    println!("\n[CONFIG] Current repository configuration:");
58
59    let configs = [
60        "core.autocrlf",
61        "core.ignorecase",
62        "pull.rebase",
63        "push.default",
64        "branch.autosetupmerge",
65    ];
66
67    for config_key in &configs {
68        match repo.config().get(config_key) {
69            Ok(value) => println!("  {} = {}", config_key, value),
70            Err(_) => println!("  {} = <not set>", config_key),
71        }
72    }
73
74    // ==================== CONFIGURATION WITH COMMITS ====================
75
76    println!("\n[COMMIT] Testing configuration with commit operations...");
77
78    // Create a test file
79    let test_file_path = repo_path.join("test.txt");
80    fs::write(
81        &test_file_path,
82        "Hello from rustic-git configuration example!",
83    )?;
84    println!("Created test file: test.txt");
85
86    // Stage the file
87    repo.add(&["test.txt"])?;
88    println!("Staged test.txt");
89
90    // Create a commit (this will use our configured user)
91    let commit_hash = repo.commit("Add test file with configuration example")?;
92    println!("Created commit: {}", commit_hash.short());
93
94    // ==================== CONFIGURATION MODIFICATION ====================
95
96    println!("\n[UPDATE] Modifying configuration values...");
97
98    // Change some configuration values
99    repo.config().set("core.autocrlf", "true")?;
100    repo.config()
101        .set("user.email", "alice.developer@newcompany.com")?;
102
103    println!("Updated configuration values");
104
105    // Display updated values
106    let autocrlf = repo.config().get("core.autocrlf")?;
107    let (updated_name, updated_email) = repo.config().get_user()?;
108
109    println!("Updated configuration:");
110    println!("  core.autocrlf = {}", autocrlf);
111    println!("  user: {} <{}>", updated_name, updated_email);
112
113    // ==================== CONFIGURATION REMOVAL ====================
114
115    println!("\n[REMOVE] Removing configuration values...");
116
117    // Remove a configuration value
118    repo.config().unset("branch.autosetupmerge")?;
119    println!("Removed branch.autosetupmerge");
120
121    // Try to get the removed value (should fail)
122    match repo.config().get("branch.autosetupmerge") {
123        Ok(value) => println!("Unexpected: branch.autosetupmerge = {}", value),
124        Err(_) => println!("Confirmed: branch.autosetupmerge is not set"),
125    }
126
127    // ==================== ADVANCED CONFIGURATION ====================
128
129    println!("\n[ADVANCED] Setting advanced configuration...");
130
131    // Set some advanced git configuration
132    repo.config().set("diff.tool", "vimdiff")?;
133    repo.config().set("merge.tool", "vimdiff")?;
134    repo.config().set("alias.st", "status")?;
135    repo.config().set("alias.co", "checkout")?;
136    repo.config().set("alias.br", "branch")?;
137    repo.config().set("alias.ci", "commit")?;
138
139    println!("Set advanced configuration (diff/merge tools and aliases)");
140
141    // Display all custom configuration
142    println!("\n[SUMMARY] Complete repository configuration summary:");
143
144    let all_configs = [
145        ("User", vec![("user.name", ""), ("user.email", "")]),
146        ("Core", vec![("core.autocrlf", ""), ("core.ignorecase", "")]),
147        ("Workflow", vec![("pull.rebase", ""), ("push.default", "")]),
148        ("Tools", vec![("diff.tool", ""), ("merge.tool", "")]),
149        (
150            "Aliases",
151            vec![
152                ("alias.st", ""),
153                ("alias.co", ""),
154                ("alias.br", ""),
155                ("alias.ci", ""),
156            ],
157        ),
158    ];
159
160    for (category, configs) in &all_configs {
161        println!("\n  {}:", category);
162        for (key, _) in configs {
163            match repo.config().get(key) {
164                Ok(value) => println!("    {} = {}", key, value),
165                Err(_) => println!("    {} = <not set>", key),
166            }
167        }
168    }
169
170    // ==================== PRACTICAL EXAMPLE ====================
171
172    println!("\n[TEAM] Practical example: Setting up repository for a team...");
173
174    // Configure repository for team development
175    repo.config().set("user.name", "Team Member")?;
176    repo.config().set("user.email", "team@company.com")?;
177    repo.config().set("core.autocrlf", "input")?;
178    repo.config().set("core.safecrlf", "true")?;
179    repo.config().set("pull.rebase", "true")?;
180    repo.config().set("push.default", "current")?;
181    repo.config().set("init.defaultBranch", "main")?;
182
183    println!("Configured repository for team development");
184
185    // Create another commit with the team configuration
186    fs::write(
187        repo_path.join("team.md"),
188        "# Team Development\n\nThis repository is configured for team development.",
189    )?;
190    repo.add(&["team.md"])?;
191    let team_commit = repo.commit("Add team development documentation")?;
192
193    println!("Created team commit: {}", team_commit.short());
194
195    // Final verification
196    let (final_name, final_email) = repo.config().get_user()?;
197    println!("\n[FINAL] Final repository configuration:");
198    println!("  User: {} <{}>", final_name, final_email);
199    println!("  Repository configured for team development workflow");
200
201    // ==================== CLEANUP ====================
202
203    println!("\n[CLEANUP] Cleaning up...");
204    fs::remove_dir_all(&repo_path).expect("Failed to clean up example");
205    println!("Example completed successfully!");
206
207    Ok(())
208}
examples/diff_operations.rs (line 52)
4fn main() -> rustic_git::Result<()> {
5    println!("Rustic Git - Diff Operations Example\n");
6
7    let repo_path = env::temp_dir().join("rustic_git_diff_example");
8    // Clean up any previous run
9    if repo_path.exists() {
10        fs::remove_dir_all(&repo_path).ok();
11    }
12    println!("Working in temporary directory: {}", repo_path.display());
13
14    // Initialize repository
15    let repo = Repository::init(&repo_path, false)?;
16    println!("Repository initialized successfully\n");
17
18    // Configure git user for commits
19    let config = repo.config();
20    config.set_user("Test User", "test@example.com")?;
21
22    println!("=== Creating Initial Files ===");
23
24    // Create initial files
25    let readme_path = repo_path.join("README.md");
26    let src_dir = repo_path.join("src");
27    fs::create_dir_all(&src_dir).unwrap();
28    let main_path = src_dir.join("main.rs");
29    let lib_path = src_dir.join("lib.rs");
30
31    fs::write(
32        &readme_path,
33        "# Test Project\n\nA sample project for testing diff operations.\n",
34    )
35    .unwrap();
36    fs::write(
37        &main_path,
38        "fn main() {\n    println!(\"Hello, world!\");\n}\n",
39    )
40    .unwrap();
41    fs::write(
42        &lib_path,
43        "pub fn add(a: i32, b: i32) -> i32 {\n    a + b\n}\n",
44    )
45    .unwrap();
46
47    println!("Created initial files: README.md, src/main.rs, src/lib.rs");
48
49    // Stage and commit initial files
50    repo.add_all()?;
51    let initial_commit = repo.commit("feat: initial commit with basic files")?;
52    println!("Initial commit: {}\n", initial_commit.short());
53
54    println!("=== Testing Different Diff Operations ===");
55
56    // Test 1: Diff with no changes (should be empty)
57    println!("1. Diff with no changes:");
58    let diff = repo.diff()?;
59    if diff.is_empty() {
60        println!("   ✓ No changes detected (as expected)");
61    } else {
62        println!("   ✗ Unexpected changes found");
63    }
64    println!();
65
66    // Test 2: Modify files and show unstaged changes
67    println!("2. Creating unstaged changes:");
68    fs::write(&readme_path, "# Test Project\n\nA sample project for testing diff operations.\n\n## Features\n- Git operations\n- Diff functionality\n").unwrap();
69    fs::write(&main_path, "fn main() {\n    println!(\"Hello, world!\");\n    println!(\"Testing diff operations!\");\n}\n").unwrap();
70
71    let diff = repo.diff()?;
72    println!("   Unstaged changes found:");
73    println!("   Files changed: {}", diff.len());
74    for file in diff.iter() {
75        println!("   - {} ({})", file.path.display(), file.status);
76    }
77    println!("   {}", diff.stats);
78    println!();
79
80    // Test 3: Stage some changes and show staged vs unstaged
81    println!("3. Staging README.md and checking staged diff:");
82    repo.add(&[&readme_path])?;
83
84    let staged_diff = repo.diff_staged()?;
85    println!("   Staged changes:");
86    for file in staged_diff.iter() {
87        println!("   - {} ({})", file.path.display(), file.status);
88    }
89    println!("   {}", staged_diff.stats);
90
91    let unstaged_diff = repo.diff()?;
92    println!("   Remaining unstaged changes:");
93    for file in unstaged_diff.iter() {
94        println!("   - {} ({})", file.path.display(), file.status);
95    }
96    println!("   {}", unstaged_diff.stats);
97    println!();
98
99    // Test 4: Diff with options
100    println!("4. Using diff options (name-only):");
101    let name_only_diff = repo.diff_with_options(&DiffOptions::new().name_only())?;
102    println!("   Modified files (name-only):");
103    for file in name_only_diff.iter() {
104        println!("   - {}", file.path.display());
105    }
106    println!();
107
108    // Test 5: Diff with file filtering
109    println!("5. Diff with path filtering (src/ only):");
110    let src_paths = vec![src_dir.clone()];
111    let filtered_diff = repo.diff_with_options(&DiffOptions::new().paths(src_paths))?;
112    println!("   Changes in src/ directory:");
113    for file in filtered_diff.iter() {
114        println!("   - {} ({})", file.path.display(), file.status);
115    }
116    println!();
117
118    // Stage remaining changes and commit
119    repo.add_all()?;
120    let second_commit = repo.commit("feat: add features section and improve main function")?;
121    println!("Second commit: {}", second_commit.short());
122
123    // Test 6: Diff between commits
124    println!("\n6. Diff between commits:");
125    let commit_diff = repo.diff_commits(&initial_commit, &second_commit)?;
126    println!(
127        "   Changes from {} to {}:",
128        initial_commit.short(),
129        second_commit.short()
130    );
131    for file in commit_diff.iter() {
132        println!(
133            "   - {} ({}) +{} -{}",
134            file.path.display(),
135            file.status,
136            file.additions,
137            file.deletions
138        );
139    }
140    println!("   {}", commit_diff.stats);
141    println!();
142
143    // Test 7: Add a new file and show it in diff
144    println!("7. Adding new file and checking diff:");
145    let test_path = repo_path.join("test.txt");
146    fs::write(
147        &test_path,
148        "This is a new test file.\nWith multiple lines.\n",
149    )
150    .unwrap();
151
152    let new_file_diff = repo.diff()?;
153    println!("   New file detected:");
154    for file in new_file_diff.iter() {
155        println!("   - {} ({})", file.path.display(), file.status);
156    }
157    println!();
158
159    // Test 8: Delete a file and show in diff
160    println!("8. Deleting file and checking diff:");
161    fs::remove_file(&lib_path).unwrap();
162
163    let deleted_file_diff = repo.diff()?;
164    println!("   Changes after file deletion:");
165    for file in deleted_file_diff.iter() {
166        println!("   - {} ({})", file.path.display(), file.status);
167    }
168    println!();
169
170    // Test 9: Diff with ignore whitespace options
171    println!("9. Testing whitespace options:");
172
173    // Add some whitespace changes
174    fs::write(&main_path, "fn main() {\n    println!(\"Hello, world!\");\n    println!(\"Testing diff operations!\");    \n}\n").unwrap();
175
176    let normal_diff = repo.diff()?;
177    let whitespace_diff = repo.diff_with_options(&DiffOptions::new().ignore_whitespace())?;
178
179    println!("   Normal diff shows {} files changed", normal_diff.len());
180    println!(
181        "   Whitespace-ignoring diff shows {} files changed",
182        whitespace_diff.len()
183    );
184    println!();
185
186    // Test 10: Show diff with HEAD
187    println!("10. Diff with HEAD (all changes since last commit):");
188    let head_diff = repo.diff_head()?;
189    println!("    All changes since last commit:");
190    for file in head_diff.iter() {
191        println!("    - {} ({})", file.path.display(), file.status);
192    }
193    println!("    {}", head_diff.stats);
194    println!();
195
196    // Test 11: Different diff output formats
197    println!("11. Testing different output formats:");
198
199    let stat_diff = repo.diff_with_options(&DiffOptions::new().stat_only())?;
200    println!("    Stat format:");
201    println!("    {}", stat_diff);
202
203    let numstat_diff = repo.diff_with_options(&DiffOptions::new().numstat())?;
204    println!("    Numstat format - {} files changed", numstat_diff.len());
205    for file in numstat_diff.iter() {
206        println!(
207            "    {} +{} -{}",
208            file.path.display(),
209            file.additions,
210            file.deletions
211        );
212    }
213    println!();
214
215    // Test 12: Filtering by file status
216    println!("12. Filtering files by status:");
217    let all_changes = repo.diff_head()?;
218
219    let added_files: Vec<_> = all_changes.files_with_status(DiffStatus::Added).collect();
220    let modified_files: Vec<_> = all_changes
221        .files_with_status(DiffStatus::Modified)
222        .collect();
223    let deleted_files: Vec<_> = all_changes.files_with_status(DiffStatus::Deleted).collect();
224
225    println!("    Added files: {}", added_files.len());
226    for file in added_files {
227        println!("      - {}", file.path.display());
228    }
229
230    println!("    Modified files: {}", modified_files.len());
231    for file in modified_files {
232        println!("      - {}", file.path.display());
233    }
234
235    println!("    Deleted files: {}", deleted_files.len());
236    for file in deleted_files {
237        println!("      - {}", file.path.display());
238    }
239    println!();
240
241    println!("=== Diff Operations Demo Complete ===");
242    println!("All diff operations completed successfully!");
243    println!("Summary of tested features:");
244    println!("✓ Basic diff operations (working dir vs index)");
245    println!("✓ Staged diff operations (index vs HEAD)");
246    println!("✓ Diff between specific commits");
247    println!("✓ Diff with various options (name-only, stat, numstat)");
248    println!("✓ Path filtering");
249    println!("✓ Whitespace handling options");
250    println!("✓ File status filtering");
251    println!("✓ Comprehensive diff statistics");
252
253    println!("\nCleaning up temporary repository...");
254    fs::remove_dir_all(&repo_path).ok();
255
256    Ok(())
257}

Trait Implementations§

Source§

impl Clone for Hash

Source§

fn clone(&self) -> Hash

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Hash

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Hash

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<&str> for Hash

Source§

fn from(s: &str) -> Self

Converts to this type from the input type.
Source§

impl From<String> for Hash

Source§

fn from(s: String) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for Hash

Source§

fn eq(&self, other: &Hash) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Eq for Hash

Source§

impl StructuralPartialEq for Hash

Auto Trait Implementations§

§

impl Freeze for Hash

§

impl RefUnwindSafe for Hash

§

impl Send for Hash

§

impl Sync for Hash

§

impl Unpin for Hash

§

impl UnwindSafe for Hash

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.