status_checking/
status_checking.rs

1//! Status Checking Example
2//!
3//! This example demonstrates comprehensive status checking capabilities:
4//! - Check clean vs dirty repository states
5//! - Explore all FileStatus variants
6//! - Use different status query methods
7//! - Handle repositories with various file states
8//!
9//! Run with: cargo run --example status_checking
10
11use rustic_git::{IndexStatus, Repository, Result, WorktreeStatus};
12use std::env;
13use std::fs;
14
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}
248
249/// Display a summary of the repository status
250fn display_status_summary(status: &rustic_git::GitStatus) {
251    if status.is_clean() {
252        println!("   Repository is clean (no changes)");
253    } else {
254        println!("   Repository has {} changes", status.entries.len());
255        println!("      Unstaged: {}", status.unstaged_files().count());
256        println!("      Untracked: {}", status.untracked_entries().count());
257    }
258}
259
260/// Display detailed status information
261fn display_detailed_status(status: &rustic_git::GitStatus) {
262    if !status.entries.is_empty() {
263        println!("   Detailed file status:");
264        for entry in &status.entries {
265            let index_marker = match entry.index_status {
266                IndexStatus::Modified => "[M]",
267                IndexStatus::Added => "[A]",
268                IndexStatus::Deleted => "[D]",
269                IndexStatus::Renamed => "[R]",
270                IndexStatus::Copied => "[C]",
271                IndexStatus::Clean => "[ ]",
272            };
273            let worktree_marker = match entry.worktree_status {
274                WorktreeStatus::Modified => "[M]",
275                WorktreeStatus::Deleted => "[D]",
276                WorktreeStatus::Untracked => "[?]",
277                WorktreeStatus::Ignored => "[I]",
278                WorktreeStatus::Clean => "[ ]",
279            };
280            println!(
281                "      {}{} Index {:?}, Worktree {:?}: {}",
282                index_marker,
283                worktree_marker,
284                entry.index_status,
285                entry.worktree_status,
286                entry.path.display()
287            );
288        }
289    }
290}