use rustic_git::{Repository, Result, StashApplyOptions, StashOptions};
use std::env;
use std::fs;
fn main() -> Result<()> {
println!("Rustic Git - Stash Operations Example\n");
let repo_path = env::temp_dir().join("rustic_git_stash_example");
if repo_path.exists() {
fs::remove_dir_all(&repo_path).expect("Failed to clean up previous example");
}
println!("Initializing repository at: {}", repo_path.display());
let repo = Repository::init(&repo_path, false)?;
repo.config()
.set_user("Stash Demo User", "stash@example.com")?;
println!("\nCreating initial commit...");
fs::write(
repo_path.join("README.md"),
"# Stash Demo Project\n\nDemonstrating Git stash operations.\n",
)?;
repo.add(&["README.md"])?;
let initial_commit = repo.commit("Initial commit: Add README")?;
println!("Created initial commit: {}", initial_commit.short());
println!("\n=== Creating Work to Stash ===");
fs::write(
repo_path.join("README.md"),
"# Stash Demo Project\n\nDemonstrating Git stash operations.\n\nAdded some new content!\n",
)?;
fs::create_dir_all(repo_path.join("src"))?;
fs::write(
repo_path.join("src/main.rs"),
"fn main() {\n println!(\"Hello, stash!\");\n}\n",
)?;
fs::write(
repo_path.join("untracked.txt"),
"This file is not tracked by git\n",
)?;
fs::write(repo_path.join("temp.log"), "Temporary log file\n")?;
repo.add(&["src/main.rs"])?;
println!("Created various types of changes:");
println!(" - Modified tracked file (README.md)");
println!(" - Added new file and staged it (src/main.rs)");
println!(" - Created untracked files (untracked.txt, temp.log)");
let status = repo.status()?;
println!("\nRepository status before stashing:");
println!(" Staged files: {}", status.staged_files().count());
println!(" Unstaged files: {}", status.unstaged_files().count());
println!(" Untracked files: {}", status.untracked_entries().count());
println!("\n=== Creating Stashes ===");
println!("\n1. Creating simple stash:");
let simple_stash = repo.stash_save("WIP: working on main function")?;
println!("Created stash: {}", simple_stash);
println!(" Index: {}", simple_stash.index);
println!(" Branch: {}", simple_stash.branch);
println!(" Hash: {}", simple_stash.hash.short());
let status_after_stash = repo.status()?;
println!("\nStatus after simple stash:");
println!(
" Staged files: {}",
status_after_stash.staged_files().count()
);
println!(
" Unstaged files: {}",
status_after_stash.unstaged_files().count()
);
println!(
" Untracked files: {}",
status_after_stash.untracked_entries().count()
);
println!("\n2. Creating stash with untracked files:");
fs::write(
repo_path.join("README.md"),
"# Stash Demo Project\n\nDemonstrating Git stash operations.\n\nSecond round of changes!\n",
)?;
fs::write(repo_path.join("config.json"), "{\"debug\": true}\n")?;
let untracked_options = StashOptions::new().with_untracked().with_keep_index();
let untracked_stash = repo.stash_push(
"WIP: config changes with untracked files",
untracked_options,
)?;
println!("Created stash with untracked files: {}", untracked_stash);
println!("\n3. Creating stash with specific paths:");
fs::write(repo_path.join("file1.txt"), "Content for file 1\n")?;
fs::write(repo_path.join("file2.txt"), "Content for file 2\n")?;
fs::write(repo_path.join("file3.txt"), "Content for file 3\n")?;
repo.add(&["file1.txt", "file2.txt", "file3.txt"])?;
fs::write(repo_path.join("file1.txt"), "Modified content for file 1\n")?;
fs::write(repo_path.join("file2.txt"), "Modified content for file 2\n")?;
fs::write(repo_path.join("file3.txt"), "Modified content for file 3\n")?;
let path_options = StashOptions::new().with_paths(vec!["file1.txt".into(), "file2.txt".into()]);
let path_stash = repo.stash_push("WIP: specific files only", path_options)?;
println!("Created stash with specific paths: {}", path_stash);
println!("\n=== Stash Listing and Filtering ===");
let stashes = repo.stash_list()?;
println!("\nAll stashes ({} total):", stashes.len());
for stash in stashes.iter() {
println!(
" [{}] {} -> {}",
stash.index,
stash.message,
stash.hash.short()
);
println!(
" Branch: {} | Created: {}",
stash.branch,
stash.timestamp.format("%Y-%m-%d %H:%M:%S")
);
}
println!("\nFiltering examples:");
let wip_stashes: Vec<_> = stashes.find_containing("WIP").collect();
println!("Stashes containing 'WIP': {} found", wip_stashes.len());
for stash in &wip_stashes {
println!(" - {}", stash.message);
}
let config_stashes: Vec<_> = stashes.find_containing("config").collect();
println!(
"Stashes containing 'config': {} found",
config_stashes.len()
);
if let Some(latest) = stashes.latest() {
println!("Latest stash: {}", latest.message);
}
if let Some(second_stash) = stashes.get(1) {
println!("Second stash: {}", second_stash.message);
}
println!("\n=== Viewing Stash Contents ===");
println!("\nShowing contents of latest stash:");
let stash_contents = repo.stash_show(0)?;
println!("{}", stash_contents);
println!("\n=== Applying and Popping Stashes ===");
println!("\n1. Testing stash apply (keeps stash in list):");
let stashes_before_apply = repo.stash_list()?;
println!("Stashes before apply: {}", stashes_before_apply.len());
repo.stash_apply(0, StashApplyOptions::new())?;
println!("Applied stash@{{0}}");
let stashes_after_apply = repo.stash_list()?;
println!("Stashes after apply: {}", stashes_after_apply.len());
let status_after_apply = repo.status()?;
println!("Status after apply:");
println!(
" Staged files: {}",
status_after_apply.staged_files().count()
);
println!(
" Unstaged files: {}",
status_after_apply.unstaged_files().count()
);
println!(
" Untracked files: {}",
status_after_apply.untracked_entries().count()
);
println!("\n2. Testing stash pop (removes stash from list):");
repo.stash_save("Temporary stash for pop test")?;
let stashes_before_pop = repo.stash_list()?;
println!("Stashes before pop: {}", stashes_before_pop.len());
repo.stash_pop(0, StashApplyOptions::new().with_quiet())?;
println!("Popped stash@{{0}}");
let stashes_after_pop = repo.stash_list()?;
println!("Stashes after pop: {}", stashes_after_pop.len());
println!("\n3. Testing apply with index restoration:");
fs::write(repo_path.join("staged_file.txt"), "This will be staged\n")?;
repo.add(&["staged_file.txt"])?;
fs::write(
repo_path.join("unstaged_file.txt"),
"This will be unstaged\n",
)?;
repo.stash_save("Stash with staged and unstaged changes")?;
let apply_options = StashApplyOptions::new().with_index();
repo.stash_apply(0, apply_options)?;
println!("Applied stash with index restoration");
let final_status = repo.status()?;
println!("Final status after index restoration:");
println!(" Staged files: {}", final_status.staged_files().count());
println!(
" Unstaged files: {}",
final_status.unstaged_files().count()
);
println!("\n=== Stash Management ===");
for i in 1..=3 {
fs::write(
repo_path.join(format!("test{}.txt", i)),
format!("Test content {}\n", i),
)?;
repo.stash_save(&format!("Test stash {}", i))?;
}
let management_stashes = repo.stash_list()?;
println!(
"\nCreated {} test stashes for management demo",
management_stashes.len()
);
println!("\n1. Dropping middle stash:");
println!("Before drop: {} stashes", management_stashes.len());
repo.stash_drop(1)?; println!("Dropped stash@{{1}}");
let after_drop = repo.stash_list()?;
println!("After drop: {} stashes", after_drop.len());
println!("Remaining stashes:");
for stash in after_drop.iter() {
println!(" [{}] {}", stash.index, stash.message);
}
println!("\n2. Clearing all stashes:");
repo.stash_clear()?;
println!("Cleared all stashes");
let final_stashes = repo.stash_list()?;
println!("Stashes after clear: {}", final_stashes.len());
println!("\n=== Error Handling ===");
println!("\n1. Testing operations on empty stash list:");
match repo.stash_apply(0, StashApplyOptions::new()) {
Ok(_) => println!("ERROR: Should have failed to apply non-existent stash"),
Err(e) => println!("Expected error applying non-existent stash: {}", e),
}
match repo.stash_show(0) {
Ok(_) => println!("ERROR: Should have failed to show non-existent stash"),
Err(e) => println!("Expected error showing non-existent stash: {}", e),
}
match repo.stash_drop(0) {
Ok(_) => println!("ERROR: Should have failed to drop non-existent stash"),
Err(e) => println!("Expected error dropping non-existent stash: {}", e),
}
println!("\n=== Summary ===");
println!("\nStash operations demonstrated:");
println!(" ✓ Basic stash save and push with options");
println!(" ✓ Stash with untracked files and keep-index");
println!(" ✓ Stash specific paths only");
println!(" ✓ Comprehensive stash listing and filtering");
println!(" ✓ Stash content viewing");
println!(" ✓ Apply vs pop operations");
println!(" ✓ Index restoration during apply");
println!(" ✓ Stash dropping and clearing");
println!(" ✓ Error handling for edge cases");
println!("\nStash options demonstrated:");
println!(" ✓ with_untracked() - Include untracked files");
println!(" ✓ with_keep_index() - Keep staged changes");
println!(" ✓ with_paths() - Stash specific files only");
println!(" ✓ with_index() - Restore staged state on apply");
println!(" ✓ with_quiet() - Suppress output messages");
println!("\nStash filtering demonstrated:");
println!(" ✓ find_containing() - Search by message content");
println!(" ✓ latest() - Get most recent stash");
println!(" ✓ get() - Get stash by index");
println!(" ✓ for_branch() - Filter by branch name");
println!("\nCleaning up example repository...");
fs::remove_dir_all(&repo_path)?;
println!("Stash operations example completed successfully!");
Ok(())
}