reset_operations/
reset_operations.rs

1use rustic_git::{Repository, ResetMode, Result};
2use std::{env, fs};
3
4/// Comprehensive demonstration of reset operations in rustic-git
5///
6/// This example showcases:
7/// - Different reset modes (soft, mixed, hard)
8/// - Reset to specific commits
9/// - File-specific resets
10/// - Error handling for invalid commits
11///
12/// Reset operations are essential for managing git history and staging area.
13fn main() -> Result<()> {
14    println!("=== Reset Operations Demo ===\n");
15
16    // Create a temporary directory for our example
17    let temp_dir = env::temp_dir().join("rustic_git_reset_demo");
18
19    // Clean up if exists
20    if temp_dir.exists() {
21        fs::remove_dir_all(&temp_dir)?;
22    }
23
24    println!("Working in temporary directory: {:?}\n", temp_dir);
25
26    // Initialize a new repository
27    let repo = Repository::init(&temp_dir, false)?;
28
29    // Configure user for commits
30    repo.config().set_user("Example User", "example@test.com")?;
31
32    demonstrate_reset_modes(&repo, &temp_dir)?;
33    demonstrate_file_resets(&repo, &temp_dir)?;
34    demonstrate_error_handling(&repo)?;
35
36    println!("\n=== Reset Operations Demo Complete ===");
37
38    // Clean up
39    fs::remove_dir_all(&temp_dir)?;
40    Ok(())
41}
42
43fn demonstrate_reset_modes(repo: &Repository, temp_dir: &std::path::Path) -> Result<()> {
44    println!("--- Demonstrating Reset Modes ---\n");
45
46    // Create initial commits
47    println!("1. Creating initial commits...");
48
49    // First commit
50    let file1_path = temp_dir.join("file1.txt");
51    fs::write(&file1_path, "Initial content")?;
52    repo.add(&["file1.txt"])?;
53    let first_commit = repo.commit("Initial commit")?;
54    println!("   Created first commit: {}", first_commit);
55
56    // Second commit
57    let file2_path = temp_dir.join("file2.txt");
58    fs::write(&file2_path, "Second file content")?;
59    repo.add(&["file2.txt"])?;
60    let second_commit = repo.commit("Add file2.txt")?;
61    println!("   Created second commit: {}", second_commit);
62
63    // Third commit
64    fs::write(&file1_path, "Modified content")?;
65    repo.add(&["file1.txt"])?;
66    let third_commit = repo.commit("Modify file1.txt")?;
67    println!("   Created third commit: {}", third_commit);
68
69    // Show current status
70    println!("\n2. Current repository state:");
71    show_repo_state(repo)?;
72
73    // Demonstrate soft reset
74    println!("\n3. Performing soft reset to second commit...");
75    repo.reset_soft(&second_commit.to_string())?;
76
77    println!("   After soft reset:");
78    show_repo_state(repo)?;
79    println!("   Note: Changes are still staged, working directory unchanged");
80
81    // Reset back to third commit for next demonstration
82    repo.reset_hard(&third_commit.to_string())?;
83
84    // Demonstrate mixed reset (default)
85    println!("\n4. Performing mixed reset to second commit...");
86    repo.reset_mixed(&second_commit.to_string())?;
87
88    println!("   After mixed reset:");
89    show_repo_state(repo)?;
90    println!("   Note: Changes are unstaged but preserved in working directory");
91
92    // Reset back to third commit for next demonstration
93    repo.reset_hard(&third_commit.to_string())?;
94
95    // Demonstrate hard reset
96    println!("\n5. Performing hard reset to first commit...");
97    repo.reset_hard(&first_commit.to_string())?;
98
99    println!("   After hard reset:");
100    show_repo_state(repo)?;
101    println!("   Note: All changes discarded, working directory matches commit");
102
103    // Demonstrate reset_with_mode for flexibility
104    println!("\n6. Using reset_with_mode for explicit control...");
105
106    // Recreate second commit for demo
107    fs::write(&file2_path, "Recreated second file")?;
108    repo.add(&["file2.txt"])?;
109    let _new_commit = repo.commit("Recreate file2.txt")?;
110
111    repo.reset_with_mode(&first_commit.to_string(), ResetMode::Mixed)?;
112    println!("   Used ResetMode::Mixed explicitly");
113    show_repo_state(repo)?;
114
115    Ok(())
116}
117
118fn demonstrate_file_resets(repo: &Repository, temp_dir: &std::path::Path) -> Result<()> {
119    println!("\n--- Demonstrating File-Specific Resets ---\n");
120
121    // Create some files and stage them
122    println!("1. Creating and staging multiple files...");
123
124    let file_a = temp_dir.join("fileA.txt");
125    let file_b = temp_dir.join("fileB.txt");
126
127    fs::write(&file_a, "Content A")?;
128    fs::write(&file_b, "Content B")?;
129
130    repo.add(&["fileA.txt", "fileB.txt"])?;
131    println!("   Staged fileA.txt and fileB.txt");
132
133    show_repo_state(repo)?;
134
135    // Reset a single file (using existing reset_file from files.rs)
136    println!("\n2. Resetting single file (fileA.txt)...");
137    repo.reset_file("fileA.txt")?;
138
139    println!("   After resetting fileA.txt:");
140    show_repo_state(repo)?;
141    println!("   Note: fileA.txt is unstaged, fileB.txt remains staged");
142
143    // Demonstrate HEAD reset (unstage all changes)
144    println!("\n3. Performing mixed reset to HEAD (unstage all)...");
145    repo.reset_mixed("HEAD")?;
146
147    println!("   After reset HEAD:");
148    show_repo_state(repo)?;
149    println!("   Note: All staged changes are now unstaged");
150
151    Ok(())
152}
153
154fn demonstrate_error_handling(repo: &Repository) -> Result<()> {
155    println!("\n--- Demonstrating Error Handling ---\n");
156
157    // Try to reset to invalid commit
158    println!("1. Attempting reset to invalid commit hash...");
159    match repo.reset_mixed("invalid_commit_hash") {
160        Ok(_) => println!("   Unexpected success!"),
161        Err(e) => println!("   Expected error: {}", e),
162    }
163
164    // Try to reset to non-existent reference
165    println!("\n2. Attempting reset to non-existent reference...");
166    match repo.reset_soft("nonexistent-branch") {
167        Ok(_) => println!("   Unexpected success!"),
168        Err(e) => println!("   Expected error: {}", e),
169    }
170
171    println!("\n   Error handling works correctly!");
172    Ok(())
173}
174
175fn show_repo_state(repo: &Repository) -> Result<()> {
176    let status = repo.status()?;
177
178    let staged_count = status.staged_files().count();
179    let unstaged_count = status.unstaged_files().count();
180    let untracked_count = status.untracked_entries().count();
181
182    println!("   Repository state:");
183    println!("     - Staged files: {}", staged_count);
184    println!("     - Modified files: {}", unstaged_count);
185    println!("     - Untracked files: {}", untracked_count);
186
187    if staged_count > 0 {
188        println!("     - Staged:");
189        for file in status.staged_files().take(5) {
190            println!("       * {}", file.path.display());
191        }
192    }
193
194    if unstaged_count > 0 {
195        println!("     - Modified:");
196        for file in status.unstaged_files().take(5) {
197            println!("       * {}", file.path.display());
198        }
199    }
200
201    if untracked_count > 0 {
202        println!("     - Untracked:");
203        for file in status.untracked_entries().take(5) {
204            println!("       * {}", file.path.display());
205        }
206    }
207
208    Ok(())
209}