Repository

Struct Repository 

Source
pub struct Repository { /* private fields */ }

Implementations§

Source§

impl Repository

Source

pub fn add<P: AsRef<Path>>(&self, paths: &[P]) -> Result<()>

Add specific files or paths to the staging area.

§Arguments
  • paths - The file paths to add to the staging area
§Returns

A Result indicating success or a GitError if the operation fails.

Examples found in repository?
examples/error_handling.rs (line 108)
100fn demonstrate_file_operation_errors(repo_path: &std::path::Path) -> Result<()> {
101    println!("File Operation Error Scenarios:\n");
102
103    // Set up a valid repository first
104    let repo = Repository::init(repo_path, false)?;
105
106    // Create some test files
107    fs::write(repo_path.join("test.txt"), "Test content")?;
108    repo.add(&["test.txt"])?;
109    repo.commit("Initial commit")?;
110
111    // 1. Adding non-existent files
112    println!("1. Attempting to add non-existent files:");
113    match repo.add(&["does_not_exist.txt", "also_missing.txt"]) {
114        Ok(_) => println!("   Unexpectedly succeeded"),
115        Err(GitError::CommandFailed(msg)) => {
116            println!("   CommandFailed caught: {}", msg);
117            println!("   Git add failed because files don't exist");
118        }
119        Err(GitError::IoError(msg)) => {
120            println!("   IoError caught: {}", msg);
121        }
122    }
123
124    // 2. Mixed valid and invalid files
125    println!("\n2. Adding mix of valid and invalid files:");
126    fs::write(repo_path.join("valid.txt"), "Valid file")?;
127
128    match repo.add(&["valid.txt", "invalid.txt"]) {
129        Ok(_) => {
130            println!("   Partially succeeded - some Git versions allow this");
131            // Check what actually got staged
132            let status = repo.status()?;
133            println!("   {} files staged despite error", status.entries.len());
134        }
135        Err(GitError::CommandFailed(msg)) => {
136            println!("   CommandFailed caught: {}", msg);
137            println!("   Entire add operation failed due to invalid file");
138
139            // Try recovery: add valid files individually
140            println!("   Recovery: Adding valid files individually...");
141            match repo.add(&["valid.txt"]) {
142                Ok(_) => println!("      Successfully added valid.txt"),
143                Err(e) => println!("      Recovery failed: {:?}", e),
144            }
145        }
146        Err(GitError::IoError(msg)) => {
147            println!("   IoError caught: {}", msg);
148        }
149    }
150
151    println!();
152    Ok(())
153}
154
155/// Demonstrate Git command related errors
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/basic_usage.rs (line 79)
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 18)
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 87)
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/status_checking.rs (line 67)
15fn main() -> Result<()> {
16    println!("Rustic Git - Status Checking Example\n");
17
18    let repo_path = env::temp_dir().join("rustic_git_status_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 status demonstration...");
27    let repo = Repository::init(&repo_path, false)?;
28
29    println!("=== Clean Repository Status ===\n");
30
31    // Check initial status (should be clean)
32    let status = repo.status()?;
33    println!("Initial repository status:");
34    display_status_summary(&status);
35    println!();
36
37    println!("=== Creating Files with Different States ===\n");
38
39    // Create various types of files to demonstrate different statuses
40    println!("Creating test files...");
41
42    // Create some files that will be untracked
43    fs::write(repo_path.join("untracked1.txt"), "This file is untracked")?;
44    fs::write(repo_path.join("untracked2.txt"), "Another untracked file")?;
45
46    // Create a .gitignore to demonstrate ignored files
47    fs::write(repo_path.join(".gitignore"), "*.log\n*.tmp\n/temp/\n")?;
48
49    // Create files that will be ignored
50    fs::write(repo_path.join("debug.log"), "Log file content")?;
51    fs::write(repo_path.join("cache.tmp"), "Temporary file")?;
52    fs::create_dir_all(repo_path.join("temp"))?;
53    fs::write(repo_path.join("temp/data.txt"), "Temp data")?;
54
55    println!("Created test files");
56
57    // Check status after creating untracked files
58    println!("\nStatus after creating untracked files:");
59    let status_untracked = repo.status()?;
60    display_status_summary(&status_untracked);
61    display_detailed_status(&status_untracked);
62    println!();
63
64    println!("=== Staging Files to Show 'Added' Status ===\n");
65
66    // Stage some files to show "Added" status
67    repo.add(&["untracked1.txt", ".gitignore"])?;
68    println!("Staged untracked1.txt and .gitignore");
69
70    let status_added = repo.status()?;
71    println!("\nStatus after staging files:");
72    display_status_summary(&status_added);
73    display_detailed_status(&status_added);
74    println!();
75
76    println!("=== Creating Initial Commit ===\n");
77
78    // Commit the staged files so we can demonstrate modified/deleted states
79    let _hash = repo.commit("Initial commit with basic files")?;
80    println!("Created initial commit");
81
82    let status_after_commit = repo.status()?;
83    println!("\nStatus after commit:");
84    display_status_summary(&status_after_commit);
85    if !status_after_commit.is_clean() {
86        display_detailed_status(&status_after_commit);
87    }
88    println!();
89
90    println!("=== Modifying Files to Show 'Modified' Status ===\n");
91
92    // Modify existing tracked files
93    fs::write(
94        repo_path.join("untracked1.txt"),
95        "This file has been MODIFIED!",
96    )?;
97    fs::write(
98        repo_path.join(".gitignore"),
99        "*.log\n*.tmp\n/temp/\n# Added comment\n",
100    )?;
101    println!("Modified untracked1.txt and .gitignore");
102
103    let status_modified = repo.status()?;
104    println!("\nStatus after modifying files:");
105    display_status_summary(&status_modified);
106    display_detailed_status(&status_modified);
107    println!();
108
109    println!("=== Demonstrating All Status Query Methods ===\n");
110
111    // Stage one of the modified files to show mixed states
112    repo.add(&["untracked1.txt"])?;
113    println!("Staged untracked1.txt (now shows as Added)");
114
115    let status_mixed = repo.status()?;
116    println!("\nMixed status demonstration:");
117    display_status_summary(&status_mixed);
118
119    // Demonstrate different query methods
120    println!("\nUsing different status query methods:");
121
122    println!("   All files ({} total):", status_mixed.entries.len());
123    for entry in &status_mixed.entries {
124        println!(
125            "      Index {:?}, Worktree {:?}: {}",
126            entry.index_status,
127            entry.worktree_status,
128            entry.path.display()
129        );
130    }
131
132    // Query by specific status
133    let unstaged_files: Vec<_> = status_mixed.unstaged_files().collect();
134    if !unstaged_files.is_empty() {
135        println!("\n   Unstaged files ({}):", unstaged_files.len());
136        for entry in &unstaged_files {
137            println!("      - {}", entry.path.display());
138        }
139    }
140
141    let untracked_files: Vec<_> = status_mixed.untracked_entries().collect();
142    if !untracked_files.is_empty() {
143        println!("\n   Untracked files ({}):", untracked_files.len());
144        for entry in &untracked_files {
145            println!("      - {}", entry.path.display());
146        }
147    }
148
149    // Query by IndexStatus enum
150    let added_files: Vec<_> = status_mixed
151        .files_with_index_status(IndexStatus::Added)
152        .collect();
153    if !added_files.is_empty() {
154        println!("\n   Added files ({}):", added_files.len());
155        for entry in &added_files {
156            println!("      - {}", entry.path.display());
157        }
158    }
159
160    println!();
161
162    println!("=== File Status Filtering Examples ===\n");
163
164    // Demonstrate filtering capabilities
165    println!("Filtering examples:");
166
167    // Count files by status
168    let mut index_status_counts = std::collections::HashMap::new();
169    let mut worktree_status_counts = std::collections::HashMap::new();
170
171    for entry in &status_mixed.entries {
172        if !matches!(entry.index_status, IndexStatus::Clean) {
173            *index_status_counts
174                .entry(format!("{:?}", entry.index_status))
175                .or_insert(0) += 1;
176        }
177        if !matches!(entry.worktree_status, WorktreeStatus::Clean) {
178            *worktree_status_counts
179                .entry(format!("{:?}", entry.worktree_status))
180                .or_insert(0) += 1;
181        }
182    }
183
184    println!("   Index status counts:");
185    for (status, count) in &index_status_counts {
186        println!("      {}: {} files", status, count);
187    }
188
189    println!("   Worktree status counts:");
190    for (status, count) in &worktree_status_counts {
191        println!("      {}: {} files", status, count);
192    }
193
194    // Filter for specific patterns
195    let txt_files: Vec<_> = status_mixed
196        .entries
197        .iter()
198        .filter(|entry| entry.path.to_string_lossy().ends_with(".txt"))
199        .collect();
200
201    if !txt_files.is_empty() {
202        println!("\n   .txt files:");
203        for entry in txt_files {
204            println!(
205                "      Index {:?}, Worktree {:?}: {}",
206                entry.index_status,
207                entry.worktree_status,
208                entry.path.display()
209            );
210        }
211    }
212
213    println!();
214
215    println!("=== Repository State Checking ===\n");
216
217    println!("Repository state summary:");
218    println!("   Total files tracked: {}", status_mixed.entries.len());
219    println!("   Is clean: {}", status_mixed.is_clean());
220    println!("   Has changes: {}", status_mixed.has_changes());
221
222    if status_mixed.has_changes() {
223        println!("   Repository needs attention!");
224
225        let unstaged_count = status_mixed.unstaged_files().count();
226        if unstaged_count > 0 {
227            println!("      - {} files need to be staged", unstaged_count);
228        }
229
230        let untracked_count = status_mixed.untracked_entries().count();
231        if untracked_count > 0 {
232            println!("      - {} untracked files to consider", untracked_count);
233        }
234
235        let staged_count = status_mixed.staged_files().count();
236        if staged_count > 0 {
237            println!("      - {} files ready to commit", staged_count);
238        }
239    }
240
241    // Clean up
242    println!("\nCleaning up example repository...");
243    fs::remove_dir_all(&repo_path)?;
244    println!("Status checking example completed!");
245
246    Ok(())
247}
examples/staging_operations.rs (line 79)
14fn main() -> Result<()> {
15    println!("Rustic Git - Staging Operations Example\n");
16
17    let repo_path = env::temp_dir().join("rustic_git_staging_example");
18
19    // Clean up any previous run
20    if repo_path.exists() {
21        fs::remove_dir_all(&repo_path).expect("Failed to clean up previous example");
22    }
23
24    // Initialize repository and create initial commit
25    println!("Setting up repository with initial files...");
26    let repo = Repository::init(&repo_path, false)?;
27
28    // Create initial files
29    fs::create_dir_all(repo_path.join("src"))?;
30    fs::create_dir_all(repo_path.join("docs"))?;
31
32    fs::write(
33        repo_path.join("README.md"),
34        "# Staging Demo\nOriginal content",
35    )?;
36    fs::write(
37        repo_path.join("src/main.rs"),
38        "fn main() { println!(\"v1\"); }",
39    )?;
40    fs::write(
41        repo_path.join("src/lib.rs"),
42        "pub fn version() -> &'static str { \"1.0\" }",
43    )?;
44
45    // Create initial commit so we can demonstrate staging tracked file changes
46    repo.add_all()?;
47    let _initial_hash = repo.commit("Initial commit with basic files")?;
48    println!("Created initial repository with 3 files\n");
49
50    println!("=== Staging Specific Files with add() ===\n");
51
52    // Create some new files and modify existing ones
53    println!("Creating new files and modifying existing ones...");
54    fs::write(repo_path.join("new_file1.txt"), "New file 1 content")?;
55    fs::write(repo_path.join("new_file2.txt"), "New file 2 content")?;
56    fs::write(repo_path.join("docs/guide.md"), "# User Guide")?;
57
58    // Modify existing files
59    fs::write(
60        repo_path.join("README.md"),
61        "# Staging Demo\nUpdated content!",
62    )?;
63    fs::write(
64        repo_path.join("src/main.rs"),
65        "fn main() { println!(\"v2 - updated!\"); }",
66    )?;
67
68    println!("Created 3 new files and modified 2 existing files");
69
70    // Show status before staging
71    println!("\nStatus before staging:");
72    let status_before = repo.status()?;
73    display_status_breakdown(&status_before);
74
75    // Stage specific files using add()
76    println!("\nUsing add() to stage specific files:");
77
78    // Stage just the README.md
79    repo.add(&["README.md"])?;
80    println!("   Staged README.md");
81
82    let status_after_readme = repo.status()?;
83    display_status_changes(
84        &status_before,
85        &status_after_readme,
86        "after staging README.md",
87    );
88
89    // Stage multiple specific files
90    repo.add(&["new_file1.txt", "src/main.rs"])?;
91    println!("   Staged new_file1.txt and src/main.rs");
92
93    let status_after_multiple = repo.status()?;
94    display_status_changes(
95        &status_after_readme,
96        &status_after_multiple,
97        "after staging multiple files",
98    );
99
100    // Stage using Path objects (alternative syntax)
101    use std::path::Path as StdPath;
102    repo.add(&[StdPath::new("docs/guide.md")])?;
103    println!("   Staged docs/guide.md using Path object");
104
105    let status_after_path = repo.status()?;
106    display_status_changes(
107        &status_after_multiple,
108        &status_after_path,
109        "after staging with Path object",
110    );
111
112    println!();
113
114    println!("=== Staging All Changes with add_all() ===\n");
115
116    // Create more files to demonstrate add_all()
117    println!("Creating additional files for add_all() demo...");
118    fs::write(
119        repo_path.join("config.toml"),
120        "[package]\nname = \"example\"",
121    )?;
122    fs::write(repo_path.join("src/utils.rs"), "pub fn helper() {}")?;
123    fs::create_dir_all(repo_path.join("tests"))?;
124    fs::write(
125        repo_path.join("tests/integration.rs"),
126        "#[test]\nfn test_basic() {}",
127    )?;
128
129    println!("Created 3 more files");
130
131    let status_before_add_all = repo.status()?;
132    println!("\nStatus before add_all():");
133    display_status_breakdown(&status_before_add_all);
134
135    // Use add_all() to stage everything remaining
136    println!("\nUsing add_all() to stage all remaining changes:");
137    repo.add_all()?;
138    println!("   Staged all changes with add_all()");
139
140    let status_after_add_all = repo.status()?;
141    display_status_changes(
142        &status_before_add_all,
143        &status_after_add_all,
144        "after add_all()",
145    );
146
147    // Create a commit to set up for add_update() demo
148    let _commit_hash = repo.commit("Add all new files and modifications")?;
149    println!("   Committed all staged changes\n");
150
151    println!("=== Staging Tracked Changes with add_update() ===\n");
152
153    // Create new untracked files and modify existing tracked files
154    println!("Setting up files for add_update() demonstration...");
155
156    // Create new untracked files (these should NOT be staged by add_update)
157    fs::write(repo_path.join("untracked1.txt"), "This is untracked")?;
158    fs::write(repo_path.join("untracked2.txt"), "Another untracked file")?;
159
160    // Modify existing tracked files (these SHOULD be staged by add_update)
161    fs::write(
162        repo_path.join("README.md"),
163        "# Staging Demo\nContent updated again for add_update demo!",
164    )?;
165    fs::write(
166        repo_path.join("src/lib.rs"),
167        "pub fn version() -> &'static str { \"2.0\" }",
168    )?;
169    fs::write(
170        repo_path.join("config.toml"),
171        "[package]\nname = \"example\"\nversion = \"0.2.0\"",
172    )?;
173
174    println!("Created 2 untracked files and modified 3 tracked files");
175
176    let status_before_add_update = repo.status()?;
177    println!("\nStatus before add_update():");
178    display_status_breakdown(&status_before_add_update);
179
180    // Use add_update() to stage only tracked file changes
181    println!("\nUsing add_update() to stage only tracked file modifications:");
182    repo.add_update()?;
183    println!("   Used add_update() - should stage modified tracked files only");
184
185    let status_after_add_update = repo.status()?;
186    display_status_changes(
187        &status_before_add_update,
188        &status_after_add_update,
189        "after add_update()",
190    );
191
192    // Verify that untracked files are still untracked
193    let remaining_untracked: Vec<_> = status_after_add_update.untracked_entries().collect();
194    if !remaining_untracked.is_empty() {
195        println!("   Untracked files remain untracked (as expected):");
196        for entry in remaining_untracked {
197            println!("      - {}", entry.path.display());
198        }
199    }
200
201    println!();
202
203    println!("=== Error Handling in Staging Operations ===\n");
204
205    // Demonstrate error handling
206    println!("Testing error conditions:");
207
208    // Try to add non-existent files
209    match repo.add(&["nonexistent_file.txt"]) {
210        Ok(_) => println!("   Unexpectedly succeeded adding non-existent file"),
211        Err(e) => println!("   Expected error for non-existent file: {:?}", e),
212    }
213
214    // Try to add empty array (should succeed but do nothing)
215    match repo.add(&[] as &[&str]) {
216        Ok(_) => println!("   Empty add() succeeded (no-op)"),
217        Err(e) => println!("   Empty add() failed: {:?}", e),
218    }
219
220    println!();
221
222    println!("=== Final Repository State ===\n");
223
224    let final_status = repo.status()?;
225    println!("Final repository summary:");
226    display_status_breakdown(&final_status);
227
228    if final_status.has_changes() {
229        let staged_count = final_status.staged_files().count();
230        let untracked_count = final_status.untracked_entries().count();
231
232        println!("\nRepository state:");
233        println!("   {} files staged and ready to commit", staged_count);
234        println!("   {} untracked files not yet added", untracked_count);
235
236        if staged_count > 0 {
237            println!("\n   You could now commit with: repo.commit(\"Your message\")?");
238        }
239    }
240
241    // Clean up
242    println!("\nCleaning up example repository...");
243    fs::remove_dir_all(&repo_path)?;
244    println!("Staging operations example completed!");
245
246    Ok(())
247}
Source

pub fn add_all(&self) -> Result<()>

Add all changes to the staging area (equivalent to git add .).

§Returns

A Result indicating success or a GitError if the operation fails.

Examples found in repository?
examples/basic_usage.rs (line 83)
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}
More examples
Hide additional examples
examples/error_handling.rs (line 225)
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}
examples/staging_operations.rs (line 46)
14fn main() -> Result<()> {
15    println!("Rustic Git - Staging Operations Example\n");
16
17    let repo_path = env::temp_dir().join("rustic_git_staging_example");
18
19    // Clean up any previous run
20    if repo_path.exists() {
21        fs::remove_dir_all(&repo_path).expect("Failed to clean up previous example");
22    }
23
24    // Initialize repository and create initial commit
25    println!("Setting up repository with initial files...");
26    let repo = Repository::init(&repo_path, false)?;
27
28    // Create initial files
29    fs::create_dir_all(repo_path.join("src"))?;
30    fs::create_dir_all(repo_path.join("docs"))?;
31
32    fs::write(
33        repo_path.join("README.md"),
34        "# Staging Demo\nOriginal content",
35    )?;
36    fs::write(
37        repo_path.join("src/main.rs"),
38        "fn main() { println!(\"v1\"); }",
39    )?;
40    fs::write(
41        repo_path.join("src/lib.rs"),
42        "pub fn version() -> &'static str { \"1.0\" }",
43    )?;
44
45    // Create initial commit so we can demonstrate staging tracked file changes
46    repo.add_all()?;
47    let _initial_hash = repo.commit("Initial commit with basic files")?;
48    println!("Created initial repository with 3 files\n");
49
50    println!("=== Staging Specific Files with add() ===\n");
51
52    // Create some new files and modify existing ones
53    println!("Creating new files and modifying existing ones...");
54    fs::write(repo_path.join("new_file1.txt"), "New file 1 content")?;
55    fs::write(repo_path.join("new_file2.txt"), "New file 2 content")?;
56    fs::write(repo_path.join("docs/guide.md"), "# User Guide")?;
57
58    // Modify existing files
59    fs::write(
60        repo_path.join("README.md"),
61        "# Staging Demo\nUpdated content!",
62    )?;
63    fs::write(
64        repo_path.join("src/main.rs"),
65        "fn main() { println!(\"v2 - updated!\"); }",
66    )?;
67
68    println!("Created 3 new files and modified 2 existing files");
69
70    // Show status before staging
71    println!("\nStatus before staging:");
72    let status_before = repo.status()?;
73    display_status_breakdown(&status_before);
74
75    // Stage specific files using add()
76    println!("\nUsing add() to stage specific files:");
77
78    // Stage just the README.md
79    repo.add(&["README.md"])?;
80    println!("   Staged README.md");
81
82    let status_after_readme = repo.status()?;
83    display_status_changes(
84        &status_before,
85        &status_after_readme,
86        "after staging README.md",
87    );
88
89    // Stage multiple specific files
90    repo.add(&["new_file1.txt", "src/main.rs"])?;
91    println!("   Staged new_file1.txt and src/main.rs");
92
93    let status_after_multiple = repo.status()?;
94    display_status_changes(
95        &status_after_readme,
96        &status_after_multiple,
97        "after staging multiple files",
98    );
99
100    // Stage using Path objects (alternative syntax)
101    use std::path::Path as StdPath;
102    repo.add(&[StdPath::new("docs/guide.md")])?;
103    println!("   Staged docs/guide.md using Path object");
104
105    let status_after_path = repo.status()?;
106    display_status_changes(
107        &status_after_multiple,
108        &status_after_path,
109        "after staging with Path object",
110    );
111
112    println!();
113
114    println!("=== Staging All Changes with add_all() ===\n");
115
116    // Create more files to demonstrate add_all()
117    println!("Creating additional files for add_all() demo...");
118    fs::write(
119        repo_path.join("config.toml"),
120        "[package]\nname = \"example\"",
121    )?;
122    fs::write(repo_path.join("src/utils.rs"), "pub fn helper() {}")?;
123    fs::create_dir_all(repo_path.join("tests"))?;
124    fs::write(
125        repo_path.join("tests/integration.rs"),
126        "#[test]\nfn test_basic() {}",
127    )?;
128
129    println!("Created 3 more files");
130
131    let status_before_add_all = repo.status()?;
132    println!("\nStatus before add_all():");
133    display_status_breakdown(&status_before_add_all);
134
135    // Use add_all() to stage everything remaining
136    println!("\nUsing add_all() to stage all remaining changes:");
137    repo.add_all()?;
138    println!("   Staged all changes with add_all()");
139
140    let status_after_add_all = repo.status()?;
141    display_status_changes(
142        &status_before_add_all,
143        &status_after_add_all,
144        "after add_all()",
145    );
146
147    // Create a commit to set up for add_update() demo
148    let _commit_hash = repo.commit("Add all new files and modifications")?;
149    println!("   Committed all staged changes\n");
150
151    println!("=== Staging Tracked Changes with add_update() ===\n");
152
153    // Create new untracked files and modify existing tracked files
154    println!("Setting up files for add_update() demonstration...");
155
156    // Create new untracked files (these should NOT be staged by add_update)
157    fs::write(repo_path.join("untracked1.txt"), "This is untracked")?;
158    fs::write(repo_path.join("untracked2.txt"), "Another untracked file")?;
159
160    // Modify existing tracked files (these SHOULD be staged by add_update)
161    fs::write(
162        repo_path.join("README.md"),
163        "# Staging Demo\nContent updated again for add_update demo!",
164    )?;
165    fs::write(
166        repo_path.join("src/lib.rs"),
167        "pub fn version() -> &'static str { \"2.0\" }",
168    )?;
169    fs::write(
170        repo_path.join("config.toml"),
171        "[package]\nname = \"example\"\nversion = \"0.2.0\"",
172    )?;
173
174    println!("Created 2 untracked files and modified 3 tracked files");
175
176    let status_before_add_update = repo.status()?;
177    println!("\nStatus before add_update():");
178    display_status_breakdown(&status_before_add_update);
179
180    // Use add_update() to stage only tracked file changes
181    println!("\nUsing add_update() to stage only tracked file modifications:");
182    repo.add_update()?;
183    println!("   Used add_update() - should stage modified tracked files only");
184
185    let status_after_add_update = repo.status()?;
186    display_status_changes(
187        &status_before_add_update,
188        &status_after_add_update,
189        "after add_update()",
190    );
191
192    // Verify that untracked files are still untracked
193    let remaining_untracked: Vec<_> = status_after_add_update.untracked_entries().collect();
194    if !remaining_untracked.is_empty() {
195        println!("   Untracked files remain untracked (as expected):");
196        for entry in remaining_untracked {
197            println!("      - {}", entry.path.display());
198        }
199    }
200
201    println!();
202
203    println!("=== Error Handling in Staging Operations ===\n");
204
205    // Demonstrate error handling
206    println!("Testing error conditions:");
207
208    // Try to add non-existent files
209    match repo.add(&["nonexistent_file.txt"]) {
210        Ok(_) => println!("   Unexpectedly succeeded adding non-existent file"),
211        Err(e) => println!("   Expected error for non-existent file: {:?}", e),
212    }
213
214    // Try to add empty array (should succeed but do nothing)
215    match repo.add(&[] as &[&str]) {
216        Ok(_) => println!("   Empty add() succeeded (no-op)"),
217        Err(e) => println!("   Empty add() failed: {:?}", e),
218    }
219
220    println!();
221
222    println!("=== Final Repository State ===\n");
223
224    let final_status = repo.status()?;
225    println!("Final repository summary:");
226    display_status_breakdown(&final_status);
227
228    if final_status.has_changes() {
229        let staged_count = final_status.staged_files().count();
230        let untracked_count = final_status.untracked_entries().count();
231
232        println!("\nRepository state:");
233        println!("   {} files staged and ready to commit", staged_count);
234        println!("   {} untracked files not yet added", untracked_count);
235
236        if staged_count > 0 {
237            println!("\n   You could now commit with: repo.commit(\"Your message\")?");
238        }
239    }
240
241    // Clean up
242    println!("\nCleaning up example repository...");
243    fs::remove_dir_all(&repo_path)?;
244    println!("Staging operations example completed!");
245
246    Ok(())
247}
examples/commit_workflows.rs (line 62)
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}
Source

pub fn add_update(&self) -> Result<()>

Add all tracked files that have been modified (equivalent to git add -u).

§Returns

A Result indicating success or a GitError if the operation fails.

Examples found in repository?
examples/staging_operations.rs (line 182)
14fn main() -> Result<()> {
15    println!("Rustic Git - Staging Operations Example\n");
16
17    let repo_path = env::temp_dir().join("rustic_git_staging_example");
18
19    // Clean up any previous run
20    if repo_path.exists() {
21        fs::remove_dir_all(&repo_path).expect("Failed to clean up previous example");
22    }
23
24    // Initialize repository and create initial commit
25    println!("Setting up repository with initial files...");
26    let repo = Repository::init(&repo_path, false)?;
27
28    // Create initial files
29    fs::create_dir_all(repo_path.join("src"))?;
30    fs::create_dir_all(repo_path.join("docs"))?;
31
32    fs::write(
33        repo_path.join("README.md"),
34        "# Staging Demo\nOriginal content",
35    )?;
36    fs::write(
37        repo_path.join("src/main.rs"),
38        "fn main() { println!(\"v1\"); }",
39    )?;
40    fs::write(
41        repo_path.join("src/lib.rs"),
42        "pub fn version() -> &'static str { \"1.0\" }",
43    )?;
44
45    // Create initial commit so we can demonstrate staging tracked file changes
46    repo.add_all()?;
47    let _initial_hash = repo.commit("Initial commit with basic files")?;
48    println!("Created initial repository with 3 files\n");
49
50    println!("=== Staging Specific Files with add() ===\n");
51
52    // Create some new files and modify existing ones
53    println!("Creating new files and modifying existing ones...");
54    fs::write(repo_path.join("new_file1.txt"), "New file 1 content")?;
55    fs::write(repo_path.join("new_file2.txt"), "New file 2 content")?;
56    fs::write(repo_path.join("docs/guide.md"), "# User Guide")?;
57
58    // Modify existing files
59    fs::write(
60        repo_path.join("README.md"),
61        "# Staging Demo\nUpdated content!",
62    )?;
63    fs::write(
64        repo_path.join("src/main.rs"),
65        "fn main() { println!(\"v2 - updated!\"); }",
66    )?;
67
68    println!("Created 3 new files and modified 2 existing files");
69
70    // Show status before staging
71    println!("\nStatus before staging:");
72    let status_before = repo.status()?;
73    display_status_breakdown(&status_before);
74
75    // Stage specific files using add()
76    println!("\nUsing add() to stage specific files:");
77
78    // Stage just the README.md
79    repo.add(&["README.md"])?;
80    println!("   Staged README.md");
81
82    let status_after_readme = repo.status()?;
83    display_status_changes(
84        &status_before,
85        &status_after_readme,
86        "after staging README.md",
87    );
88
89    // Stage multiple specific files
90    repo.add(&["new_file1.txt", "src/main.rs"])?;
91    println!("   Staged new_file1.txt and src/main.rs");
92
93    let status_after_multiple = repo.status()?;
94    display_status_changes(
95        &status_after_readme,
96        &status_after_multiple,
97        "after staging multiple files",
98    );
99
100    // Stage using Path objects (alternative syntax)
101    use std::path::Path as StdPath;
102    repo.add(&[StdPath::new("docs/guide.md")])?;
103    println!("   Staged docs/guide.md using Path object");
104
105    let status_after_path = repo.status()?;
106    display_status_changes(
107        &status_after_multiple,
108        &status_after_path,
109        "after staging with Path object",
110    );
111
112    println!();
113
114    println!("=== Staging All Changes with add_all() ===\n");
115
116    // Create more files to demonstrate add_all()
117    println!("Creating additional files for add_all() demo...");
118    fs::write(
119        repo_path.join("config.toml"),
120        "[package]\nname = \"example\"",
121    )?;
122    fs::write(repo_path.join("src/utils.rs"), "pub fn helper() {}")?;
123    fs::create_dir_all(repo_path.join("tests"))?;
124    fs::write(
125        repo_path.join("tests/integration.rs"),
126        "#[test]\nfn test_basic() {}",
127    )?;
128
129    println!("Created 3 more files");
130
131    let status_before_add_all = repo.status()?;
132    println!("\nStatus before add_all():");
133    display_status_breakdown(&status_before_add_all);
134
135    // Use add_all() to stage everything remaining
136    println!("\nUsing add_all() to stage all remaining changes:");
137    repo.add_all()?;
138    println!("   Staged all changes with add_all()");
139
140    let status_after_add_all = repo.status()?;
141    display_status_changes(
142        &status_before_add_all,
143        &status_after_add_all,
144        "after add_all()",
145    );
146
147    // Create a commit to set up for add_update() demo
148    let _commit_hash = repo.commit("Add all new files and modifications")?;
149    println!("   Committed all staged changes\n");
150
151    println!("=== Staging Tracked Changes with add_update() ===\n");
152
153    // Create new untracked files and modify existing tracked files
154    println!("Setting up files for add_update() demonstration...");
155
156    // Create new untracked files (these should NOT be staged by add_update)
157    fs::write(repo_path.join("untracked1.txt"), "This is untracked")?;
158    fs::write(repo_path.join("untracked2.txt"), "Another untracked file")?;
159
160    // Modify existing tracked files (these SHOULD be staged by add_update)
161    fs::write(
162        repo_path.join("README.md"),
163        "# Staging Demo\nContent updated again for add_update demo!",
164    )?;
165    fs::write(
166        repo_path.join("src/lib.rs"),
167        "pub fn version() -> &'static str { \"2.0\" }",
168    )?;
169    fs::write(
170        repo_path.join("config.toml"),
171        "[package]\nname = \"example\"\nversion = \"0.2.0\"",
172    )?;
173
174    println!("Created 2 untracked files and modified 3 tracked files");
175
176    let status_before_add_update = repo.status()?;
177    println!("\nStatus before add_update():");
178    display_status_breakdown(&status_before_add_update);
179
180    // Use add_update() to stage only tracked file changes
181    println!("\nUsing add_update() to stage only tracked file modifications:");
182    repo.add_update()?;
183    println!("   Used add_update() - should stage modified tracked files only");
184
185    let status_after_add_update = repo.status()?;
186    display_status_changes(
187        &status_before_add_update,
188        &status_after_add_update,
189        "after add_update()",
190    );
191
192    // Verify that untracked files are still untracked
193    let remaining_untracked: Vec<_> = status_after_add_update.untracked_entries().collect();
194    if !remaining_untracked.is_empty() {
195        println!("   Untracked files remain untracked (as expected):");
196        for entry in remaining_untracked {
197            println!("      - {}", entry.path.display());
198        }
199    }
200
201    println!();
202
203    println!("=== Error Handling in Staging Operations ===\n");
204
205    // Demonstrate error handling
206    println!("Testing error conditions:");
207
208    // Try to add non-existent files
209    match repo.add(&["nonexistent_file.txt"]) {
210        Ok(_) => println!("   Unexpectedly succeeded adding non-existent file"),
211        Err(e) => println!("   Expected error for non-existent file: {:?}", e),
212    }
213
214    // Try to add empty array (should succeed but do nothing)
215    match repo.add(&[] as &[&str]) {
216        Ok(_) => println!("   Empty add() succeeded (no-op)"),
217        Err(e) => println!("   Empty add() failed: {:?}", e),
218    }
219
220    println!();
221
222    println!("=== Final Repository State ===\n");
223
224    let final_status = repo.status()?;
225    println!("Final repository summary:");
226    display_status_breakdown(&final_status);
227
228    if final_status.has_changes() {
229        let staged_count = final_status.staged_files().count();
230        let untracked_count = final_status.untracked_entries().count();
231
232        println!("\nRepository state:");
233        println!("   {} files staged and ready to commit", staged_count);
234        println!("   {} untracked files not yet added", untracked_count);
235
236        if staged_count > 0 {
237            println!("\n   You could now commit with: repo.commit(\"Your message\")?");
238        }
239    }
240
241    // Clean up
242    println!("\nCleaning up example repository...");
243    fs::remove_dir_all(&repo_path)?;
244    println!("Staging operations example completed!");
245
246    Ok(())
247}
Source§

impl Repository

Source

pub fn branches(&self) -> Result<BranchList>

List all branches in the repository

Examples found in repository?
examples/branch_operations.rs (line 23)
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}
Source

pub fn current_branch(&self) -> Result<Option<Branch>>

Get the current branch

Examples found in repository?
examples/branch_operations.rs (line 30)
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}
Source

pub fn create_branch( &self, name: &str, start_point: Option<&str>, ) -> Result<Branch>

Create a new branch

Examples found in repository?
examples/branch_operations.rs (line 40)
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}
Source

pub fn delete_branch(&self, branch: &Branch, force: bool) -> Result<()>

Delete a branch

Examples found in repository?
examples/branch_operations.rs (line 129)
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}
Source

pub fn checkout(&self, branch: &Branch) -> Result<()>

Switch to an existing branch

Examples found in repository?
examples/branch_operations.rs (line 75)
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}
Source

pub fn checkout_new( &self, name: &str, start_point: Option<&str>, ) -> Result<Branch>

Create a new branch and switch to it

Examples found in repository?
examples/branch_operations.rs (line 55)
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}
Source§

impl Repository

Source

pub fn commit(&self, message: &str) -> Result<Hash>

Create a commit with the given message.

§Arguments
  • message - The commit message
§Returns

A Result containing the Hash of the new commit or a GitError.

Examples found in repository?
examples/error_handling.rs (line 109)
100fn demonstrate_file_operation_errors(repo_path: &std::path::Path) -> Result<()> {
101    println!("File Operation Error Scenarios:\n");
102
103    // Set up a valid repository first
104    let repo = Repository::init(repo_path, false)?;
105
106    // Create some test files
107    fs::write(repo_path.join("test.txt"), "Test content")?;
108    repo.add(&["test.txt"])?;
109    repo.commit("Initial commit")?;
110
111    // 1. Adding non-existent files
112    println!("1. Attempting to add non-existent files:");
113    match repo.add(&["does_not_exist.txt", "also_missing.txt"]) {
114        Ok(_) => println!("   Unexpectedly succeeded"),
115        Err(GitError::CommandFailed(msg)) => {
116            println!("   CommandFailed caught: {}", msg);
117            println!("   Git add failed because files don't exist");
118        }
119        Err(GitError::IoError(msg)) => {
120            println!("   IoError caught: {}", msg);
121        }
122    }
123
124    // 2. Mixed valid and invalid files
125    println!("\n2. Adding mix of valid and invalid files:");
126    fs::write(repo_path.join("valid.txt"), "Valid file")?;
127
128    match repo.add(&["valid.txt", "invalid.txt"]) {
129        Ok(_) => {
130            println!("   Partially succeeded - some Git versions allow this");
131            // Check what actually got staged
132            let status = repo.status()?;
133            println!("   {} files staged despite error", status.entries.len());
134        }
135        Err(GitError::CommandFailed(msg)) => {
136            println!("   CommandFailed caught: {}", msg);
137            println!("   Entire add operation failed due to invalid file");
138
139            // Try recovery: add valid files individually
140            println!("   Recovery: Adding valid files individually...");
141            match repo.add(&["valid.txt"]) {
142                Ok(_) => println!("      Successfully added valid.txt"),
143                Err(e) => println!("      Recovery failed: {:?}", e),
144            }
145        }
146        Err(GitError::IoError(msg)) => {
147            println!("   IoError caught: {}", msg);
148        }
149    }
150
151    println!();
152    Ok(())
153}
154
155/// Demonstrate Git command related errors
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/basic_usage.rs (line 109)
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 19)
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 91)
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/status_checking.rs (line 79)
15fn main() -> Result<()> {
16    println!("Rustic Git - Status Checking Example\n");
17
18    let repo_path = env::temp_dir().join("rustic_git_status_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 status demonstration...");
27    let repo = Repository::init(&repo_path, false)?;
28
29    println!("=== Clean Repository Status ===\n");
30
31    // Check initial status (should be clean)
32    let status = repo.status()?;
33    println!("Initial repository status:");
34    display_status_summary(&status);
35    println!();
36
37    println!("=== Creating Files with Different States ===\n");
38
39    // Create various types of files to demonstrate different statuses
40    println!("Creating test files...");
41
42    // Create some files that will be untracked
43    fs::write(repo_path.join("untracked1.txt"), "This file is untracked")?;
44    fs::write(repo_path.join("untracked2.txt"), "Another untracked file")?;
45
46    // Create a .gitignore to demonstrate ignored files
47    fs::write(repo_path.join(".gitignore"), "*.log\n*.tmp\n/temp/\n")?;
48
49    // Create files that will be ignored
50    fs::write(repo_path.join("debug.log"), "Log file content")?;
51    fs::write(repo_path.join("cache.tmp"), "Temporary file")?;
52    fs::create_dir_all(repo_path.join("temp"))?;
53    fs::write(repo_path.join("temp/data.txt"), "Temp data")?;
54
55    println!("Created test files");
56
57    // Check status after creating untracked files
58    println!("\nStatus after creating untracked files:");
59    let status_untracked = repo.status()?;
60    display_status_summary(&status_untracked);
61    display_detailed_status(&status_untracked);
62    println!();
63
64    println!("=== Staging Files to Show 'Added' Status ===\n");
65
66    // Stage some files to show "Added" status
67    repo.add(&["untracked1.txt", ".gitignore"])?;
68    println!("Staged untracked1.txt and .gitignore");
69
70    let status_added = repo.status()?;
71    println!("\nStatus after staging files:");
72    display_status_summary(&status_added);
73    display_detailed_status(&status_added);
74    println!();
75
76    println!("=== Creating Initial Commit ===\n");
77
78    // Commit the staged files so we can demonstrate modified/deleted states
79    let _hash = repo.commit("Initial commit with basic files")?;
80    println!("Created initial commit");
81
82    let status_after_commit = repo.status()?;
83    println!("\nStatus after commit:");
84    display_status_summary(&status_after_commit);
85    if !status_after_commit.is_clean() {
86        display_detailed_status(&status_after_commit);
87    }
88    println!();
89
90    println!("=== Modifying Files to Show 'Modified' Status ===\n");
91
92    // Modify existing tracked files
93    fs::write(
94        repo_path.join("untracked1.txt"),
95        "This file has been MODIFIED!",
96    )?;
97    fs::write(
98        repo_path.join(".gitignore"),
99        "*.log\n*.tmp\n/temp/\n# Added comment\n",
100    )?;
101    println!("Modified untracked1.txt and .gitignore");
102
103    let status_modified = repo.status()?;
104    println!("\nStatus after modifying files:");
105    display_status_summary(&status_modified);
106    display_detailed_status(&status_modified);
107    println!();
108
109    println!("=== Demonstrating All Status Query Methods ===\n");
110
111    // Stage one of the modified files to show mixed states
112    repo.add(&["untracked1.txt"])?;
113    println!("Staged untracked1.txt (now shows as Added)");
114
115    let status_mixed = repo.status()?;
116    println!("\nMixed status demonstration:");
117    display_status_summary(&status_mixed);
118
119    // Demonstrate different query methods
120    println!("\nUsing different status query methods:");
121
122    println!("   All files ({} total):", status_mixed.entries.len());
123    for entry in &status_mixed.entries {
124        println!(
125            "      Index {:?}, Worktree {:?}: {}",
126            entry.index_status,
127            entry.worktree_status,
128            entry.path.display()
129        );
130    }
131
132    // Query by specific status
133    let unstaged_files: Vec<_> = status_mixed.unstaged_files().collect();
134    if !unstaged_files.is_empty() {
135        println!("\n   Unstaged files ({}):", unstaged_files.len());
136        for entry in &unstaged_files {
137            println!("      - {}", entry.path.display());
138        }
139    }
140
141    let untracked_files: Vec<_> = status_mixed.untracked_entries().collect();
142    if !untracked_files.is_empty() {
143        println!("\n   Untracked files ({}):", untracked_files.len());
144        for entry in &untracked_files {
145            println!("      - {}", entry.path.display());
146        }
147    }
148
149    // Query by IndexStatus enum
150    let added_files: Vec<_> = status_mixed
151        .files_with_index_status(IndexStatus::Added)
152        .collect();
153    if !added_files.is_empty() {
154        println!("\n   Added files ({}):", added_files.len());
155        for entry in &added_files {
156            println!("      - {}", entry.path.display());
157        }
158    }
159
160    println!();
161
162    println!("=== File Status Filtering Examples ===\n");
163
164    // Demonstrate filtering capabilities
165    println!("Filtering examples:");
166
167    // Count files by status
168    let mut index_status_counts = std::collections::HashMap::new();
169    let mut worktree_status_counts = std::collections::HashMap::new();
170
171    for entry in &status_mixed.entries {
172        if !matches!(entry.index_status, IndexStatus::Clean) {
173            *index_status_counts
174                .entry(format!("{:?}", entry.index_status))
175                .or_insert(0) += 1;
176        }
177        if !matches!(entry.worktree_status, WorktreeStatus::Clean) {
178            *worktree_status_counts
179                .entry(format!("{:?}", entry.worktree_status))
180                .or_insert(0) += 1;
181        }
182    }
183
184    println!("   Index status counts:");
185    for (status, count) in &index_status_counts {
186        println!("      {}: {} files", status, count);
187    }
188
189    println!("   Worktree status counts:");
190    for (status, count) in &worktree_status_counts {
191        println!("      {}: {} files", status, count);
192    }
193
194    // Filter for specific patterns
195    let txt_files: Vec<_> = status_mixed
196        .entries
197        .iter()
198        .filter(|entry| entry.path.to_string_lossy().ends_with(".txt"))
199        .collect();
200
201    if !txt_files.is_empty() {
202        println!("\n   .txt files:");
203        for entry in txt_files {
204            println!(
205                "      Index {:?}, Worktree {:?}: {}",
206                entry.index_status,
207                entry.worktree_status,
208                entry.path.display()
209            );
210        }
211    }
212
213    println!();
214
215    println!("=== Repository State Checking ===\n");
216
217    println!("Repository state summary:");
218    println!("   Total files tracked: {}", status_mixed.entries.len());
219    println!("   Is clean: {}", status_mixed.is_clean());
220    println!("   Has changes: {}", status_mixed.has_changes());
221
222    if status_mixed.has_changes() {
223        println!("   Repository needs attention!");
224
225        let unstaged_count = status_mixed.unstaged_files().count();
226        if unstaged_count > 0 {
227            println!("      - {} files need to be staged", unstaged_count);
228        }
229
230        let untracked_count = status_mixed.untracked_entries().count();
231        if untracked_count > 0 {
232            println!("      - {} untracked files to consider", untracked_count);
233        }
234
235        let staged_count = status_mixed.staged_files().count();
236        if staged_count > 0 {
237            println!("      - {} files ready to commit", staged_count);
238        }
239    }
240
241    // Clean up
242    println!("\nCleaning up example repository...");
243    fs::remove_dir_all(&repo_path)?;
244    println!("Status checking example completed!");
245
246    Ok(())
247}
examples/staging_operations.rs (line 47)
14fn main() -> Result<()> {
15    println!("Rustic Git - Staging Operations Example\n");
16
17    let repo_path = env::temp_dir().join("rustic_git_staging_example");
18
19    // Clean up any previous run
20    if repo_path.exists() {
21        fs::remove_dir_all(&repo_path).expect("Failed to clean up previous example");
22    }
23
24    // Initialize repository and create initial commit
25    println!("Setting up repository with initial files...");
26    let repo = Repository::init(&repo_path, false)?;
27
28    // Create initial files
29    fs::create_dir_all(repo_path.join("src"))?;
30    fs::create_dir_all(repo_path.join("docs"))?;
31
32    fs::write(
33        repo_path.join("README.md"),
34        "# Staging Demo\nOriginal content",
35    )?;
36    fs::write(
37        repo_path.join("src/main.rs"),
38        "fn main() { println!(\"v1\"); }",
39    )?;
40    fs::write(
41        repo_path.join("src/lib.rs"),
42        "pub fn version() -> &'static str { \"1.0\" }",
43    )?;
44
45    // Create initial commit so we can demonstrate staging tracked file changes
46    repo.add_all()?;
47    let _initial_hash = repo.commit("Initial commit with basic files")?;
48    println!("Created initial repository with 3 files\n");
49
50    println!("=== Staging Specific Files with add() ===\n");
51
52    // Create some new files and modify existing ones
53    println!("Creating new files and modifying existing ones...");
54    fs::write(repo_path.join("new_file1.txt"), "New file 1 content")?;
55    fs::write(repo_path.join("new_file2.txt"), "New file 2 content")?;
56    fs::write(repo_path.join("docs/guide.md"), "# User Guide")?;
57
58    // Modify existing files
59    fs::write(
60        repo_path.join("README.md"),
61        "# Staging Demo\nUpdated content!",
62    )?;
63    fs::write(
64        repo_path.join("src/main.rs"),
65        "fn main() { println!(\"v2 - updated!\"); }",
66    )?;
67
68    println!("Created 3 new files and modified 2 existing files");
69
70    // Show status before staging
71    println!("\nStatus before staging:");
72    let status_before = repo.status()?;
73    display_status_breakdown(&status_before);
74
75    // Stage specific files using add()
76    println!("\nUsing add() to stage specific files:");
77
78    // Stage just the README.md
79    repo.add(&["README.md"])?;
80    println!("   Staged README.md");
81
82    let status_after_readme = repo.status()?;
83    display_status_changes(
84        &status_before,
85        &status_after_readme,
86        "after staging README.md",
87    );
88
89    // Stage multiple specific files
90    repo.add(&["new_file1.txt", "src/main.rs"])?;
91    println!("   Staged new_file1.txt and src/main.rs");
92
93    let status_after_multiple = repo.status()?;
94    display_status_changes(
95        &status_after_readme,
96        &status_after_multiple,
97        "after staging multiple files",
98    );
99
100    // Stage using Path objects (alternative syntax)
101    use std::path::Path as StdPath;
102    repo.add(&[StdPath::new("docs/guide.md")])?;
103    println!("   Staged docs/guide.md using Path object");
104
105    let status_after_path = repo.status()?;
106    display_status_changes(
107        &status_after_multiple,
108        &status_after_path,
109        "after staging with Path object",
110    );
111
112    println!();
113
114    println!("=== Staging All Changes with add_all() ===\n");
115
116    // Create more files to demonstrate add_all()
117    println!("Creating additional files for add_all() demo...");
118    fs::write(
119        repo_path.join("config.toml"),
120        "[package]\nname = \"example\"",
121    )?;
122    fs::write(repo_path.join("src/utils.rs"), "pub fn helper() {}")?;
123    fs::create_dir_all(repo_path.join("tests"))?;
124    fs::write(
125        repo_path.join("tests/integration.rs"),
126        "#[test]\nfn test_basic() {}",
127    )?;
128
129    println!("Created 3 more files");
130
131    let status_before_add_all = repo.status()?;
132    println!("\nStatus before add_all():");
133    display_status_breakdown(&status_before_add_all);
134
135    // Use add_all() to stage everything remaining
136    println!("\nUsing add_all() to stage all remaining changes:");
137    repo.add_all()?;
138    println!("   Staged all changes with add_all()");
139
140    let status_after_add_all = repo.status()?;
141    display_status_changes(
142        &status_before_add_all,
143        &status_after_add_all,
144        "after add_all()",
145    );
146
147    // Create a commit to set up for add_update() demo
148    let _commit_hash = repo.commit("Add all new files and modifications")?;
149    println!("   Committed all staged changes\n");
150
151    println!("=== Staging Tracked Changes with add_update() ===\n");
152
153    // Create new untracked files and modify existing tracked files
154    println!("Setting up files for add_update() demonstration...");
155
156    // Create new untracked files (these should NOT be staged by add_update)
157    fs::write(repo_path.join("untracked1.txt"), "This is untracked")?;
158    fs::write(repo_path.join("untracked2.txt"), "Another untracked file")?;
159
160    // Modify existing tracked files (these SHOULD be staged by add_update)
161    fs::write(
162        repo_path.join("README.md"),
163        "# Staging Demo\nContent updated again for add_update demo!",
164    )?;
165    fs::write(
166        repo_path.join("src/lib.rs"),
167        "pub fn version() -> &'static str { \"2.0\" }",
168    )?;
169    fs::write(
170        repo_path.join("config.toml"),
171        "[package]\nname = \"example\"\nversion = \"0.2.0\"",
172    )?;
173
174    println!("Created 2 untracked files and modified 3 tracked files");
175
176    let status_before_add_update = repo.status()?;
177    println!("\nStatus before add_update():");
178    display_status_breakdown(&status_before_add_update);
179
180    // Use add_update() to stage only tracked file changes
181    println!("\nUsing add_update() to stage only tracked file modifications:");
182    repo.add_update()?;
183    println!("   Used add_update() - should stage modified tracked files only");
184
185    let status_after_add_update = repo.status()?;
186    display_status_changes(
187        &status_before_add_update,
188        &status_after_add_update,
189        "after add_update()",
190    );
191
192    // Verify that untracked files are still untracked
193    let remaining_untracked: Vec<_> = status_after_add_update.untracked_entries().collect();
194    if !remaining_untracked.is_empty() {
195        println!("   Untracked files remain untracked (as expected):");
196        for entry in remaining_untracked {
197            println!("      - {}", entry.path.display());
198        }
199    }
200
201    println!();
202
203    println!("=== Error Handling in Staging Operations ===\n");
204
205    // Demonstrate error handling
206    println!("Testing error conditions:");
207
208    // Try to add non-existent files
209    match repo.add(&["nonexistent_file.txt"]) {
210        Ok(_) => println!("   Unexpectedly succeeded adding non-existent file"),
211        Err(e) => println!("   Expected error for non-existent file: {:?}", e),
212    }
213
214    // Try to add empty array (should succeed but do nothing)
215    match repo.add(&[] as &[&str]) {
216        Ok(_) => println!("   Empty add() succeeded (no-op)"),
217        Err(e) => println!("   Empty add() failed: {:?}", e),
218    }
219
220    println!();
221
222    println!("=== Final Repository State ===\n");
223
224    let final_status = repo.status()?;
225    println!("Final repository summary:");
226    display_status_breakdown(&final_status);
227
228    if final_status.has_changes() {
229        let staged_count = final_status.staged_files().count();
230        let untracked_count = final_status.untracked_entries().count();
231
232        println!("\nRepository state:");
233        println!("   {} files staged and ready to commit", staged_count);
234        println!("   {} untracked files not yet added", untracked_count);
235
236        if staged_count > 0 {
237            println!("\n   You could now commit with: repo.commit(\"Your message\")?");
238        }
239    }
240
241    // Clean up
242    println!("\nCleaning up example repository...");
243    fs::remove_dir_all(&repo_path)?;
244    println!("Staging operations example completed!");
245
246    Ok(())
247}
Source

pub fn commit_with_author(&self, message: &str, author: &str) -> Result<Hash>

Create a commit with the given message and author.

§Arguments
  • message - The commit message
  • author - The author in format “Name email@example.com
§Returns

A Result containing the Hash of the new commit or a GitError.

Examples found in repository?
examples/commit_workflows.rs (lines 151-154)
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}
Source§

impl Repository

Source

pub fn log(&self) -> Result<CommitLog>

Get commit history with default options

Examples found in repository?
examples/commit_history.rs (line 82)
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 recent_commits(&self, count: usize) -> Result<CommitLog>

Get recent N commits

Examples found in repository?
examples/commit_history.rs (line 92)
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 log_with_options(&self, options: &LogOptions) -> Result<CommitLog>

Get commit history with custom options

Examples found in repository?
examples/commit_history.rs (line 121)
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 log_range(&self, from: &Hash, to: &Hash) -> Result<CommitLog>

Get commits in a range between two commits

Examples found in repository?
examples/commit_history.rs (line 206)
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 log_for_paths(&self, paths: &[impl AsRef<Path>]) -> Result<CommitLog>

Get commits that affected specific paths

Examples found in repository?
examples/commit_history.rs (line 129)
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 show_commit(&self, hash: &Hash) -> Result<CommitDetails>

Get detailed information about a specific commit

Examples found in repository?
examples/commit_history.rs (line 138)
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§

impl Repository

Source

pub fn status(&self) -> Result<GitStatus>

Get the status of the repository.

§Returns

A Result containing the GitStatus or a GitError.

Examples found in repository?
examples/error_handling.rs (line 132)
100fn demonstrate_file_operation_errors(repo_path: &std::path::Path) -> Result<()> {
101    println!("File Operation Error Scenarios:\n");
102
103    // Set up a valid repository first
104    let repo = Repository::init(repo_path, false)?;
105
106    // Create some test files
107    fs::write(repo_path.join("test.txt"), "Test content")?;
108    repo.add(&["test.txt"])?;
109    repo.commit("Initial commit")?;
110
111    // 1. Adding non-existent files
112    println!("1. Attempting to add non-existent files:");
113    match repo.add(&["does_not_exist.txt", "also_missing.txt"]) {
114        Ok(_) => println!("   Unexpectedly succeeded"),
115        Err(GitError::CommandFailed(msg)) => {
116            println!("   CommandFailed caught: {}", msg);
117            println!("   Git add failed because files don't exist");
118        }
119        Err(GitError::IoError(msg)) => {
120            println!("   IoError caught: {}", msg);
121        }
122    }
123
124    // 2. Mixed valid and invalid files
125    println!("\n2. Adding mix of valid and invalid files:");
126    fs::write(repo_path.join("valid.txt"), "Valid file")?;
127
128    match repo.add(&["valid.txt", "invalid.txt"]) {
129        Ok(_) => {
130            println!("   Partially succeeded - some Git versions allow this");
131            // Check what actually got staged
132            let status = repo.status()?;
133            println!("   {} files staged despite error", status.entries.len());
134        }
135        Err(GitError::CommandFailed(msg)) => {
136            println!("   CommandFailed caught: {}", msg);
137            println!("   Entire add operation failed due to invalid file");
138
139            // Try recovery: add valid files individually
140            println!("   Recovery: Adding valid files individually...");
141            match repo.add(&["valid.txt"]) {
142                Ok(_) => println!("      Successfully added valid.txt"),
143                Err(e) => println!("      Recovery failed: {:?}", e),
144            }
145        }
146        Err(GitError::IoError(msg)) => {
147            println!("   IoError caught: {}", msg);
148        }
149    }
150
151    println!();
152    Ok(())
153}
154
155/// Demonstrate Git command related errors
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}
More examples
Hide additional examples
examples/basic_usage.rs (line 59)
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/repository_operations.rs (line 71)
15fn main() -> Result<()> {
16    println!("Rustic Git - Repository Operations Example\n");
17
18    let base_path = env::temp_dir().join("rustic_git_repo_example");
19    let regular_repo_path = base_path.join("regular");
20    let bare_repo_path = base_path.join("bare");
21    let nonexistent_path = base_path.join("nonexistent");
22
23    // Clean up any previous runs
24    if base_path.exists() {
25        fs::remove_dir_all(&base_path).expect("Failed to clean up previous example");
26    }
27    fs::create_dir_all(&base_path)?;
28
29    println!("=== Repository Initialization ===\n");
30
31    // 1. Initialize a regular repository
32    println!("Initializing regular repository...");
33    let regular_repo = Repository::init(&regular_repo_path, false)?;
34    println!(
35        "Regular repository created at: {}",
36        regular_repo_path.display()
37    );
38    println!("   Repository path: {:?}", regular_repo.repo_path());
39
40    // Verify it's a git repo by checking for .git directory
41    if regular_repo_path.join(".git").exists() {
42        println!("   .git directory found");
43    }
44    println!();
45
46    // 2. Initialize a bare repository
47    println!("Initializing bare repository...");
48    let bare_repo = Repository::init(&bare_repo_path, true)?;
49    println!("Bare repository created at: {}", bare_repo_path.display());
50    println!("   Repository path: {:?}", bare_repo.repo_path());
51
52    // Verify bare repo structure (has HEAD, objects, etc. directly)
53    if bare_repo_path.join("HEAD").exists() {
54        println!("   HEAD file found (bare repository structure)");
55    }
56    if bare_repo_path.join("objects").exists() {
57        println!("   objects directory found");
58    }
59    println!();
60
61    println!("=== Repository Opening ===\n");
62
63    // 3. Open the existing regular repository
64    println!("Opening existing regular repository...");
65    match Repository::open(&regular_repo_path) {
66        Ok(opened_repo) => {
67            println!("Successfully opened regular repository");
68            println!("   Repository path: {:?}", opened_repo.repo_path());
69
70            // Test that we can perform operations on the opened repo
71            let status = opened_repo.status()?;
72            println!("   Repository status: {} files", status.entries.len());
73        }
74        Err(e) => {
75            println!("Failed to open regular repository: {:?}", e);
76        }
77    }
78    println!();
79
80    // 4. Open the existing bare repository
81    println!("Opening existing bare repository...");
82    match Repository::open(&bare_repo_path) {
83        Ok(opened_bare) => {
84            println!("Successfully opened bare repository");
85            println!("   Repository path: {:?}", opened_bare.repo_path());
86
87            // Note: status operations might behave differently on bare repos
88            match opened_bare.status() {
89                Ok(status) => println!("   Bare repository status: {} files", status.entries.len()),
90                Err(e) => println!(
91                    "   Note: Status check on bare repo failed (expected): {:?}",
92                    e
93                ),
94            }
95        }
96        Err(e) => {
97            println!("Failed to open bare repository: {:?}", e);
98        }
99    }
100    println!();
101
102    println!("=== Error Handling ===\n");
103
104    // 5. Try to open a non-existent repository
105    println!("Attempting to open non-existent repository...");
106    match Repository::open(&nonexistent_path) {
107        Ok(_repo) => {
108            println!("Unexpectedly succeeded opening non-existent repo");
109        }
110        Err(GitError::CommandFailed(msg)) => {
111            println!("Expected error caught: CommandFailed");
112            println!("   Error message: {}", msg);
113        }
114        Err(GitError::IoError(msg)) => {
115            println!("Expected error caught: IoError");
116            println!("   Error message: {}", msg);
117        }
118    }
119    println!();
120
121    // 6. Try to open a regular file as a repository
122    let fake_repo_path = base_path.join("fake.txt");
123    fs::write(&fake_repo_path, "This is not a git repository")?;
124
125    println!("Attempting to open regular file as repository...");
126    match Repository::open(&fake_repo_path) {
127        Ok(_repo) => {
128            println!("Unexpectedly succeeded opening regular file as repo");
129        }
130        Err(GitError::CommandFailed(msg)) => {
131            println!("Expected error caught: CommandFailed");
132            println!("   Error message: {}", msg);
133        }
134        Err(GitError::IoError(msg)) => {
135            println!("Expected error caught: IoError");
136            println!("   Error message: {}", msg);
137        }
138    }
139    println!();
140
141    println!("=== Repository Information ===\n");
142
143    // 7. Compare regular vs bare repository information
144    println!("Comparing repository types:");
145
146    let regular_path = regular_repo.repo_path();
147    let bare_path = bare_repo.repo_path();
148
149    println!("   Regular repo path: {:?}", regular_path);
150    println!("   Bare repo path: {:?}", bare_path);
151
152    // Show directory contents
153    if let Ok(entries) = fs::read_dir(regular_path) {
154        let mut files: Vec<_> = entries.filter_map(|e| e.ok()).collect();
155        files.sort_by_key(|e| e.file_name());
156
157        println!("   Regular repo contents:");
158        for entry in files {
159            if let Some(name) = entry.file_name().to_str() {
160                let is_dir = entry.file_type().map(|t| t.is_dir()).unwrap_or(false);
161                let marker = if is_dir { "[DIR]" } else { "[FILE]" };
162                println!("     {} {}", marker, name);
163            }
164        }
165    }
166
167    if let Ok(entries) = fs::read_dir(bare_path) {
168        let mut files: Vec<_> = entries.filter_map(|e| e.ok()).collect();
169        files.sort_by_key(|e| e.file_name());
170
171        println!("   Bare repo contents:");
172        for entry in files {
173            if let Some(name) = entry.file_name().to_str() {
174                let is_dir = entry.file_type().map(|t| t.is_dir()).unwrap_or(false);
175                let marker = if is_dir { "[DIR]" } else { "[FILE]" };
176                println!("     {} {}", marker, name);
177            }
178        }
179    }
180    println!();
181
182    // Clean up
183    println!("Cleaning up example repositories...");
184    fs::remove_dir_all(&base_path)?;
185    println!("Repository operations example completed!");
186
187    Ok(())
188}
examples/status_checking.rs (line 32)
15fn main() -> Result<()> {
16    println!("Rustic Git - Status Checking Example\n");
17
18    let repo_path = env::temp_dir().join("rustic_git_status_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 status demonstration...");
27    let repo = Repository::init(&repo_path, false)?;
28
29    println!("=== Clean Repository Status ===\n");
30
31    // Check initial status (should be clean)
32    let status = repo.status()?;
33    println!("Initial repository status:");
34    display_status_summary(&status);
35    println!();
36
37    println!("=== Creating Files with Different States ===\n");
38
39    // Create various types of files to demonstrate different statuses
40    println!("Creating test files...");
41
42    // Create some files that will be untracked
43    fs::write(repo_path.join("untracked1.txt"), "This file is untracked")?;
44    fs::write(repo_path.join("untracked2.txt"), "Another untracked file")?;
45
46    // Create a .gitignore to demonstrate ignored files
47    fs::write(repo_path.join(".gitignore"), "*.log\n*.tmp\n/temp/\n")?;
48
49    // Create files that will be ignored
50    fs::write(repo_path.join("debug.log"), "Log file content")?;
51    fs::write(repo_path.join("cache.tmp"), "Temporary file")?;
52    fs::create_dir_all(repo_path.join("temp"))?;
53    fs::write(repo_path.join("temp/data.txt"), "Temp data")?;
54
55    println!("Created test files");
56
57    // Check status after creating untracked files
58    println!("\nStatus after creating untracked files:");
59    let status_untracked = repo.status()?;
60    display_status_summary(&status_untracked);
61    display_detailed_status(&status_untracked);
62    println!();
63
64    println!("=== Staging Files to Show 'Added' Status ===\n");
65
66    // Stage some files to show "Added" status
67    repo.add(&["untracked1.txt", ".gitignore"])?;
68    println!("Staged untracked1.txt and .gitignore");
69
70    let status_added = repo.status()?;
71    println!("\nStatus after staging files:");
72    display_status_summary(&status_added);
73    display_detailed_status(&status_added);
74    println!();
75
76    println!("=== Creating Initial Commit ===\n");
77
78    // Commit the staged files so we can demonstrate modified/deleted states
79    let _hash = repo.commit("Initial commit with basic files")?;
80    println!("Created initial commit");
81
82    let status_after_commit = repo.status()?;
83    println!("\nStatus after commit:");
84    display_status_summary(&status_after_commit);
85    if !status_after_commit.is_clean() {
86        display_detailed_status(&status_after_commit);
87    }
88    println!();
89
90    println!("=== Modifying Files to Show 'Modified' Status ===\n");
91
92    // Modify existing tracked files
93    fs::write(
94        repo_path.join("untracked1.txt"),
95        "This file has been MODIFIED!",
96    )?;
97    fs::write(
98        repo_path.join(".gitignore"),
99        "*.log\n*.tmp\n/temp/\n# Added comment\n",
100    )?;
101    println!("Modified untracked1.txt and .gitignore");
102
103    let status_modified = repo.status()?;
104    println!("\nStatus after modifying files:");
105    display_status_summary(&status_modified);
106    display_detailed_status(&status_modified);
107    println!();
108
109    println!("=== Demonstrating All Status Query Methods ===\n");
110
111    // Stage one of the modified files to show mixed states
112    repo.add(&["untracked1.txt"])?;
113    println!("Staged untracked1.txt (now shows as Added)");
114
115    let status_mixed = repo.status()?;
116    println!("\nMixed status demonstration:");
117    display_status_summary(&status_mixed);
118
119    // Demonstrate different query methods
120    println!("\nUsing different status query methods:");
121
122    println!("   All files ({} total):", status_mixed.entries.len());
123    for entry in &status_mixed.entries {
124        println!(
125            "      Index {:?}, Worktree {:?}: {}",
126            entry.index_status,
127            entry.worktree_status,
128            entry.path.display()
129        );
130    }
131
132    // Query by specific status
133    let unstaged_files: Vec<_> = status_mixed.unstaged_files().collect();
134    if !unstaged_files.is_empty() {
135        println!("\n   Unstaged files ({}):", unstaged_files.len());
136        for entry in &unstaged_files {
137            println!("      - {}", entry.path.display());
138        }
139    }
140
141    let untracked_files: Vec<_> = status_mixed.untracked_entries().collect();
142    if !untracked_files.is_empty() {
143        println!("\n   Untracked files ({}):", untracked_files.len());
144        for entry in &untracked_files {
145            println!("      - {}", entry.path.display());
146        }
147    }
148
149    // Query by IndexStatus enum
150    let added_files: Vec<_> = status_mixed
151        .files_with_index_status(IndexStatus::Added)
152        .collect();
153    if !added_files.is_empty() {
154        println!("\n   Added files ({}):", added_files.len());
155        for entry in &added_files {
156            println!("      - {}", entry.path.display());
157        }
158    }
159
160    println!();
161
162    println!("=== File Status Filtering Examples ===\n");
163
164    // Demonstrate filtering capabilities
165    println!("Filtering examples:");
166
167    // Count files by status
168    let mut index_status_counts = std::collections::HashMap::new();
169    let mut worktree_status_counts = std::collections::HashMap::new();
170
171    for entry in &status_mixed.entries {
172        if !matches!(entry.index_status, IndexStatus::Clean) {
173            *index_status_counts
174                .entry(format!("{:?}", entry.index_status))
175                .or_insert(0) += 1;
176        }
177        if !matches!(entry.worktree_status, WorktreeStatus::Clean) {
178            *worktree_status_counts
179                .entry(format!("{:?}", entry.worktree_status))
180                .or_insert(0) += 1;
181        }
182    }
183
184    println!("   Index status counts:");
185    for (status, count) in &index_status_counts {
186        println!("      {}: {} files", status, count);
187    }
188
189    println!("   Worktree status counts:");
190    for (status, count) in &worktree_status_counts {
191        println!("      {}: {} files", status, count);
192    }
193
194    // Filter for specific patterns
195    let txt_files: Vec<_> = status_mixed
196        .entries
197        .iter()
198        .filter(|entry| entry.path.to_string_lossy().ends_with(".txt"))
199        .collect();
200
201    if !txt_files.is_empty() {
202        println!("\n   .txt files:");
203        for entry in txt_files {
204            println!(
205                "      Index {:?}, Worktree {:?}: {}",
206                entry.index_status,
207                entry.worktree_status,
208                entry.path.display()
209            );
210        }
211    }
212
213    println!();
214
215    println!("=== Repository State Checking ===\n");
216
217    println!("Repository state summary:");
218    println!("   Total files tracked: {}", status_mixed.entries.len());
219    println!("   Is clean: {}", status_mixed.is_clean());
220    println!("   Has changes: {}", status_mixed.has_changes());
221
222    if status_mixed.has_changes() {
223        println!("   Repository needs attention!");
224
225        let unstaged_count = status_mixed.unstaged_files().count();
226        if unstaged_count > 0 {
227            println!("      - {} files need to be staged", unstaged_count);
228        }
229
230        let untracked_count = status_mixed.untracked_entries().count();
231        if untracked_count > 0 {
232            println!("      - {} untracked files to consider", untracked_count);
233        }
234
235        let staged_count = status_mixed.staged_files().count();
236        if staged_count > 0 {
237            println!("      - {} files ready to commit", staged_count);
238        }
239    }
240
241    // Clean up
242    println!("\nCleaning up example repository...");
243    fs::remove_dir_all(&repo_path)?;
244    println!("Status checking example completed!");
245
246    Ok(())
247}
examples/staging_operations.rs (line 72)
14fn main() -> Result<()> {
15    println!("Rustic Git - Staging Operations Example\n");
16
17    let repo_path = env::temp_dir().join("rustic_git_staging_example");
18
19    // Clean up any previous run
20    if repo_path.exists() {
21        fs::remove_dir_all(&repo_path).expect("Failed to clean up previous example");
22    }
23
24    // Initialize repository and create initial commit
25    println!("Setting up repository with initial files...");
26    let repo = Repository::init(&repo_path, false)?;
27
28    // Create initial files
29    fs::create_dir_all(repo_path.join("src"))?;
30    fs::create_dir_all(repo_path.join("docs"))?;
31
32    fs::write(
33        repo_path.join("README.md"),
34        "# Staging Demo\nOriginal content",
35    )?;
36    fs::write(
37        repo_path.join("src/main.rs"),
38        "fn main() { println!(\"v1\"); }",
39    )?;
40    fs::write(
41        repo_path.join("src/lib.rs"),
42        "pub fn version() -> &'static str { \"1.0\" }",
43    )?;
44
45    // Create initial commit so we can demonstrate staging tracked file changes
46    repo.add_all()?;
47    let _initial_hash = repo.commit("Initial commit with basic files")?;
48    println!("Created initial repository with 3 files\n");
49
50    println!("=== Staging Specific Files with add() ===\n");
51
52    // Create some new files and modify existing ones
53    println!("Creating new files and modifying existing ones...");
54    fs::write(repo_path.join("new_file1.txt"), "New file 1 content")?;
55    fs::write(repo_path.join("new_file2.txt"), "New file 2 content")?;
56    fs::write(repo_path.join("docs/guide.md"), "# User Guide")?;
57
58    // Modify existing files
59    fs::write(
60        repo_path.join("README.md"),
61        "# Staging Demo\nUpdated content!",
62    )?;
63    fs::write(
64        repo_path.join("src/main.rs"),
65        "fn main() { println!(\"v2 - updated!\"); }",
66    )?;
67
68    println!("Created 3 new files and modified 2 existing files");
69
70    // Show status before staging
71    println!("\nStatus before staging:");
72    let status_before = repo.status()?;
73    display_status_breakdown(&status_before);
74
75    // Stage specific files using add()
76    println!("\nUsing add() to stage specific files:");
77
78    // Stage just the README.md
79    repo.add(&["README.md"])?;
80    println!("   Staged README.md");
81
82    let status_after_readme = repo.status()?;
83    display_status_changes(
84        &status_before,
85        &status_after_readme,
86        "after staging README.md",
87    );
88
89    // Stage multiple specific files
90    repo.add(&["new_file1.txt", "src/main.rs"])?;
91    println!("   Staged new_file1.txt and src/main.rs");
92
93    let status_after_multiple = repo.status()?;
94    display_status_changes(
95        &status_after_readme,
96        &status_after_multiple,
97        "after staging multiple files",
98    );
99
100    // Stage using Path objects (alternative syntax)
101    use std::path::Path as StdPath;
102    repo.add(&[StdPath::new("docs/guide.md")])?;
103    println!("   Staged docs/guide.md using Path object");
104
105    let status_after_path = repo.status()?;
106    display_status_changes(
107        &status_after_multiple,
108        &status_after_path,
109        "after staging with Path object",
110    );
111
112    println!();
113
114    println!("=== Staging All Changes with add_all() ===\n");
115
116    // Create more files to demonstrate add_all()
117    println!("Creating additional files for add_all() demo...");
118    fs::write(
119        repo_path.join("config.toml"),
120        "[package]\nname = \"example\"",
121    )?;
122    fs::write(repo_path.join("src/utils.rs"), "pub fn helper() {}")?;
123    fs::create_dir_all(repo_path.join("tests"))?;
124    fs::write(
125        repo_path.join("tests/integration.rs"),
126        "#[test]\nfn test_basic() {}",
127    )?;
128
129    println!("Created 3 more files");
130
131    let status_before_add_all = repo.status()?;
132    println!("\nStatus before add_all():");
133    display_status_breakdown(&status_before_add_all);
134
135    // Use add_all() to stage everything remaining
136    println!("\nUsing add_all() to stage all remaining changes:");
137    repo.add_all()?;
138    println!("   Staged all changes with add_all()");
139
140    let status_after_add_all = repo.status()?;
141    display_status_changes(
142        &status_before_add_all,
143        &status_after_add_all,
144        "after add_all()",
145    );
146
147    // Create a commit to set up for add_update() demo
148    let _commit_hash = repo.commit("Add all new files and modifications")?;
149    println!("   Committed all staged changes\n");
150
151    println!("=== Staging Tracked Changes with add_update() ===\n");
152
153    // Create new untracked files and modify existing tracked files
154    println!("Setting up files for add_update() demonstration...");
155
156    // Create new untracked files (these should NOT be staged by add_update)
157    fs::write(repo_path.join("untracked1.txt"), "This is untracked")?;
158    fs::write(repo_path.join("untracked2.txt"), "Another untracked file")?;
159
160    // Modify existing tracked files (these SHOULD be staged by add_update)
161    fs::write(
162        repo_path.join("README.md"),
163        "# Staging Demo\nContent updated again for add_update demo!",
164    )?;
165    fs::write(
166        repo_path.join("src/lib.rs"),
167        "pub fn version() -> &'static str { \"2.0\" }",
168    )?;
169    fs::write(
170        repo_path.join("config.toml"),
171        "[package]\nname = \"example\"\nversion = \"0.2.0\"",
172    )?;
173
174    println!("Created 2 untracked files and modified 3 tracked files");
175
176    let status_before_add_update = repo.status()?;
177    println!("\nStatus before add_update():");
178    display_status_breakdown(&status_before_add_update);
179
180    // Use add_update() to stage only tracked file changes
181    println!("\nUsing add_update() to stage only tracked file modifications:");
182    repo.add_update()?;
183    println!("   Used add_update() - should stage modified tracked files only");
184
185    let status_after_add_update = repo.status()?;
186    display_status_changes(
187        &status_before_add_update,
188        &status_after_add_update,
189        "after add_update()",
190    );
191
192    // Verify that untracked files are still untracked
193    let remaining_untracked: Vec<_> = status_after_add_update.untracked_entries().collect();
194    if !remaining_untracked.is_empty() {
195        println!("   Untracked files remain untracked (as expected):");
196        for entry in remaining_untracked {
197            println!("      - {}", entry.path.display());
198        }
199    }
200
201    println!();
202
203    println!("=== Error Handling in Staging Operations ===\n");
204
205    // Demonstrate error handling
206    println!("Testing error conditions:");
207
208    // Try to add non-existent files
209    match repo.add(&["nonexistent_file.txt"]) {
210        Ok(_) => println!("   Unexpectedly succeeded adding non-existent file"),
211        Err(e) => println!("   Expected error for non-existent file: {:?}", e),
212    }
213
214    // Try to add empty array (should succeed but do nothing)
215    match repo.add(&[] as &[&str]) {
216        Ok(_) => println!("   Empty add() succeeded (no-op)"),
217        Err(e) => println!("   Empty add() failed: {:?}", e),
218    }
219
220    println!();
221
222    println!("=== Final Repository State ===\n");
223
224    let final_status = repo.status()?;
225    println!("Final repository summary:");
226    display_status_breakdown(&final_status);
227
228    if final_status.has_changes() {
229        let staged_count = final_status.staged_files().count();
230        let untracked_count = final_status.untracked_entries().count();
231
232        println!("\nRepository state:");
233        println!("   {} files staged and ready to commit", staged_count);
234        println!("   {} untracked files not yet added", untracked_count);
235
236        if staged_count > 0 {
237            println!("\n   You could now commit with: repo.commit(\"Your message\")?");
238        }
239    }
240
241    // Clean up
242    println!("\nCleaning up example repository...");
243    fs::remove_dir_all(&repo_path)?;
244    println!("Staging operations example completed!");
245
246    Ok(())
247}
examples/commit_workflows.rs (line 317)
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}
Source§

impl Repository

Source

pub fn ensure_git() -> Result<()>

Ensure that Git is available in the system PATH.

This function checks if the git command is available in the system PATH. The result is cached, so subsequent calls are very fast. If Git is not found, it returns a GitError::CommandFailed with an appropriate error message.

§Returns

A Result containing either Ok(()) if Git is available or a GitError.

Source

pub fn open<P: AsRef<Path>>(path: P) -> Result<Self>

Open an existing Git repository at the specified path.

§Arguments
  • path - The path to an existing Git repository.
§Returns

A Result containing either the opened Repository instance or a GitError.

Examples found in repository?
examples/error_handling.rs (line 49)
44fn demonstrate_repository_errors(repo_path: &std::path::Path) -> Result<()> {
45    println!("Repository Error Scenarios:\n");
46
47    // 1. Opening non-existent repository
48    println!("1. Attempting to open non-existent repository:");
49    match Repository::open("/definitely/does/not/exist") {
50        Ok(_) => println!("   Unexpectedly succeeded"),
51        Err(GitError::IoError(msg)) => {
52            println!("   IoError caught: {}", msg);
53            println!("   This typically happens when the path doesn't exist");
54        }
55        Err(GitError::CommandFailed(msg)) => {
56            println!("   CommandFailed caught: {}", msg);
57            println!("   Git command failed - path exists but isn't a repo");
58        }
59    }
60
61    // 2. Opening a file as a repository
62    let fake_repo_path = repo_path.with_extension("fake.txt");
63    fs::write(&fake_repo_path, "This is not a git repository")?;
64
65    println!("\n2. Attempting to open regular file as repository:");
66    match Repository::open(&fake_repo_path) {
67        Ok(_) => println!("   Unexpectedly succeeded"),
68        Err(GitError::CommandFailed(msg)) => {
69            println!("   CommandFailed caught: {}", msg);
70            println!("   Git recognized the path but it's not a repository");
71        }
72        Err(GitError::IoError(msg)) => {
73            println!("   IoError caught: {}", msg);
74        }
75    }
76
77    fs::remove_file(&fake_repo_path)?;
78
79    // 3. Initializing repository with invalid path
80    println!("\n3. Attempting to initialize repository with problematic path:");
81
82    // Try to initialize in a location that might cause issues
83    match Repository::init("/root/definitely_no_permission", false) {
84        Ok(_) => println!("   Unexpectedly succeeded (you might be running as root!)"),
85        Err(GitError::IoError(msg)) => {
86            println!("   IoError caught: {}", msg);
87            println!("   Likely a permission issue");
88        }
89        Err(GitError::CommandFailed(msg)) => {
90            println!("   CommandFailed caught: {}", msg);
91            println!("   Git init command failed");
92        }
93    }
94
95    println!();
96    Ok(())
97}
98
99/// Demonstrate file operation related errors
100fn demonstrate_file_operation_errors(repo_path: &std::path::Path) -> Result<()> {
101    println!("File Operation Error Scenarios:\n");
102
103    // Set up a valid repository first
104    let repo = Repository::init(repo_path, false)?;
105
106    // Create some test files
107    fs::write(repo_path.join("test.txt"), "Test content")?;
108    repo.add(&["test.txt"])?;
109    repo.commit("Initial commit")?;
110
111    // 1. Adding non-existent files
112    println!("1. Attempting to add non-existent files:");
113    match repo.add(&["does_not_exist.txt", "also_missing.txt"]) {
114        Ok(_) => println!("   Unexpectedly succeeded"),
115        Err(GitError::CommandFailed(msg)) => {
116            println!("   CommandFailed caught: {}", msg);
117            println!("   Git add failed because files don't exist");
118        }
119        Err(GitError::IoError(msg)) => {
120            println!("   IoError caught: {}", msg);
121        }
122    }
123
124    // 2. Mixed valid and invalid files
125    println!("\n2. Adding mix of valid and invalid files:");
126    fs::write(repo_path.join("valid.txt"), "Valid file")?;
127
128    match repo.add(&["valid.txt", "invalid.txt"]) {
129        Ok(_) => {
130            println!("   Partially succeeded - some Git versions allow this");
131            // Check what actually got staged
132            let status = repo.status()?;
133            println!("   {} files staged despite error", status.entries.len());
134        }
135        Err(GitError::CommandFailed(msg)) => {
136            println!("   CommandFailed caught: {}", msg);
137            println!("   Entire add operation failed due to invalid file");
138
139            // Try recovery: add valid files individually
140            println!("   Recovery: Adding valid files individually...");
141            match repo.add(&["valid.txt"]) {
142                Ok(_) => println!("      Successfully added valid.txt"),
143                Err(e) => println!("      Recovery failed: {:?}", e),
144            }
145        }
146        Err(GitError::IoError(msg)) => {
147            println!("   IoError caught: {}", msg);
148        }
149    }
150
151    println!();
152    Ok(())
153}
154
155/// Demonstrate Git command related errors
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}
More examples
Hide additional examples
examples/repository_operations.rs (line 65)
15fn main() -> Result<()> {
16    println!("Rustic Git - Repository Operations Example\n");
17
18    let base_path = env::temp_dir().join("rustic_git_repo_example");
19    let regular_repo_path = base_path.join("regular");
20    let bare_repo_path = base_path.join("bare");
21    let nonexistent_path = base_path.join("nonexistent");
22
23    // Clean up any previous runs
24    if base_path.exists() {
25        fs::remove_dir_all(&base_path).expect("Failed to clean up previous example");
26    }
27    fs::create_dir_all(&base_path)?;
28
29    println!("=== Repository Initialization ===\n");
30
31    // 1. Initialize a regular repository
32    println!("Initializing regular repository...");
33    let regular_repo = Repository::init(&regular_repo_path, false)?;
34    println!(
35        "Regular repository created at: {}",
36        regular_repo_path.display()
37    );
38    println!("   Repository path: {:?}", regular_repo.repo_path());
39
40    // Verify it's a git repo by checking for .git directory
41    if regular_repo_path.join(".git").exists() {
42        println!("   .git directory found");
43    }
44    println!();
45
46    // 2. Initialize a bare repository
47    println!("Initializing bare repository...");
48    let bare_repo = Repository::init(&bare_repo_path, true)?;
49    println!("Bare repository created at: {}", bare_repo_path.display());
50    println!("   Repository path: {:?}", bare_repo.repo_path());
51
52    // Verify bare repo structure (has HEAD, objects, etc. directly)
53    if bare_repo_path.join("HEAD").exists() {
54        println!("   HEAD file found (bare repository structure)");
55    }
56    if bare_repo_path.join("objects").exists() {
57        println!("   objects directory found");
58    }
59    println!();
60
61    println!("=== Repository Opening ===\n");
62
63    // 3. Open the existing regular repository
64    println!("Opening existing regular repository...");
65    match Repository::open(&regular_repo_path) {
66        Ok(opened_repo) => {
67            println!("Successfully opened regular repository");
68            println!("   Repository path: {:?}", opened_repo.repo_path());
69
70            // Test that we can perform operations on the opened repo
71            let status = opened_repo.status()?;
72            println!("   Repository status: {} files", status.entries.len());
73        }
74        Err(e) => {
75            println!("Failed to open regular repository: {:?}", e);
76        }
77    }
78    println!();
79
80    // 4. Open the existing bare repository
81    println!("Opening existing bare repository...");
82    match Repository::open(&bare_repo_path) {
83        Ok(opened_bare) => {
84            println!("Successfully opened bare repository");
85            println!("   Repository path: {:?}", opened_bare.repo_path());
86
87            // Note: status operations might behave differently on bare repos
88            match opened_bare.status() {
89                Ok(status) => println!("   Bare repository status: {} files", status.entries.len()),
90                Err(e) => println!(
91                    "   Note: Status check on bare repo failed (expected): {:?}",
92                    e
93                ),
94            }
95        }
96        Err(e) => {
97            println!("Failed to open bare repository: {:?}", e);
98        }
99    }
100    println!();
101
102    println!("=== Error Handling ===\n");
103
104    // 5. Try to open a non-existent repository
105    println!("Attempting to open non-existent repository...");
106    match Repository::open(&nonexistent_path) {
107        Ok(_repo) => {
108            println!("Unexpectedly succeeded opening non-existent repo");
109        }
110        Err(GitError::CommandFailed(msg)) => {
111            println!("Expected error caught: CommandFailed");
112            println!("   Error message: {}", msg);
113        }
114        Err(GitError::IoError(msg)) => {
115            println!("Expected error caught: IoError");
116            println!("   Error message: {}", msg);
117        }
118    }
119    println!();
120
121    // 6. Try to open a regular file as a repository
122    let fake_repo_path = base_path.join("fake.txt");
123    fs::write(&fake_repo_path, "This is not a git repository")?;
124
125    println!("Attempting to open regular file as repository...");
126    match Repository::open(&fake_repo_path) {
127        Ok(_repo) => {
128            println!("Unexpectedly succeeded opening regular file as repo");
129        }
130        Err(GitError::CommandFailed(msg)) => {
131            println!("Expected error caught: CommandFailed");
132            println!("   Error message: {}", msg);
133        }
134        Err(GitError::IoError(msg)) => {
135            println!("Expected error caught: IoError");
136            println!("   Error message: {}", msg);
137        }
138    }
139    println!();
140
141    println!("=== Repository Information ===\n");
142
143    // 7. Compare regular vs bare repository information
144    println!("Comparing repository types:");
145
146    let regular_path = regular_repo.repo_path();
147    let bare_path = bare_repo.repo_path();
148
149    println!("   Regular repo path: {:?}", regular_path);
150    println!("   Bare repo path: {:?}", bare_path);
151
152    // Show directory contents
153    if let Ok(entries) = fs::read_dir(regular_path) {
154        let mut files: Vec<_> = entries.filter_map(|e| e.ok()).collect();
155        files.sort_by_key(|e| e.file_name());
156
157        println!("   Regular repo contents:");
158        for entry in files {
159            if let Some(name) = entry.file_name().to_str() {
160                let is_dir = entry.file_type().map(|t| t.is_dir()).unwrap_or(false);
161                let marker = if is_dir { "[DIR]" } else { "[FILE]" };
162                println!("     {} {}", marker, name);
163            }
164        }
165    }
166
167    if let Ok(entries) = fs::read_dir(bare_path) {
168        let mut files: Vec<_> = entries.filter_map(|e| e.ok()).collect();
169        files.sort_by_key(|e| e.file_name());
170
171        println!("   Bare repo contents:");
172        for entry in files {
173            if let Some(name) = entry.file_name().to_str() {
174                let is_dir = entry.file_type().map(|t| t.is_dir()).unwrap_or(false);
175                let marker = if is_dir { "[DIR]" } else { "[FILE]" };
176                println!("     {} {}", marker, name);
177            }
178        }
179    }
180    println!();
181
182    // Clean up
183    println!("Cleaning up example repositories...");
184    fs::remove_dir_all(&base_path)?;
185    println!("Repository operations example completed!");
186
187    Ok(())
188}
Source

pub fn init<P: AsRef<Path>>(path: P, bare: bool) -> Result<Self>

Initialize a new Git repository at the specified path.

§Arguments
  • path - The path where the repository should be initialized.
  • bare - Whether the repository should be bare or not.
§Returns

A Result containing either the initialized Repository instance or a GitError.

Examples found in repository?
examples/error_handling.rs (line 83)
44fn demonstrate_repository_errors(repo_path: &std::path::Path) -> Result<()> {
45    println!("Repository Error Scenarios:\n");
46
47    // 1. Opening non-existent repository
48    println!("1. Attempting to open non-existent repository:");
49    match Repository::open("/definitely/does/not/exist") {
50        Ok(_) => println!("   Unexpectedly succeeded"),
51        Err(GitError::IoError(msg)) => {
52            println!("   IoError caught: {}", msg);
53            println!("   This typically happens when the path doesn't exist");
54        }
55        Err(GitError::CommandFailed(msg)) => {
56            println!("   CommandFailed caught: {}", msg);
57            println!("   Git command failed - path exists but isn't a repo");
58        }
59    }
60
61    // 2. Opening a file as a repository
62    let fake_repo_path = repo_path.with_extension("fake.txt");
63    fs::write(&fake_repo_path, "This is not a git repository")?;
64
65    println!("\n2. Attempting to open regular file as repository:");
66    match Repository::open(&fake_repo_path) {
67        Ok(_) => println!("   Unexpectedly succeeded"),
68        Err(GitError::CommandFailed(msg)) => {
69            println!("   CommandFailed caught: {}", msg);
70            println!("   Git recognized the path but it's not a repository");
71        }
72        Err(GitError::IoError(msg)) => {
73            println!("   IoError caught: {}", msg);
74        }
75    }
76
77    fs::remove_file(&fake_repo_path)?;
78
79    // 3. Initializing repository with invalid path
80    println!("\n3. Attempting to initialize repository with problematic path:");
81
82    // Try to initialize in a location that might cause issues
83    match Repository::init("/root/definitely_no_permission", false) {
84        Ok(_) => println!("   Unexpectedly succeeded (you might be running as root!)"),
85        Err(GitError::IoError(msg)) => {
86            println!("   IoError caught: {}", msg);
87            println!("   Likely a permission issue");
88        }
89        Err(GitError::CommandFailed(msg)) => {
90            println!("   CommandFailed caught: {}", msg);
91            println!("   Git init command failed");
92        }
93    }
94
95    println!();
96    Ok(())
97}
98
99/// Demonstrate file operation related errors
100fn demonstrate_file_operation_errors(repo_path: &std::path::Path) -> Result<()> {
101    println!("File Operation Error Scenarios:\n");
102
103    // Set up a valid repository first
104    let repo = Repository::init(repo_path, false)?;
105
106    // Create some test files
107    fs::write(repo_path.join("test.txt"), "Test content")?;
108    repo.add(&["test.txt"])?;
109    repo.commit("Initial commit")?;
110
111    // 1. Adding non-existent files
112    println!("1. Attempting to add non-existent files:");
113    match repo.add(&["does_not_exist.txt", "also_missing.txt"]) {
114        Ok(_) => println!("   Unexpectedly succeeded"),
115        Err(GitError::CommandFailed(msg)) => {
116            println!("   CommandFailed caught: {}", msg);
117            println!("   Git add failed because files don't exist");
118        }
119        Err(GitError::IoError(msg)) => {
120            println!("   IoError caught: {}", msg);
121        }
122    }
123
124    // 2. Mixed valid and invalid files
125    println!("\n2. Adding mix of valid and invalid files:");
126    fs::write(repo_path.join("valid.txt"), "Valid file")?;
127
128    match repo.add(&["valid.txt", "invalid.txt"]) {
129        Ok(_) => {
130            println!("   Partially succeeded - some Git versions allow this");
131            // Check what actually got staged
132            let status = repo.status()?;
133            println!("   {} files staged despite error", status.entries.len());
134        }
135        Err(GitError::CommandFailed(msg)) => {
136            println!("   CommandFailed caught: {}", msg);
137            println!("   Entire add operation failed due to invalid file");
138
139            // Try recovery: add valid files individually
140            println!("   Recovery: Adding valid files individually...");
141            match repo.add(&["valid.txt"]) {
142                Ok(_) => println!("      Successfully added valid.txt"),
143                Err(e) => println!("      Recovery failed: {:?}", e),
144            }
145        }
146        Err(GitError::IoError(msg)) => {
147            println!("   IoError caught: {}", msg);
148        }
149    }
150
151    println!();
152    Ok(())
153}
154
155/// Demonstrate Git command related errors
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/basic_usage.rs (line 30)
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 13)
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/repository_operations.rs (line 33)
15fn main() -> Result<()> {
16    println!("Rustic Git - Repository Operations Example\n");
17
18    let base_path = env::temp_dir().join("rustic_git_repo_example");
19    let regular_repo_path = base_path.join("regular");
20    let bare_repo_path = base_path.join("bare");
21    let nonexistent_path = base_path.join("nonexistent");
22
23    // Clean up any previous runs
24    if base_path.exists() {
25        fs::remove_dir_all(&base_path).expect("Failed to clean up previous example");
26    }
27    fs::create_dir_all(&base_path)?;
28
29    println!("=== Repository Initialization ===\n");
30
31    // 1. Initialize a regular repository
32    println!("Initializing regular repository...");
33    let regular_repo = Repository::init(&regular_repo_path, false)?;
34    println!(
35        "Regular repository created at: {}",
36        regular_repo_path.display()
37    );
38    println!("   Repository path: {:?}", regular_repo.repo_path());
39
40    // Verify it's a git repo by checking for .git directory
41    if regular_repo_path.join(".git").exists() {
42        println!("   .git directory found");
43    }
44    println!();
45
46    // 2. Initialize a bare repository
47    println!("Initializing bare repository...");
48    let bare_repo = Repository::init(&bare_repo_path, true)?;
49    println!("Bare repository created at: {}", bare_repo_path.display());
50    println!("   Repository path: {:?}", bare_repo.repo_path());
51
52    // Verify bare repo structure (has HEAD, objects, etc. directly)
53    if bare_repo_path.join("HEAD").exists() {
54        println!("   HEAD file found (bare repository structure)");
55    }
56    if bare_repo_path.join("objects").exists() {
57        println!("   objects directory found");
58    }
59    println!();
60
61    println!("=== Repository Opening ===\n");
62
63    // 3. Open the existing regular repository
64    println!("Opening existing regular repository...");
65    match Repository::open(&regular_repo_path) {
66        Ok(opened_repo) => {
67            println!("Successfully opened regular repository");
68            println!("   Repository path: {:?}", opened_repo.repo_path());
69
70            // Test that we can perform operations on the opened repo
71            let status = opened_repo.status()?;
72            println!("   Repository status: {} files", status.entries.len());
73        }
74        Err(e) => {
75            println!("Failed to open regular repository: {:?}", e);
76        }
77    }
78    println!();
79
80    // 4. Open the existing bare repository
81    println!("Opening existing bare repository...");
82    match Repository::open(&bare_repo_path) {
83        Ok(opened_bare) => {
84            println!("Successfully opened bare repository");
85            println!("   Repository path: {:?}", opened_bare.repo_path());
86
87            // Note: status operations might behave differently on bare repos
88            match opened_bare.status() {
89                Ok(status) => println!("   Bare repository status: {} files", status.entries.len()),
90                Err(e) => println!(
91                    "   Note: Status check on bare repo failed (expected): {:?}",
92                    e
93                ),
94            }
95        }
96        Err(e) => {
97            println!("Failed to open bare repository: {:?}", e);
98        }
99    }
100    println!();
101
102    println!("=== Error Handling ===\n");
103
104    // 5. Try to open a non-existent repository
105    println!("Attempting to open non-existent repository...");
106    match Repository::open(&nonexistent_path) {
107        Ok(_repo) => {
108            println!("Unexpectedly succeeded opening non-existent repo");
109        }
110        Err(GitError::CommandFailed(msg)) => {
111            println!("Expected error caught: CommandFailed");
112            println!("   Error message: {}", msg);
113        }
114        Err(GitError::IoError(msg)) => {
115            println!("Expected error caught: IoError");
116            println!("   Error message: {}", msg);
117        }
118    }
119    println!();
120
121    // 6. Try to open a regular file as a repository
122    let fake_repo_path = base_path.join("fake.txt");
123    fs::write(&fake_repo_path, "This is not a git repository")?;
124
125    println!("Attempting to open regular file as repository...");
126    match Repository::open(&fake_repo_path) {
127        Ok(_repo) => {
128            println!("Unexpectedly succeeded opening regular file as repo");
129        }
130        Err(GitError::CommandFailed(msg)) => {
131            println!("Expected error caught: CommandFailed");
132            println!("   Error message: {}", msg);
133        }
134        Err(GitError::IoError(msg)) => {
135            println!("Expected error caught: IoError");
136            println!("   Error message: {}", msg);
137        }
138    }
139    println!();
140
141    println!("=== Repository Information ===\n");
142
143    // 7. Compare regular vs bare repository information
144    println!("Comparing repository types:");
145
146    let regular_path = regular_repo.repo_path();
147    let bare_path = bare_repo.repo_path();
148
149    println!("   Regular repo path: {:?}", regular_path);
150    println!("   Bare repo path: {:?}", bare_path);
151
152    // Show directory contents
153    if let Ok(entries) = fs::read_dir(regular_path) {
154        let mut files: Vec<_> = entries.filter_map(|e| e.ok()).collect();
155        files.sort_by_key(|e| e.file_name());
156
157        println!("   Regular repo contents:");
158        for entry in files {
159            if let Some(name) = entry.file_name().to_str() {
160                let is_dir = entry.file_type().map(|t| t.is_dir()).unwrap_or(false);
161                let marker = if is_dir { "[DIR]" } else { "[FILE]" };
162                println!("     {} {}", marker, name);
163            }
164        }
165    }
166
167    if let Ok(entries) = fs::read_dir(bare_path) {
168        let mut files: Vec<_> = entries.filter_map(|e| e.ok()).collect();
169        files.sort_by_key(|e| e.file_name());
170
171        println!("   Bare repo contents:");
172        for entry in files {
173            if let Some(name) = entry.file_name().to_str() {
174                let is_dir = entry.file_type().map(|t| t.is_dir()).unwrap_or(false);
175                let marker = if is_dir { "[DIR]" } else { "[FILE]" };
176                println!("     {} {}", marker, name);
177            }
178        }
179    }
180    println!();
181
182    // Clean up
183    println!("Cleaning up example repositories...");
184    fs::remove_dir_all(&base_path)?;
185    println!("Repository operations example completed!");
186
187    Ok(())
188}
examples/config_operations.rs (line 28)
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/status_checking.rs (line 27)
15fn main() -> Result<()> {
16    println!("Rustic Git - Status Checking Example\n");
17
18    let repo_path = env::temp_dir().join("rustic_git_status_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 status demonstration...");
27    let repo = Repository::init(&repo_path, false)?;
28
29    println!("=== Clean Repository Status ===\n");
30
31    // Check initial status (should be clean)
32    let status = repo.status()?;
33    println!("Initial repository status:");
34    display_status_summary(&status);
35    println!();
36
37    println!("=== Creating Files with Different States ===\n");
38
39    // Create various types of files to demonstrate different statuses
40    println!("Creating test files...");
41
42    // Create some files that will be untracked
43    fs::write(repo_path.join("untracked1.txt"), "This file is untracked")?;
44    fs::write(repo_path.join("untracked2.txt"), "Another untracked file")?;
45
46    // Create a .gitignore to demonstrate ignored files
47    fs::write(repo_path.join(".gitignore"), "*.log\n*.tmp\n/temp/\n")?;
48
49    // Create files that will be ignored
50    fs::write(repo_path.join("debug.log"), "Log file content")?;
51    fs::write(repo_path.join("cache.tmp"), "Temporary file")?;
52    fs::create_dir_all(repo_path.join("temp"))?;
53    fs::write(repo_path.join("temp/data.txt"), "Temp data")?;
54
55    println!("Created test files");
56
57    // Check status after creating untracked files
58    println!("\nStatus after creating untracked files:");
59    let status_untracked = repo.status()?;
60    display_status_summary(&status_untracked);
61    display_detailed_status(&status_untracked);
62    println!();
63
64    println!("=== Staging Files to Show 'Added' Status ===\n");
65
66    // Stage some files to show "Added" status
67    repo.add(&["untracked1.txt", ".gitignore"])?;
68    println!("Staged untracked1.txt and .gitignore");
69
70    let status_added = repo.status()?;
71    println!("\nStatus after staging files:");
72    display_status_summary(&status_added);
73    display_detailed_status(&status_added);
74    println!();
75
76    println!("=== Creating Initial Commit ===\n");
77
78    // Commit the staged files so we can demonstrate modified/deleted states
79    let _hash = repo.commit("Initial commit with basic files")?;
80    println!("Created initial commit");
81
82    let status_after_commit = repo.status()?;
83    println!("\nStatus after commit:");
84    display_status_summary(&status_after_commit);
85    if !status_after_commit.is_clean() {
86        display_detailed_status(&status_after_commit);
87    }
88    println!();
89
90    println!("=== Modifying Files to Show 'Modified' Status ===\n");
91
92    // Modify existing tracked files
93    fs::write(
94        repo_path.join("untracked1.txt"),
95        "This file has been MODIFIED!",
96    )?;
97    fs::write(
98        repo_path.join(".gitignore"),
99        "*.log\n*.tmp\n/temp/\n# Added comment\n",
100    )?;
101    println!("Modified untracked1.txt and .gitignore");
102
103    let status_modified = repo.status()?;
104    println!("\nStatus after modifying files:");
105    display_status_summary(&status_modified);
106    display_detailed_status(&status_modified);
107    println!();
108
109    println!("=== Demonstrating All Status Query Methods ===\n");
110
111    // Stage one of the modified files to show mixed states
112    repo.add(&["untracked1.txt"])?;
113    println!("Staged untracked1.txt (now shows as Added)");
114
115    let status_mixed = repo.status()?;
116    println!("\nMixed status demonstration:");
117    display_status_summary(&status_mixed);
118
119    // Demonstrate different query methods
120    println!("\nUsing different status query methods:");
121
122    println!("   All files ({} total):", status_mixed.entries.len());
123    for entry in &status_mixed.entries {
124        println!(
125            "      Index {:?}, Worktree {:?}: {}",
126            entry.index_status,
127            entry.worktree_status,
128            entry.path.display()
129        );
130    }
131
132    // Query by specific status
133    let unstaged_files: Vec<_> = status_mixed.unstaged_files().collect();
134    if !unstaged_files.is_empty() {
135        println!("\n   Unstaged files ({}):", unstaged_files.len());
136        for entry in &unstaged_files {
137            println!("      - {}", entry.path.display());
138        }
139    }
140
141    let untracked_files: Vec<_> = status_mixed.untracked_entries().collect();
142    if !untracked_files.is_empty() {
143        println!("\n   Untracked files ({}):", untracked_files.len());
144        for entry in &untracked_files {
145            println!("      - {}", entry.path.display());
146        }
147    }
148
149    // Query by IndexStatus enum
150    let added_files: Vec<_> = status_mixed
151        .files_with_index_status(IndexStatus::Added)
152        .collect();
153    if !added_files.is_empty() {
154        println!("\n   Added files ({}):", added_files.len());
155        for entry in &added_files {
156            println!("      - {}", entry.path.display());
157        }
158    }
159
160    println!();
161
162    println!("=== File Status Filtering Examples ===\n");
163
164    // Demonstrate filtering capabilities
165    println!("Filtering examples:");
166
167    // Count files by status
168    let mut index_status_counts = std::collections::HashMap::new();
169    let mut worktree_status_counts = std::collections::HashMap::new();
170
171    for entry in &status_mixed.entries {
172        if !matches!(entry.index_status, IndexStatus::Clean) {
173            *index_status_counts
174                .entry(format!("{:?}", entry.index_status))
175                .or_insert(0) += 1;
176        }
177        if !matches!(entry.worktree_status, WorktreeStatus::Clean) {
178            *worktree_status_counts
179                .entry(format!("{:?}", entry.worktree_status))
180                .or_insert(0) += 1;
181        }
182    }
183
184    println!("   Index status counts:");
185    for (status, count) in &index_status_counts {
186        println!("      {}: {} files", status, count);
187    }
188
189    println!("   Worktree status counts:");
190    for (status, count) in &worktree_status_counts {
191        println!("      {}: {} files", status, count);
192    }
193
194    // Filter for specific patterns
195    let txt_files: Vec<_> = status_mixed
196        .entries
197        .iter()
198        .filter(|entry| entry.path.to_string_lossy().ends_with(".txt"))
199        .collect();
200
201    if !txt_files.is_empty() {
202        println!("\n   .txt files:");
203        for entry in txt_files {
204            println!(
205                "      Index {:?}, Worktree {:?}: {}",
206                entry.index_status,
207                entry.worktree_status,
208                entry.path.display()
209            );
210        }
211    }
212
213    println!();
214
215    println!("=== Repository State Checking ===\n");
216
217    println!("Repository state summary:");
218    println!("   Total files tracked: {}", status_mixed.entries.len());
219    println!("   Is clean: {}", status_mixed.is_clean());
220    println!("   Has changes: {}", status_mixed.has_changes());
221
222    if status_mixed.has_changes() {
223        println!("   Repository needs attention!");
224
225        let unstaged_count = status_mixed.unstaged_files().count();
226        if unstaged_count > 0 {
227            println!("      - {} files need to be staged", unstaged_count);
228        }
229
230        let untracked_count = status_mixed.untracked_entries().count();
231        if untracked_count > 0 {
232            println!("      - {} untracked files to consider", untracked_count);
233        }
234
235        let staged_count = status_mixed.staged_files().count();
236        if staged_count > 0 {
237            println!("      - {} files ready to commit", staged_count);
238        }
239    }
240
241    // Clean up
242    println!("\nCleaning up example repository...");
243    fs::remove_dir_all(&repo_path)?;
244    println!("Status checking example completed!");
245
246    Ok(())
247}
Source

pub fn repo_path(&self) -> &Path

Examples found in repository?
examples/repository_operations.rs (line 38)
15fn main() -> Result<()> {
16    println!("Rustic Git - Repository Operations Example\n");
17
18    let base_path = env::temp_dir().join("rustic_git_repo_example");
19    let regular_repo_path = base_path.join("regular");
20    let bare_repo_path = base_path.join("bare");
21    let nonexistent_path = base_path.join("nonexistent");
22
23    // Clean up any previous runs
24    if base_path.exists() {
25        fs::remove_dir_all(&base_path).expect("Failed to clean up previous example");
26    }
27    fs::create_dir_all(&base_path)?;
28
29    println!("=== Repository Initialization ===\n");
30
31    // 1. Initialize a regular repository
32    println!("Initializing regular repository...");
33    let regular_repo = Repository::init(&regular_repo_path, false)?;
34    println!(
35        "Regular repository created at: {}",
36        regular_repo_path.display()
37    );
38    println!("   Repository path: {:?}", regular_repo.repo_path());
39
40    // Verify it's a git repo by checking for .git directory
41    if regular_repo_path.join(".git").exists() {
42        println!("   .git directory found");
43    }
44    println!();
45
46    // 2. Initialize a bare repository
47    println!("Initializing bare repository...");
48    let bare_repo = Repository::init(&bare_repo_path, true)?;
49    println!("Bare repository created at: {}", bare_repo_path.display());
50    println!("   Repository path: {:?}", bare_repo.repo_path());
51
52    // Verify bare repo structure (has HEAD, objects, etc. directly)
53    if bare_repo_path.join("HEAD").exists() {
54        println!("   HEAD file found (bare repository structure)");
55    }
56    if bare_repo_path.join("objects").exists() {
57        println!("   objects directory found");
58    }
59    println!();
60
61    println!("=== Repository Opening ===\n");
62
63    // 3. Open the existing regular repository
64    println!("Opening existing regular repository...");
65    match Repository::open(&regular_repo_path) {
66        Ok(opened_repo) => {
67            println!("Successfully opened regular repository");
68            println!("   Repository path: {:?}", opened_repo.repo_path());
69
70            // Test that we can perform operations on the opened repo
71            let status = opened_repo.status()?;
72            println!("   Repository status: {} files", status.entries.len());
73        }
74        Err(e) => {
75            println!("Failed to open regular repository: {:?}", e);
76        }
77    }
78    println!();
79
80    // 4. Open the existing bare repository
81    println!("Opening existing bare repository...");
82    match Repository::open(&bare_repo_path) {
83        Ok(opened_bare) => {
84            println!("Successfully opened bare repository");
85            println!("   Repository path: {:?}", opened_bare.repo_path());
86
87            // Note: status operations might behave differently on bare repos
88            match opened_bare.status() {
89                Ok(status) => println!("   Bare repository status: {} files", status.entries.len()),
90                Err(e) => println!(
91                    "   Note: Status check on bare repo failed (expected): {:?}",
92                    e
93                ),
94            }
95        }
96        Err(e) => {
97            println!("Failed to open bare repository: {:?}", e);
98        }
99    }
100    println!();
101
102    println!("=== Error Handling ===\n");
103
104    // 5. Try to open a non-existent repository
105    println!("Attempting to open non-existent repository...");
106    match Repository::open(&nonexistent_path) {
107        Ok(_repo) => {
108            println!("Unexpectedly succeeded opening non-existent repo");
109        }
110        Err(GitError::CommandFailed(msg)) => {
111            println!("Expected error caught: CommandFailed");
112            println!("   Error message: {}", msg);
113        }
114        Err(GitError::IoError(msg)) => {
115            println!("Expected error caught: IoError");
116            println!("   Error message: {}", msg);
117        }
118    }
119    println!();
120
121    // 6. Try to open a regular file as a repository
122    let fake_repo_path = base_path.join("fake.txt");
123    fs::write(&fake_repo_path, "This is not a git repository")?;
124
125    println!("Attempting to open regular file as repository...");
126    match Repository::open(&fake_repo_path) {
127        Ok(_repo) => {
128            println!("Unexpectedly succeeded opening regular file as repo");
129        }
130        Err(GitError::CommandFailed(msg)) => {
131            println!("Expected error caught: CommandFailed");
132            println!("   Error message: {}", msg);
133        }
134        Err(GitError::IoError(msg)) => {
135            println!("Expected error caught: IoError");
136            println!("   Error message: {}", msg);
137        }
138    }
139    println!();
140
141    println!("=== Repository Information ===\n");
142
143    // 7. Compare regular vs bare repository information
144    println!("Comparing repository types:");
145
146    let regular_path = regular_repo.repo_path();
147    let bare_path = bare_repo.repo_path();
148
149    println!("   Regular repo path: {:?}", regular_path);
150    println!("   Bare repo path: {:?}", bare_path);
151
152    // Show directory contents
153    if let Ok(entries) = fs::read_dir(regular_path) {
154        let mut files: Vec<_> = entries.filter_map(|e| e.ok()).collect();
155        files.sort_by_key(|e| e.file_name());
156
157        println!("   Regular repo contents:");
158        for entry in files {
159            if let Some(name) = entry.file_name().to_str() {
160                let is_dir = entry.file_type().map(|t| t.is_dir()).unwrap_or(false);
161                let marker = if is_dir { "[DIR]" } else { "[FILE]" };
162                println!("     {} {}", marker, name);
163            }
164        }
165    }
166
167    if let Ok(entries) = fs::read_dir(bare_path) {
168        let mut files: Vec<_> = entries.filter_map(|e| e.ok()).collect();
169        files.sort_by_key(|e| e.file_name());
170
171        println!("   Bare repo contents:");
172        for entry in files {
173            if let Some(name) = entry.file_name().to_str() {
174                let is_dir = entry.file_type().map(|t| t.is_dir()).unwrap_or(false);
175                let marker = if is_dir { "[DIR]" } else { "[FILE]" };
176                println!("     {} {}", marker, name);
177            }
178        }
179    }
180    println!();
181
182    // Clean up
183    println!("Cleaning up example repositories...");
184    fs::remove_dir_all(&base_path)?;
185    println!("Repository operations example completed!");
186
187    Ok(())
188}
Source

pub fn config(&self) -> RepoConfig<'_>

Get a configuration manager for this repository

Returns a RepoConfig instance that can be used to get and set git configuration values for this repository.

§Example
use rustic_git::Repository;
use std::env;

let test_path = env::temp_dir().join("test");
let repo = Repository::init(&test_path, false)?;
repo.config().set_user("John Doe", "john@example.com")?;

let (name, email) = repo.config().get_user()?;
assert_eq!(name, "John Doe");
assert_eq!(email, "john@example.com");
Examples found in repository?
examples/config_operations.rs (line 35)
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}

Trait Implementations§

Source§

impl Debug for Repository

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

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> 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, 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.