pub struct Repository { /* private fields */ }
Implementations§
Source§impl Repository
impl Repository
Sourcepub fn add<P: AsRef<Path>>(&self, paths: &[P]) -> Result<()>
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?
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
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}
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}
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}
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}
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}
Sourcepub fn add_all(&self) -> Result<()>
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?
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
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}
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}
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}
Sourcepub fn add_update(&self) -> Result<()>
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?
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
impl Repository
Sourcepub fn branches(&self) -> Result<BranchList>
pub fn branches(&self) -> Result<BranchList>
List all branches in the repository
Examples found in repository?
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}
Sourcepub fn current_branch(&self) -> Result<Option<Branch>>
pub fn current_branch(&self) -> Result<Option<Branch>>
Get the current branch
Examples found in repository?
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}
Sourcepub fn create_branch(
&self,
name: &str,
start_point: Option<&str>,
) -> Result<Branch>
pub fn create_branch( &self, name: &str, start_point: Option<&str>, ) -> Result<Branch>
Create a new branch
Examples found in repository?
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}
Sourcepub fn delete_branch(&self, branch: &Branch, force: bool) -> Result<()>
pub fn delete_branch(&self, branch: &Branch, force: bool) -> Result<()>
Delete a branch
Examples found in repository?
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}
Sourcepub fn checkout(&self, branch: &Branch) -> Result<()>
pub fn checkout(&self, branch: &Branch) -> Result<()>
Switch to an existing branch
Examples found in repository?
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}
Sourcepub fn checkout_new(
&self,
name: &str,
start_point: Option<&str>,
) -> Result<Branch>
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?
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
impl Repository
Sourcepub fn commit(&self, message: &str) -> Result<Hash>
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?
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
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}
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}
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}
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}
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}
Create a commit with the given message and author.
§Arguments
message
- The commit messageauthor
- 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?
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
impl Repository
Sourcepub fn log(&self) -> Result<CommitLog>
pub fn log(&self) -> Result<CommitLog>
Get commit history with default options
Examples found in repository?
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}
Sourcepub fn recent_commits(&self, count: usize) -> Result<CommitLog>
pub fn recent_commits(&self, count: usize) -> Result<CommitLog>
Get recent N commits
Examples found in repository?
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}
Sourcepub fn log_with_options(&self, options: &LogOptions) -> Result<CommitLog>
pub fn log_with_options(&self, options: &LogOptions) -> Result<CommitLog>
Get commit history with custom options
Examples found in repository?
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}
Sourcepub fn log_range(&self, from: &Hash, to: &Hash) -> Result<CommitLog>
pub fn log_range(&self, from: &Hash, to: &Hash) -> Result<CommitLog>
Get commits in a range between two commits
Examples found in repository?
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}
Sourcepub fn log_for_paths(&self, paths: &[impl AsRef<Path>]) -> Result<CommitLog>
pub fn log_for_paths(&self, paths: &[impl AsRef<Path>]) -> Result<CommitLog>
Get commits that affected specific paths
Examples found in repository?
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}
Sourcepub fn show_commit(&self, hash: &Hash) -> Result<CommitDetails>
pub fn show_commit(&self, hash: &Hash) -> Result<CommitDetails>
Get detailed information about a specific commit
Examples found in repository?
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
impl Repository
Sourcepub fn status(&self) -> Result<GitStatus>
pub fn status(&self) -> Result<GitStatus>
Examples found in repository?
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
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}
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(®ular_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(®ular_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}
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}
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}
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
impl Repository
Sourcepub fn ensure_git() -> Result<()>
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
.
Sourcepub fn open<P: AsRef<Path>>(path: P) -> Result<Self>
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?
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
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(®ular_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(®ular_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}
Sourcepub fn init<P: AsRef<Path>>(path: P, bare: bool) -> Result<Self>
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?
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
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}
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}
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(®ular_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(®ular_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}
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}
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}
Sourcepub fn repo_path(&self) -> &Path
pub fn repo_path(&self) -> &Path
Examples found in repository?
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(®ular_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(®ular_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}
Sourcepub fn config(&self) -> RepoConfig<'_>
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?
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}