MoveOptions

Struct MoveOptions 

Source
pub struct MoveOptions {
    pub force: bool,
    pub verbose: bool,
    pub dry_run: bool,
}
Expand description

Options for move operations

Fields§

§force: bool

Force move even if destination exists

§verbose: bool

Show verbose output

§dry_run: bool

Dry run - don’t actually move files

Implementations§

Source§

impl MoveOptions

Source

pub fn new() -> Self

Create new move options with default values

Examples found in repository?
examples/file_lifecycle_operations.rs (line 264)
16fn main() -> Result<()> {
17    println!("Rustic Git - File Lifecycle Operations Example\n");
18
19    let base_path = env::temp_dir().join("rustic_git_files_example");
20    let repo_path = base_path.join("main_repo");
21
22    // Clean up any previous runs
23    if base_path.exists() {
24        fs::remove_dir_all(&base_path).expect("Failed to clean up previous example");
25    }
26    fs::create_dir_all(&base_path)?;
27
28    println!("=== Repository Setup ===\n");
29
30    // Initialize repository
31    println!("Initializing repository for file lifecycle demonstrations...");
32    let repo = Repository::init(&repo_path, false)?;
33    println!("Repository initialized at: {}", repo_path.display());
34
35    // Set up git configuration for commits
36    repo.config().set_user("Demo User", "demo@example.com")?;
37
38    // Create initial project structure
39    fs::create_dir_all(repo_path.join("src"))?;
40    fs::create_dir_all(repo_path.join("docs"))?;
41    fs::create_dir_all(repo_path.join("tests"))?;
42
43    let files = [
44        (
45            "README.md",
46            "# File Lifecycle Demo\n\nDemonstrating rustic-git file management capabilities.",
47        ),
48        (
49            "src/main.rs",
50            "fn main() {\n    println!(\"Hello, world!\");\n}",
51        ),
52        (
53            "src/lib.rs",
54            "//! Library module\n\npub fn greet() {\n    println!(\"Hello from lib!\");\n}",
55        ),
56        ("docs/guide.md", "# User Guide\n\nThis is the user guide."),
57        (
58            "tests/integration.rs",
59            "#[test]\nfn test_basic() {\n    assert_eq!(2 + 2, 4);\n}",
60        ),
61    ];
62
63    for (path, content) in &files {
64        fs::write(repo_path.join(path), content)?;
65    }
66
67    repo.add(&files.iter().map(|(path, _)| *path).collect::<Vec<_>>())?;
68    let initial_commit = repo.commit("Initial project setup")?;
69    println!("Created initial commit: {}\n", initial_commit.short());
70
71    println!("=== File Restoration Operations ===\n");
72
73    // Modify some files
74    println!("Modifying files to demonstrate restoration...");
75    fs::write(
76        repo_path.join("README.md"),
77        "# Modified README\n\nThis content has been changed.",
78    )?;
79    fs::write(
80        repo_path.join("src/main.rs"),
81        "fn main() {\n    println!(\"Modified main!\");\n    println!(\"Added new line!\");\n}",
82    )?;
83
84    println!("   Modified README.md and src/main.rs");
85
86    // Show current status
87    let status = repo.status()?;
88    println!(
89        "   Files with modifications: {}",
90        status.unstaged_files().count()
91    );
92    for entry in status.unstaged_files() {
93        println!("     - {}", entry.path.display());
94    }
95    println!();
96
97    // Restore single file with checkout_file
98    println!("Restoring README.md using checkout_file():");
99    repo.checkout_file("README.md")?;
100    let restored_content = fs::read_to_string(repo_path.join("README.md"))?;
101    println!("   ✓ README.md restored to original state");
102    println!(
103        "   Content preview: {:?}",
104        restored_content.lines().next().unwrap_or("")
105    );
106    println!();
107
108    // Demonstrate advanced restore with options
109    println!("Creating second commit for restore demonstration...");
110    fs::write(
111        repo_path.join("src/advanced.rs"),
112        "//! Advanced module\n\npub fn advanced_function() {\n    println!(\"Advanced functionality\");\n}",
113    )?;
114    repo.add(&["src/advanced.rs"])?;
115    let second_commit = repo.commit("Add advanced module")?;
116    println!("   Second commit: {}", second_commit.short());
117
118    // Modify the advanced file
119    fs::write(
120        repo_path.join("src/advanced.rs"),
121        "//! HEAVILY MODIFIED\n\npub fn broken_function() {\n    panic!(\"This is broken!\");\n}",
122    )?;
123    println!("   Modified src/advanced.rs");
124
125    // Restore from specific commit using restore with options
126    println!("Restoring src/advanced.rs from specific commit using restore():");
127    let restore_options = RestoreOptions::new()
128        .with_source(format!("{}", second_commit))
129        .with_worktree();
130    repo.restore(&["src/advanced.rs"], restore_options)?;
131
132    let restored_advanced = fs::read_to_string(repo_path.join("src/advanced.rs"))?;
133    println!("   ✓ File restored from commit {}", second_commit.short());
134    println!(
135        "   Content preview: {:?}",
136        restored_advanced.lines().next().unwrap_or("")
137    );
138    println!();
139
140    println!("=== Staging Area Operations ===\n");
141
142    // Modify and stage files
143    println!("Demonstrating staging area manipulation...");
144    fs::write(
145        repo_path.join("src/lib.rs"),
146        "//! STAGED CHANGES\n\npub fn new_function() {\n    println!(\"This will be staged\");\n}",
147    )?;
148    repo.add(&["src/lib.rs"])?;
149    println!("   Modified and staged src/lib.rs");
150
151    let status = repo.status()?;
152    println!("   Staged files: {}", status.staged_files().count());
153    for entry in status.staged_files() {
154        println!("     - {}", entry.path.display());
155    }
156
157    // Unstage the file
158    println!("Unstaging src/lib.rs using reset_file():");
159    repo.reset_file("src/lib.rs")?;
160
161    let status_after_reset = repo.status()?;
162    println!("   ✓ File unstaged (now in modified files)");
163    println!(
164        "   Staged files: {}",
165        status_after_reset.staged_files().count()
166    );
167    println!(
168        "   Modified files: {}",
169        status_after_reset.unstaged_files().count()
170    );
171    println!();
172
173    println!("=== File Removal Operations ===\n");
174
175    // Create files for removal demonstration
176    println!("Creating files for removal demonstration...");
177    fs::write(repo_path.join("temp_file.txt"), "This is a temporary file")?;
178    fs::write(
179        repo_path.join("docs/old_doc.md"),
180        "# Old Documentation\n\nThis document is outdated.",
181    )?;
182    fs::create_dir_all(repo_path.join("old_directory"))?;
183    fs::write(
184        repo_path.join("old_directory/nested_file.txt"),
185        "Nested content",
186    )?;
187
188    // Add and commit these files
189    repo.add(&[
190        "temp_file.txt",
191        "docs/old_doc.md",
192        "old_directory/nested_file.txt",
193    ])?;
194    repo.commit("Add files for removal demo")?;
195    println!("   Created and committed files for removal");
196
197    // Basic file removal
198    println!("Removing temp_file.txt using rm():");
199    repo.rm(&["temp_file.txt"])?;
200    println!("   ✓ temp_file.txt removed from repository and working tree");
201    assert!(!repo_path.join("temp_file.txt").exists());
202
203    // Remove from index only (keep in working tree)
204    println!("Removing docs/old_doc.md from index only using rm_with_options():");
205    let cached_remove_options = RemoveOptions::new().with_cached();
206    repo.rm_with_options(&["docs/old_doc.md"], cached_remove_options)?;
207
208    println!("   ✓ File removed from index but kept in working tree");
209    assert!(repo_path.join("docs/old_doc.md").exists());
210    let content = fs::read_to_string(repo_path.join("docs/old_doc.md"))?;
211    println!(
212        "   Working tree content still available: {:?}",
213        content.lines().next().unwrap_or("")
214    );
215
216    // Recursive removal
217    println!("Removing old_directory/ recursively:");
218    let recursive_options = RemoveOptions::new().with_recursive();
219    repo.rm_with_options(&["old_directory/"], recursive_options)?;
220    println!("   ✓ Directory and contents removed recursively");
221    assert!(!repo_path.join("old_directory").exists());
222    println!();
223
224    println!("=== File Move/Rename Operations ===\n");
225
226    // Create files for move demonstration
227    println!("Creating files for move/rename demonstration...");
228    fs::write(repo_path.join("old_name.txt"), "This file will be renamed")?;
229    fs::create_dir_all(repo_path.join("source_dir"))?;
230    fs::write(
231        repo_path.join("source_dir/movable.txt"),
232        "This file will be moved",
233    )?;
234    fs::create_dir_all(repo_path.join("target_dir"))?;
235
236    repo.add(&["old_name.txt", "source_dir/movable.txt"])?;
237    repo.commit("Add files for move demo")?;
238    println!("   Created files for move demonstration");
239
240    // Simple rename
241    println!("Renaming old_name.txt to new_name.txt using mv():");
242    repo.mv("old_name.txt", "new_name.txt")?;
243
244    assert!(!repo_path.join("old_name.txt").exists());
245    assert!(repo_path.join("new_name.txt").exists());
246    let content = fs::read_to_string(repo_path.join("new_name.txt"))?;
247    println!("   ✓ File renamed successfully");
248    println!("   Content preserved: {:?}", content.trim());
249
250    // Move file to different directory
251    println!("Moving source_dir/movable.txt to target_dir/ using mv():");
252    repo.mv("source_dir/movable.txt", "target_dir/movable.txt")?;
253
254    assert!(!repo_path.join("source_dir/movable.txt").exists());
255    assert!(repo_path.join("target_dir/movable.txt").exists());
256    println!("   ✓ File moved to different directory");
257
258    // Demonstrate move with options (dry run)
259    fs::write(repo_path.join("test_move.txt"), "Test content for dry run")?;
260    repo.add(&["test_move.txt"])?;
261    repo.commit("Add test file for dry run demo")?;
262
263    println!("Demonstrating dry run move (won't actually move):");
264    let dry_run_options = MoveOptions::new().with_dry_run().with_verbose();
265    repo.mv_with_options("test_move.txt", "would_be_moved.txt", dry_run_options)?;
266
267    // File should still exist at original location
268    assert!(repo_path.join("test_move.txt").exists());
269    assert!(!repo_path.join("would_be_moved.txt").exists());
270    println!("   ✓ Dry run completed - no actual move performed");
271    println!();
272
273    println!("=== .gitignore Management ===\n");
274
275    // Initially no ignore patterns
276    println!("Checking initial .gitignore state:");
277    let initial_patterns = repo.ignore_list()?;
278    println!("   Initial ignore patterns: {}", initial_patterns.len());
279
280    // Add ignore patterns
281    println!("Adding ignore patterns...");
282    repo.ignore_add(&[
283        "*.tmp",
284        "*.log",
285        "build/",
286        "node_modules/",
287        ".DS_Store",
288        "*.secret",
289    ])?;
290    println!("   Added 6 ignore patterns to .gitignore");
291
292    // List current patterns
293    let patterns = repo.ignore_list()?;
294    println!("   Current ignore patterns: {}", patterns.len());
295    for (i, pattern) in patterns.iter().enumerate() {
296        println!("     {}. {}", i + 1, pattern);
297    }
298
299    // Create test files to check ignore status
300    println!("\nCreating test files to check ignore status...");
301    let test_files = [
302        ("regular_file.txt", false),
303        ("temp_file.tmp", true),
304        ("debug.log", true),
305        ("important.secret", true),
306        ("normal.md", false),
307    ];
308
309    for (filename, _) in &test_files {
310        fs::write(repo_path.join(filename), "test content")?;
311    }
312
313    // Check ignore status for each file
314    println!("Checking ignore status for test files:");
315    for (filename, expected_ignored) in &test_files {
316        let is_ignored = repo.ignore_check(filename)?;
317        let status_symbol = if is_ignored { "🚫" } else { "✅" };
318        println!(
319            "   {} {} - {}",
320            status_symbol,
321            filename,
322            if is_ignored { "IGNORED" } else { "TRACKED" }
323        );
324
325        // Verify expectation
326        assert_eq!(
327            is_ignored, *expected_ignored,
328            "Ignore status mismatch for {}",
329            filename
330        );
331    }
332    println!();
333
334    println!("=== Error Handling and Edge Cases ===\n");
335
336    // Test error cases
337    println!("Testing error conditions:");
338
339    // Try to checkout non-existent file
340    println!("   Attempting to checkout non-existent file:");
341    match repo.checkout_file("nonexistent.txt") {
342        Ok(_) => println!("     Unexpected success"),
343        Err(e) => println!("     ✓ Expected error: {}", e),
344    }
345
346    // Try to reset non-existent file
347    println!("   Attempting to reset non-staged file:");
348    match repo.reset_file("new_name.txt") {
349        Ok(_) => println!("     ✓ Reset succeeded (file not staged, no error)"),
350        Err(e) => println!("     Error: {}", e),
351    }
352
353    // Try to remove non-existent file
354    println!("   Attempting to remove non-existent file:");
355    match repo.rm(&["definitely_not_here.txt"]) {
356        Ok(_) => println!("     Unexpected success"),
357        Err(e) => println!("     ✓ Expected error: {}", e),
358    }
359
360    // Try to remove with ignore-unmatch option
361    println!("   Attempting to remove with ignore-unmatch option:");
362    let ignore_unmatch_options = RemoveOptions::new().with_ignore_unmatch();
363    match repo.rm_with_options(&["also_not_here.txt"], ignore_unmatch_options) {
364        Ok(_) => println!("     ✓ Succeeded with ignore-unmatch (no error)"),
365        Err(e) => println!("     Error: {}", e),
366    }
367
368    // Try to move to existing file without force
369    fs::write(repo_path.join("existing_target.txt"), "existing content")?;
370    repo.add(&["existing_target.txt"])?;
371    repo.commit("Add existing target")?;
372
373    println!("   Attempting to move to existing file without force:");
374    match repo.mv("test_move.txt", "existing_target.txt") {
375        Ok(_) => println!("     Unexpected success (git may have overwritten)"),
376        Err(e) => println!("     ✓ Expected error: {}", e),
377    }
378    println!();
379
380    println!("=== Advanced Restore Operations ===\n");
381
382    // Demonstrate restore with staged and worktree options
383    println!("Demonstrating advanced restore with staging area...");
384
385    // Modify file and stage it
386    fs::write(repo_path.join("new_name.txt"), "staged changes")?;
387    repo.add(&["new_name.txt"])?;
388
389    // Modify it again in working tree
390    fs::write(repo_path.join("new_name.txt"), "working tree changes")?;
391
392    println!("   File has both staged and working tree changes");
393
394    // Restore only staged area
395    println!("   Restoring staged changes only:");
396    let staged_restore = RestoreOptions::new().with_staged();
397    repo.restore(&["new_name.txt"], staged_restore)?;
398
399    let content_after_staged_restore = fs::read_to_string(repo_path.join("new_name.txt"))?;
400    println!("     ✓ Staged changes restored, working tree preserved");
401    println!(
402        "     Working tree content: {:?}",
403        content_after_staged_restore.trim()
404    );
405
406    // Restore working tree
407    println!("   Restoring working tree:");
408    let worktree_restore = RestoreOptions::new().with_worktree();
409    repo.restore(&["new_name.txt"], worktree_restore)?;
410
411    let final_content = fs::read_to_string(repo_path.join("new_name.txt"))?;
412    println!("     ✓ Working tree restored to committed state");
413    println!("     Final content: {:?}", final_content.trim());
414    println!();
415
416    println!("=== Repository State Summary ===\n");
417
418    let final_status = repo.status()?;
419    println!("Final repository state:");
420    println!("   Clean repository: {}", final_status.is_clean());
421    println!("   Staged files: {}", final_status.staged_files().count());
422    println!(
423        "   Modified files: {}",
424        final_status.unstaged_files().count()
425    );
426    println!(
427        "   Untracked files: {}",
428        final_status.untracked_entries().count()
429    );
430
431    if !final_status.is_clean() {
432        println!("\n   Remaining changes:");
433        for entry in final_status.staged_files() {
434            println!("     Staged: {}", entry.path.display());
435        }
436        for entry in final_status.unstaged_files() {
437            println!("     Modified: {}", entry.path.display());
438        }
439        for entry in final_status.untracked_entries() {
440            println!("     Untracked: {}", entry.path.display());
441        }
442    }
443
444    // Show .gitignore content
445    let final_patterns = repo.ignore_list()?;
446    println!("\n   .gitignore patterns: {}", final_patterns.len());
447    for pattern in final_patterns {
448        println!("     - {}", pattern);
449    }
450
451    println!("\n=== Summary ===\n");
452
453    println!("File lifecycle operations demonstration completed!");
454    println!("  Repository: {}", repo_path.display());
455
456    println!("\nOperations demonstrated:");
457    println!("  ✓ File restoration from HEAD (checkout_file)");
458    println!("  ✓ Advanced file restoration with options (restore)");
459    println!("  ✓ Unstaging files (reset_file)");
460    println!("  ✓ File removal with various options (rm, rm_with_options)");
461    println!("  ✓ File moving and renaming (mv, mv_with_options)");
462    println!("  ✓ .gitignore pattern management (ignore_add, ignore_list, ignore_check)");
463    println!("  ✓ Staged vs working tree restoration");
464    println!("  ✓ Error handling for invalid operations");
465    println!("  ✓ Dry run and verbose options");
466    println!("  ✓ Recursive and cached removal options");
467
468    // Clean up
469    println!("\nCleaning up example repositories...");
470    fs::remove_dir_all(&base_path)?;
471    println!("File lifecycle operations example completed!");
472
473    Ok(())
474}
Source

pub fn with_force(self) -> Self

Enable force move

Source

pub fn with_verbose(self) -> Self

Enable verbose output

Examples found in repository?
examples/file_lifecycle_operations.rs (line 264)
16fn main() -> Result<()> {
17    println!("Rustic Git - File Lifecycle Operations Example\n");
18
19    let base_path = env::temp_dir().join("rustic_git_files_example");
20    let repo_path = base_path.join("main_repo");
21
22    // Clean up any previous runs
23    if base_path.exists() {
24        fs::remove_dir_all(&base_path).expect("Failed to clean up previous example");
25    }
26    fs::create_dir_all(&base_path)?;
27
28    println!("=== Repository Setup ===\n");
29
30    // Initialize repository
31    println!("Initializing repository for file lifecycle demonstrations...");
32    let repo = Repository::init(&repo_path, false)?;
33    println!("Repository initialized at: {}", repo_path.display());
34
35    // Set up git configuration for commits
36    repo.config().set_user("Demo User", "demo@example.com")?;
37
38    // Create initial project structure
39    fs::create_dir_all(repo_path.join("src"))?;
40    fs::create_dir_all(repo_path.join("docs"))?;
41    fs::create_dir_all(repo_path.join("tests"))?;
42
43    let files = [
44        (
45            "README.md",
46            "# File Lifecycle Demo\n\nDemonstrating rustic-git file management capabilities.",
47        ),
48        (
49            "src/main.rs",
50            "fn main() {\n    println!(\"Hello, world!\");\n}",
51        ),
52        (
53            "src/lib.rs",
54            "//! Library module\n\npub fn greet() {\n    println!(\"Hello from lib!\");\n}",
55        ),
56        ("docs/guide.md", "# User Guide\n\nThis is the user guide."),
57        (
58            "tests/integration.rs",
59            "#[test]\nfn test_basic() {\n    assert_eq!(2 + 2, 4);\n}",
60        ),
61    ];
62
63    for (path, content) in &files {
64        fs::write(repo_path.join(path), content)?;
65    }
66
67    repo.add(&files.iter().map(|(path, _)| *path).collect::<Vec<_>>())?;
68    let initial_commit = repo.commit("Initial project setup")?;
69    println!("Created initial commit: {}\n", initial_commit.short());
70
71    println!("=== File Restoration Operations ===\n");
72
73    // Modify some files
74    println!("Modifying files to demonstrate restoration...");
75    fs::write(
76        repo_path.join("README.md"),
77        "# Modified README\n\nThis content has been changed.",
78    )?;
79    fs::write(
80        repo_path.join("src/main.rs"),
81        "fn main() {\n    println!(\"Modified main!\");\n    println!(\"Added new line!\");\n}",
82    )?;
83
84    println!("   Modified README.md and src/main.rs");
85
86    // Show current status
87    let status = repo.status()?;
88    println!(
89        "   Files with modifications: {}",
90        status.unstaged_files().count()
91    );
92    for entry in status.unstaged_files() {
93        println!("     - {}", entry.path.display());
94    }
95    println!();
96
97    // Restore single file with checkout_file
98    println!("Restoring README.md using checkout_file():");
99    repo.checkout_file("README.md")?;
100    let restored_content = fs::read_to_string(repo_path.join("README.md"))?;
101    println!("   ✓ README.md restored to original state");
102    println!(
103        "   Content preview: {:?}",
104        restored_content.lines().next().unwrap_or("")
105    );
106    println!();
107
108    // Demonstrate advanced restore with options
109    println!("Creating second commit for restore demonstration...");
110    fs::write(
111        repo_path.join("src/advanced.rs"),
112        "//! Advanced module\n\npub fn advanced_function() {\n    println!(\"Advanced functionality\");\n}",
113    )?;
114    repo.add(&["src/advanced.rs"])?;
115    let second_commit = repo.commit("Add advanced module")?;
116    println!("   Second commit: {}", second_commit.short());
117
118    // Modify the advanced file
119    fs::write(
120        repo_path.join("src/advanced.rs"),
121        "//! HEAVILY MODIFIED\n\npub fn broken_function() {\n    panic!(\"This is broken!\");\n}",
122    )?;
123    println!("   Modified src/advanced.rs");
124
125    // Restore from specific commit using restore with options
126    println!("Restoring src/advanced.rs from specific commit using restore():");
127    let restore_options = RestoreOptions::new()
128        .with_source(format!("{}", second_commit))
129        .with_worktree();
130    repo.restore(&["src/advanced.rs"], restore_options)?;
131
132    let restored_advanced = fs::read_to_string(repo_path.join("src/advanced.rs"))?;
133    println!("   ✓ File restored from commit {}", second_commit.short());
134    println!(
135        "   Content preview: {:?}",
136        restored_advanced.lines().next().unwrap_or("")
137    );
138    println!();
139
140    println!("=== Staging Area Operations ===\n");
141
142    // Modify and stage files
143    println!("Demonstrating staging area manipulation...");
144    fs::write(
145        repo_path.join("src/lib.rs"),
146        "//! STAGED CHANGES\n\npub fn new_function() {\n    println!(\"This will be staged\");\n}",
147    )?;
148    repo.add(&["src/lib.rs"])?;
149    println!("   Modified and staged src/lib.rs");
150
151    let status = repo.status()?;
152    println!("   Staged files: {}", status.staged_files().count());
153    for entry in status.staged_files() {
154        println!("     - {}", entry.path.display());
155    }
156
157    // Unstage the file
158    println!("Unstaging src/lib.rs using reset_file():");
159    repo.reset_file("src/lib.rs")?;
160
161    let status_after_reset = repo.status()?;
162    println!("   ✓ File unstaged (now in modified files)");
163    println!(
164        "   Staged files: {}",
165        status_after_reset.staged_files().count()
166    );
167    println!(
168        "   Modified files: {}",
169        status_after_reset.unstaged_files().count()
170    );
171    println!();
172
173    println!("=== File Removal Operations ===\n");
174
175    // Create files for removal demonstration
176    println!("Creating files for removal demonstration...");
177    fs::write(repo_path.join("temp_file.txt"), "This is a temporary file")?;
178    fs::write(
179        repo_path.join("docs/old_doc.md"),
180        "# Old Documentation\n\nThis document is outdated.",
181    )?;
182    fs::create_dir_all(repo_path.join("old_directory"))?;
183    fs::write(
184        repo_path.join("old_directory/nested_file.txt"),
185        "Nested content",
186    )?;
187
188    // Add and commit these files
189    repo.add(&[
190        "temp_file.txt",
191        "docs/old_doc.md",
192        "old_directory/nested_file.txt",
193    ])?;
194    repo.commit("Add files for removal demo")?;
195    println!("   Created and committed files for removal");
196
197    // Basic file removal
198    println!("Removing temp_file.txt using rm():");
199    repo.rm(&["temp_file.txt"])?;
200    println!("   ✓ temp_file.txt removed from repository and working tree");
201    assert!(!repo_path.join("temp_file.txt").exists());
202
203    // Remove from index only (keep in working tree)
204    println!("Removing docs/old_doc.md from index only using rm_with_options():");
205    let cached_remove_options = RemoveOptions::new().with_cached();
206    repo.rm_with_options(&["docs/old_doc.md"], cached_remove_options)?;
207
208    println!("   ✓ File removed from index but kept in working tree");
209    assert!(repo_path.join("docs/old_doc.md").exists());
210    let content = fs::read_to_string(repo_path.join("docs/old_doc.md"))?;
211    println!(
212        "   Working tree content still available: {:?}",
213        content.lines().next().unwrap_or("")
214    );
215
216    // Recursive removal
217    println!("Removing old_directory/ recursively:");
218    let recursive_options = RemoveOptions::new().with_recursive();
219    repo.rm_with_options(&["old_directory/"], recursive_options)?;
220    println!("   ✓ Directory and contents removed recursively");
221    assert!(!repo_path.join("old_directory").exists());
222    println!();
223
224    println!("=== File Move/Rename Operations ===\n");
225
226    // Create files for move demonstration
227    println!("Creating files for move/rename demonstration...");
228    fs::write(repo_path.join("old_name.txt"), "This file will be renamed")?;
229    fs::create_dir_all(repo_path.join("source_dir"))?;
230    fs::write(
231        repo_path.join("source_dir/movable.txt"),
232        "This file will be moved",
233    )?;
234    fs::create_dir_all(repo_path.join("target_dir"))?;
235
236    repo.add(&["old_name.txt", "source_dir/movable.txt"])?;
237    repo.commit("Add files for move demo")?;
238    println!("   Created files for move demonstration");
239
240    // Simple rename
241    println!("Renaming old_name.txt to new_name.txt using mv():");
242    repo.mv("old_name.txt", "new_name.txt")?;
243
244    assert!(!repo_path.join("old_name.txt").exists());
245    assert!(repo_path.join("new_name.txt").exists());
246    let content = fs::read_to_string(repo_path.join("new_name.txt"))?;
247    println!("   ✓ File renamed successfully");
248    println!("   Content preserved: {:?}", content.trim());
249
250    // Move file to different directory
251    println!("Moving source_dir/movable.txt to target_dir/ using mv():");
252    repo.mv("source_dir/movable.txt", "target_dir/movable.txt")?;
253
254    assert!(!repo_path.join("source_dir/movable.txt").exists());
255    assert!(repo_path.join("target_dir/movable.txt").exists());
256    println!("   ✓ File moved to different directory");
257
258    // Demonstrate move with options (dry run)
259    fs::write(repo_path.join("test_move.txt"), "Test content for dry run")?;
260    repo.add(&["test_move.txt"])?;
261    repo.commit("Add test file for dry run demo")?;
262
263    println!("Demonstrating dry run move (won't actually move):");
264    let dry_run_options = MoveOptions::new().with_dry_run().with_verbose();
265    repo.mv_with_options("test_move.txt", "would_be_moved.txt", dry_run_options)?;
266
267    // File should still exist at original location
268    assert!(repo_path.join("test_move.txt").exists());
269    assert!(!repo_path.join("would_be_moved.txt").exists());
270    println!("   ✓ Dry run completed - no actual move performed");
271    println!();
272
273    println!("=== .gitignore Management ===\n");
274
275    // Initially no ignore patterns
276    println!("Checking initial .gitignore state:");
277    let initial_patterns = repo.ignore_list()?;
278    println!("   Initial ignore patterns: {}", initial_patterns.len());
279
280    // Add ignore patterns
281    println!("Adding ignore patterns...");
282    repo.ignore_add(&[
283        "*.tmp",
284        "*.log",
285        "build/",
286        "node_modules/",
287        ".DS_Store",
288        "*.secret",
289    ])?;
290    println!("   Added 6 ignore patterns to .gitignore");
291
292    // List current patterns
293    let patterns = repo.ignore_list()?;
294    println!("   Current ignore patterns: {}", patterns.len());
295    for (i, pattern) in patterns.iter().enumerate() {
296        println!("     {}. {}", i + 1, pattern);
297    }
298
299    // Create test files to check ignore status
300    println!("\nCreating test files to check ignore status...");
301    let test_files = [
302        ("regular_file.txt", false),
303        ("temp_file.tmp", true),
304        ("debug.log", true),
305        ("important.secret", true),
306        ("normal.md", false),
307    ];
308
309    for (filename, _) in &test_files {
310        fs::write(repo_path.join(filename), "test content")?;
311    }
312
313    // Check ignore status for each file
314    println!("Checking ignore status for test files:");
315    for (filename, expected_ignored) in &test_files {
316        let is_ignored = repo.ignore_check(filename)?;
317        let status_symbol = if is_ignored { "🚫" } else { "✅" };
318        println!(
319            "   {} {} - {}",
320            status_symbol,
321            filename,
322            if is_ignored { "IGNORED" } else { "TRACKED" }
323        );
324
325        // Verify expectation
326        assert_eq!(
327            is_ignored, *expected_ignored,
328            "Ignore status mismatch for {}",
329            filename
330        );
331    }
332    println!();
333
334    println!("=== Error Handling and Edge Cases ===\n");
335
336    // Test error cases
337    println!("Testing error conditions:");
338
339    // Try to checkout non-existent file
340    println!("   Attempting to checkout non-existent file:");
341    match repo.checkout_file("nonexistent.txt") {
342        Ok(_) => println!("     Unexpected success"),
343        Err(e) => println!("     ✓ Expected error: {}", e),
344    }
345
346    // Try to reset non-existent file
347    println!("   Attempting to reset non-staged file:");
348    match repo.reset_file("new_name.txt") {
349        Ok(_) => println!("     ✓ Reset succeeded (file not staged, no error)"),
350        Err(e) => println!("     Error: {}", e),
351    }
352
353    // Try to remove non-existent file
354    println!("   Attempting to remove non-existent file:");
355    match repo.rm(&["definitely_not_here.txt"]) {
356        Ok(_) => println!("     Unexpected success"),
357        Err(e) => println!("     ✓ Expected error: {}", e),
358    }
359
360    // Try to remove with ignore-unmatch option
361    println!("   Attempting to remove with ignore-unmatch option:");
362    let ignore_unmatch_options = RemoveOptions::new().with_ignore_unmatch();
363    match repo.rm_with_options(&["also_not_here.txt"], ignore_unmatch_options) {
364        Ok(_) => println!("     ✓ Succeeded with ignore-unmatch (no error)"),
365        Err(e) => println!("     Error: {}", e),
366    }
367
368    // Try to move to existing file without force
369    fs::write(repo_path.join("existing_target.txt"), "existing content")?;
370    repo.add(&["existing_target.txt"])?;
371    repo.commit("Add existing target")?;
372
373    println!("   Attempting to move to existing file without force:");
374    match repo.mv("test_move.txt", "existing_target.txt") {
375        Ok(_) => println!("     Unexpected success (git may have overwritten)"),
376        Err(e) => println!("     ✓ Expected error: {}", e),
377    }
378    println!();
379
380    println!("=== Advanced Restore Operations ===\n");
381
382    // Demonstrate restore with staged and worktree options
383    println!("Demonstrating advanced restore with staging area...");
384
385    // Modify file and stage it
386    fs::write(repo_path.join("new_name.txt"), "staged changes")?;
387    repo.add(&["new_name.txt"])?;
388
389    // Modify it again in working tree
390    fs::write(repo_path.join("new_name.txt"), "working tree changes")?;
391
392    println!("   File has both staged and working tree changes");
393
394    // Restore only staged area
395    println!("   Restoring staged changes only:");
396    let staged_restore = RestoreOptions::new().with_staged();
397    repo.restore(&["new_name.txt"], staged_restore)?;
398
399    let content_after_staged_restore = fs::read_to_string(repo_path.join("new_name.txt"))?;
400    println!("     ✓ Staged changes restored, working tree preserved");
401    println!(
402        "     Working tree content: {:?}",
403        content_after_staged_restore.trim()
404    );
405
406    // Restore working tree
407    println!("   Restoring working tree:");
408    let worktree_restore = RestoreOptions::new().with_worktree();
409    repo.restore(&["new_name.txt"], worktree_restore)?;
410
411    let final_content = fs::read_to_string(repo_path.join("new_name.txt"))?;
412    println!("     ✓ Working tree restored to committed state");
413    println!("     Final content: {:?}", final_content.trim());
414    println!();
415
416    println!("=== Repository State Summary ===\n");
417
418    let final_status = repo.status()?;
419    println!("Final repository state:");
420    println!("   Clean repository: {}", final_status.is_clean());
421    println!("   Staged files: {}", final_status.staged_files().count());
422    println!(
423        "   Modified files: {}",
424        final_status.unstaged_files().count()
425    );
426    println!(
427        "   Untracked files: {}",
428        final_status.untracked_entries().count()
429    );
430
431    if !final_status.is_clean() {
432        println!("\n   Remaining changes:");
433        for entry in final_status.staged_files() {
434            println!("     Staged: {}", entry.path.display());
435        }
436        for entry in final_status.unstaged_files() {
437            println!("     Modified: {}", entry.path.display());
438        }
439        for entry in final_status.untracked_entries() {
440            println!("     Untracked: {}", entry.path.display());
441        }
442    }
443
444    // Show .gitignore content
445    let final_patterns = repo.ignore_list()?;
446    println!("\n   .gitignore patterns: {}", final_patterns.len());
447    for pattern in final_patterns {
448        println!("     - {}", pattern);
449    }
450
451    println!("\n=== Summary ===\n");
452
453    println!("File lifecycle operations demonstration completed!");
454    println!("  Repository: {}", repo_path.display());
455
456    println!("\nOperations demonstrated:");
457    println!("  ✓ File restoration from HEAD (checkout_file)");
458    println!("  ✓ Advanced file restoration with options (restore)");
459    println!("  ✓ Unstaging files (reset_file)");
460    println!("  ✓ File removal with various options (rm, rm_with_options)");
461    println!("  ✓ File moving and renaming (mv, mv_with_options)");
462    println!("  ✓ .gitignore pattern management (ignore_add, ignore_list, ignore_check)");
463    println!("  ✓ Staged vs working tree restoration");
464    println!("  ✓ Error handling for invalid operations");
465    println!("  ✓ Dry run and verbose options");
466    println!("  ✓ Recursive and cached removal options");
467
468    // Clean up
469    println!("\nCleaning up example repositories...");
470    fs::remove_dir_all(&base_path)?;
471    println!("File lifecycle operations example completed!");
472
473    Ok(())
474}
Source

pub fn with_dry_run(self) -> Self

Enable dry run mode

Examples found in repository?
examples/file_lifecycle_operations.rs (line 264)
16fn main() -> Result<()> {
17    println!("Rustic Git - File Lifecycle Operations Example\n");
18
19    let base_path = env::temp_dir().join("rustic_git_files_example");
20    let repo_path = base_path.join("main_repo");
21
22    // Clean up any previous runs
23    if base_path.exists() {
24        fs::remove_dir_all(&base_path).expect("Failed to clean up previous example");
25    }
26    fs::create_dir_all(&base_path)?;
27
28    println!("=== Repository Setup ===\n");
29
30    // Initialize repository
31    println!("Initializing repository for file lifecycle demonstrations...");
32    let repo = Repository::init(&repo_path, false)?;
33    println!("Repository initialized at: {}", repo_path.display());
34
35    // Set up git configuration for commits
36    repo.config().set_user("Demo User", "demo@example.com")?;
37
38    // Create initial project structure
39    fs::create_dir_all(repo_path.join("src"))?;
40    fs::create_dir_all(repo_path.join("docs"))?;
41    fs::create_dir_all(repo_path.join("tests"))?;
42
43    let files = [
44        (
45            "README.md",
46            "# File Lifecycle Demo\n\nDemonstrating rustic-git file management capabilities.",
47        ),
48        (
49            "src/main.rs",
50            "fn main() {\n    println!(\"Hello, world!\");\n}",
51        ),
52        (
53            "src/lib.rs",
54            "//! Library module\n\npub fn greet() {\n    println!(\"Hello from lib!\");\n}",
55        ),
56        ("docs/guide.md", "# User Guide\n\nThis is the user guide."),
57        (
58            "tests/integration.rs",
59            "#[test]\nfn test_basic() {\n    assert_eq!(2 + 2, 4);\n}",
60        ),
61    ];
62
63    for (path, content) in &files {
64        fs::write(repo_path.join(path), content)?;
65    }
66
67    repo.add(&files.iter().map(|(path, _)| *path).collect::<Vec<_>>())?;
68    let initial_commit = repo.commit("Initial project setup")?;
69    println!("Created initial commit: {}\n", initial_commit.short());
70
71    println!("=== File Restoration Operations ===\n");
72
73    // Modify some files
74    println!("Modifying files to demonstrate restoration...");
75    fs::write(
76        repo_path.join("README.md"),
77        "# Modified README\n\nThis content has been changed.",
78    )?;
79    fs::write(
80        repo_path.join("src/main.rs"),
81        "fn main() {\n    println!(\"Modified main!\");\n    println!(\"Added new line!\");\n}",
82    )?;
83
84    println!("   Modified README.md and src/main.rs");
85
86    // Show current status
87    let status = repo.status()?;
88    println!(
89        "   Files with modifications: {}",
90        status.unstaged_files().count()
91    );
92    for entry in status.unstaged_files() {
93        println!("     - {}", entry.path.display());
94    }
95    println!();
96
97    // Restore single file with checkout_file
98    println!("Restoring README.md using checkout_file():");
99    repo.checkout_file("README.md")?;
100    let restored_content = fs::read_to_string(repo_path.join("README.md"))?;
101    println!("   ✓ README.md restored to original state");
102    println!(
103        "   Content preview: {:?}",
104        restored_content.lines().next().unwrap_or("")
105    );
106    println!();
107
108    // Demonstrate advanced restore with options
109    println!("Creating second commit for restore demonstration...");
110    fs::write(
111        repo_path.join("src/advanced.rs"),
112        "//! Advanced module\n\npub fn advanced_function() {\n    println!(\"Advanced functionality\");\n}",
113    )?;
114    repo.add(&["src/advanced.rs"])?;
115    let second_commit = repo.commit("Add advanced module")?;
116    println!("   Second commit: {}", second_commit.short());
117
118    // Modify the advanced file
119    fs::write(
120        repo_path.join("src/advanced.rs"),
121        "//! HEAVILY MODIFIED\n\npub fn broken_function() {\n    panic!(\"This is broken!\");\n}",
122    )?;
123    println!("   Modified src/advanced.rs");
124
125    // Restore from specific commit using restore with options
126    println!("Restoring src/advanced.rs from specific commit using restore():");
127    let restore_options = RestoreOptions::new()
128        .with_source(format!("{}", second_commit))
129        .with_worktree();
130    repo.restore(&["src/advanced.rs"], restore_options)?;
131
132    let restored_advanced = fs::read_to_string(repo_path.join("src/advanced.rs"))?;
133    println!("   ✓ File restored from commit {}", second_commit.short());
134    println!(
135        "   Content preview: {:?}",
136        restored_advanced.lines().next().unwrap_or("")
137    );
138    println!();
139
140    println!("=== Staging Area Operations ===\n");
141
142    // Modify and stage files
143    println!("Demonstrating staging area manipulation...");
144    fs::write(
145        repo_path.join("src/lib.rs"),
146        "//! STAGED CHANGES\n\npub fn new_function() {\n    println!(\"This will be staged\");\n}",
147    )?;
148    repo.add(&["src/lib.rs"])?;
149    println!("   Modified and staged src/lib.rs");
150
151    let status = repo.status()?;
152    println!("   Staged files: {}", status.staged_files().count());
153    for entry in status.staged_files() {
154        println!("     - {}", entry.path.display());
155    }
156
157    // Unstage the file
158    println!("Unstaging src/lib.rs using reset_file():");
159    repo.reset_file("src/lib.rs")?;
160
161    let status_after_reset = repo.status()?;
162    println!("   ✓ File unstaged (now in modified files)");
163    println!(
164        "   Staged files: {}",
165        status_after_reset.staged_files().count()
166    );
167    println!(
168        "   Modified files: {}",
169        status_after_reset.unstaged_files().count()
170    );
171    println!();
172
173    println!("=== File Removal Operations ===\n");
174
175    // Create files for removal demonstration
176    println!("Creating files for removal demonstration...");
177    fs::write(repo_path.join("temp_file.txt"), "This is a temporary file")?;
178    fs::write(
179        repo_path.join("docs/old_doc.md"),
180        "# Old Documentation\n\nThis document is outdated.",
181    )?;
182    fs::create_dir_all(repo_path.join("old_directory"))?;
183    fs::write(
184        repo_path.join("old_directory/nested_file.txt"),
185        "Nested content",
186    )?;
187
188    // Add and commit these files
189    repo.add(&[
190        "temp_file.txt",
191        "docs/old_doc.md",
192        "old_directory/nested_file.txt",
193    ])?;
194    repo.commit("Add files for removal demo")?;
195    println!("   Created and committed files for removal");
196
197    // Basic file removal
198    println!("Removing temp_file.txt using rm():");
199    repo.rm(&["temp_file.txt"])?;
200    println!("   ✓ temp_file.txt removed from repository and working tree");
201    assert!(!repo_path.join("temp_file.txt").exists());
202
203    // Remove from index only (keep in working tree)
204    println!("Removing docs/old_doc.md from index only using rm_with_options():");
205    let cached_remove_options = RemoveOptions::new().with_cached();
206    repo.rm_with_options(&["docs/old_doc.md"], cached_remove_options)?;
207
208    println!("   ✓ File removed from index but kept in working tree");
209    assert!(repo_path.join("docs/old_doc.md").exists());
210    let content = fs::read_to_string(repo_path.join("docs/old_doc.md"))?;
211    println!(
212        "   Working tree content still available: {:?}",
213        content.lines().next().unwrap_or("")
214    );
215
216    // Recursive removal
217    println!("Removing old_directory/ recursively:");
218    let recursive_options = RemoveOptions::new().with_recursive();
219    repo.rm_with_options(&["old_directory/"], recursive_options)?;
220    println!("   ✓ Directory and contents removed recursively");
221    assert!(!repo_path.join("old_directory").exists());
222    println!();
223
224    println!("=== File Move/Rename Operations ===\n");
225
226    // Create files for move demonstration
227    println!("Creating files for move/rename demonstration...");
228    fs::write(repo_path.join("old_name.txt"), "This file will be renamed")?;
229    fs::create_dir_all(repo_path.join("source_dir"))?;
230    fs::write(
231        repo_path.join("source_dir/movable.txt"),
232        "This file will be moved",
233    )?;
234    fs::create_dir_all(repo_path.join("target_dir"))?;
235
236    repo.add(&["old_name.txt", "source_dir/movable.txt"])?;
237    repo.commit("Add files for move demo")?;
238    println!("   Created files for move demonstration");
239
240    // Simple rename
241    println!("Renaming old_name.txt to new_name.txt using mv():");
242    repo.mv("old_name.txt", "new_name.txt")?;
243
244    assert!(!repo_path.join("old_name.txt").exists());
245    assert!(repo_path.join("new_name.txt").exists());
246    let content = fs::read_to_string(repo_path.join("new_name.txt"))?;
247    println!("   ✓ File renamed successfully");
248    println!("   Content preserved: {:?}", content.trim());
249
250    // Move file to different directory
251    println!("Moving source_dir/movable.txt to target_dir/ using mv():");
252    repo.mv("source_dir/movable.txt", "target_dir/movable.txt")?;
253
254    assert!(!repo_path.join("source_dir/movable.txt").exists());
255    assert!(repo_path.join("target_dir/movable.txt").exists());
256    println!("   ✓ File moved to different directory");
257
258    // Demonstrate move with options (dry run)
259    fs::write(repo_path.join("test_move.txt"), "Test content for dry run")?;
260    repo.add(&["test_move.txt"])?;
261    repo.commit("Add test file for dry run demo")?;
262
263    println!("Demonstrating dry run move (won't actually move):");
264    let dry_run_options = MoveOptions::new().with_dry_run().with_verbose();
265    repo.mv_with_options("test_move.txt", "would_be_moved.txt", dry_run_options)?;
266
267    // File should still exist at original location
268    assert!(repo_path.join("test_move.txt").exists());
269    assert!(!repo_path.join("would_be_moved.txt").exists());
270    println!("   ✓ Dry run completed - no actual move performed");
271    println!();
272
273    println!("=== .gitignore Management ===\n");
274
275    // Initially no ignore patterns
276    println!("Checking initial .gitignore state:");
277    let initial_patterns = repo.ignore_list()?;
278    println!("   Initial ignore patterns: {}", initial_patterns.len());
279
280    // Add ignore patterns
281    println!("Adding ignore patterns...");
282    repo.ignore_add(&[
283        "*.tmp",
284        "*.log",
285        "build/",
286        "node_modules/",
287        ".DS_Store",
288        "*.secret",
289    ])?;
290    println!("   Added 6 ignore patterns to .gitignore");
291
292    // List current patterns
293    let patterns = repo.ignore_list()?;
294    println!("   Current ignore patterns: {}", patterns.len());
295    for (i, pattern) in patterns.iter().enumerate() {
296        println!("     {}. {}", i + 1, pattern);
297    }
298
299    // Create test files to check ignore status
300    println!("\nCreating test files to check ignore status...");
301    let test_files = [
302        ("regular_file.txt", false),
303        ("temp_file.tmp", true),
304        ("debug.log", true),
305        ("important.secret", true),
306        ("normal.md", false),
307    ];
308
309    for (filename, _) in &test_files {
310        fs::write(repo_path.join(filename), "test content")?;
311    }
312
313    // Check ignore status for each file
314    println!("Checking ignore status for test files:");
315    for (filename, expected_ignored) in &test_files {
316        let is_ignored = repo.ignore_check(filename)?;
317        let status_symbol = if is_ignored { "🚫" } else { "✅" };
318        println!(
319            "   {} {} - {}",
320            status_symbol,
321            filename,
322            if is_ignored { "IGNORED" } else { "TRACKED" }
323        );
324
325        // Verify expectation
326        assert_eq!(
327            is_ignored, *expected_ignored,
328            "Ignore status mismatch for {}",
329            filename
330        );
331    }
332    println!();
333
334    println!("=== Error Handling and Edge Cases ===\n");
335
336    // Test error cases
337    println!("Testing error conditions:");
338
339    // Try to checkout non-existent file
340    println!("   Attempting to checkout non-existent file:");
341    match repo.checkout_file("nonexistent.txt") {
342        Ok(_) => println!("     Unexpected success"),
343        Err(e) => println!("     ✓ Expected error: {}", e),
344    }
345
346    // Try to reset non-existent file
347    println!("   Attempting to reset non-staged file:");
348    match repo.reset_file("new_name.txt") {
349        Ok(_) => println!("     ✓ Reset succeeded (file not staged, no error)"),
350        Err(e) => println!("     Error: {}", e),
351    }
352
353    // Try to remove non-existent file
354    println!("   Attempting to remove non-existent file:");
355    match repo.rm(&["definitely_not_here.txt"]) {
356        Ok(_) => println!("     Unexpected success"),
357        Err(e) => println!("     ✓ Expected error: {}", e),
358    }
359
360    // Try to remove with ignore-unmatch option
361    println!("   Attempting to remove with ignore-unmatch option:");
362    let ignore_unmatch_options = RemoveOptions::new().with_ignore_unmatch();
363    match repo.rm_with_options(&["also_not_here.txt"], ignore_unmatch_options) {
364        Ok(_) => println!("     ✓ Succeeded with ignore-unmatch (no error)"),
365        Err(e) => println!("     Error: {}", e),
366    }
367
368    // Try to move to existing file without force
369    fs::write(repo_path.join("existing_target.txt"), "existing content")?;
370    repo.add(&["existing_target.txt"])?;
371    repo.commit("Add existing target")?;
372
373    println!("   Attempting to move to existing file without force:");
374    match repo.mv("test_move.txt", "existing_target.txt") {
375        Ok(_) => println!("     Unexpected success (git may have overwritten)"),
376        Err(e) => println!("     ✓ Expected error: {}", e),
377    }
378    println!();
379
380    println!("=== Advanced Restore Operations ===\n");
381
382    // Demonstrate restore with staged and worktree options
383    println!("Demonstrating advanced restore with staging area...");
384
385    // Modify file and stage it
386    fs::write(repo_path.join("new_name.txt"), "staged changes")?;
387    repo.add(&["new_name.txt"])?;
388
389    // Modify it again in working tree
390    fs::write(repo_path.join("new_name.txt"), "working tree changes")?;
391
392    println!("   File has both staged and working tree changes");
393
394    // Restore only staged area
395    println!("   Restoring staged changes only:");
396    let staged_restore = RestoreOptions::new().with_staged();
397    repo.restore(&["new_name.txt"], staged_restore)?;
398
399    let content_after_staged_restore = fs::read_to_string(repo_path.join("new_name.txt"))?;
400    println!("     ✓ Staged changes restored, working tree preserved");
401    println!(
402        "     Working tree content: {:?}",
403        content_after_staged_restore.trim()
404    );
405
406    // Restore working tree
407    println!("   Restoring working tree:");
408    let worktree_restore = RestoreOptions::new().with_worktree();
409    repo.restore(&["new_name.txt"], worktree_restore)?;
410
411    let final_content = fs::read_to_string(repo_path.join("new_name.txt"))?;
412    println!("     ✓ Working tree restored to committed state");
413    println!("     Final content: {:?}", final_content.trim());
414    println!();
415
416    println!("=== Repository State Summary ===\n");
417
418    let final_status = repo.status()?;
419    println!("Final repository state:");
420    println!("   Clean repository: {}", final_status.is_clean());
421    println!("   Staged files: {}", final_status.staged_files().count());
422    println!(
423        "   Modified files: {}",
424        final_status.unstaged_files().count()
425    );
426    println!(
427        "   Untracked files: {}",
428        final_status.untracked_entries().count()
429    );
430
431    if !final_status.is_clean() {
432        println!("\n   Remaining changes:");
433        for entry in final_status.staged_files() {
434            println!("     Staged: {}", entry.path.display());
435        }
436        for entry in final_status.unstaged_files() {
437            println!("     Modified: {}", entry.path.display());
438        }
439        for entry in final_status.untracked_entries() {
440            println!("     Untracked: {}", entry.path.display());
441        }
442    }
443
444    // Show .gitignore content
445    let final_patterns = repo.ignore_list()?;
446    println!("\n   .gitignore patterns: {}", final_patterns.len());
447    for pattern in final_patterns {
448        println!("     - {}", pattern);
449    }
450
451    println!("\n=== Summary ===\n");
452
453    println!("File lifecycle operations demonstration completed!");
454    println!("  Repository: {}", repo_path.display());
455
456    println!("\nOperations demonstrated:");
457    println!("  ✓ File restoration from HEAD (checkout_file)");
458    println!("  ✓ Advanced file restoration with options (restore)");
459    println!("  ✓ Unstaging files (reset_file)");
460    println!("  ✓ File removal with various options (rm, rm_with_options)");
461    println!("  ✓ File moving and renaming (mv, mv_with_options)");
462    println!("  ✓ .gitignore pattern management (ignore_add, ignore_list, ignore_check)");
463    println!("  ✓ Staged vs working tree restoration");
464    println!("  ✓ Error handling for invalid operations");
465    println!("  ✓ Dry run and verbose options");
466    println!("  ✓ Recursive and cached removal options");
467
468    // Clean up
469    println!("\nCleaning up example repositories...");
470    fs::remove_dir_all(&base_path)?;
471    println!("File lifecycle operations example completed!");
472
473    Ok(())
474}

Trait Implementations§

Source§

impl Clone for MoveOptions

Source§

fn clone(&self) -> MoveOptions

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for MoveOptions

Source§

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

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

impl Default for MoveOptions

Source§

fn default() -> MoveOptions

Returns the “default value” for a type. 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> CloneToUninit for T
where T: Clone,

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

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

Source§

fn into(self) -> U

Calls U::from(self).

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

Source§

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

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

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

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

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

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