CommitLog

Struct CommitLog 

Source
pub struct CommitLog { /* private fields */ }

Implementations§

Source§

impl CommitLog

Source

pub fn new(commits: Vec<Commit>) -> Self

Create a new CommitLog from a vector of commits

Source

pub fn all(&self) -> &[Commit]

Get all commits

Source

pub fn iter(&self) -> impl Iterator<Item = &Commit>

Get an iterator over all commits

Examples found in repository?
examples/merge_operations.rs (line 146)
96fn demonstrate_no_fast_forward_merge(repo: &Repository, temp_dir: &std::path::Path) -> Result<()> {
97    println!("\n--- Demonstrating No-Fast-Forward Merge ---\n");
98
99    // Add a commit to master to prevent fast-forward
100    println!("1. Adding commit to master...");
101    let readme_path = temp_dir.join("README.md");
102    fs::write(
103        &readme_path,
104        "# Project\n\nInitial content\n\n## Updates\nAdded documentation",
105    )?;
106    repo.add(&["README.md"])?;
107    let master_commit = repo.commit("Update documentation")?;
108    println!("   Master commit: {}", master_commit);
109
110    // Create another feature branch
111    println!("\n2. Creating another feature branch...");
112    repo.checkout_new("feature/no-ff", None)?;
113
114    let config_path = temp_dir.join("config.yaml");
115    fs::write(&config_path, "app:\n  name: example\n  version: 1.0")?;
116    repo.add(&["config.yaml"])?;
117    let config_commit = repo.commit("Add configuration file")?;
118    println!("   Config commit: {}", config_commit);
119
120    // Switch back to master
121    println!("\n3. Switching back to master...");
122    let branches = repo.branches()?;
123    let master_branch = branches.find("master").unwrap();
124    repo.checkout(master_branch)?;
125
126    // Perform no-fast-forward merge
127    println!("\n4. Performing no-fast-forward merge...");
128    let options = MergeOptions::new()
129        .with_fast_forward(FastForwardMode::Never)
130        .with_message("Merge feature/no-ff into master".to_string());
131
132    let merge_status = repo.merge_with_options("feature/no-ff", options)?;
133
134    match merge_status {
135        MergeStatus::Success(hash) => {
136            println!("   ✓ Merge commit created!");
137            println!("   Merge commit: {}", hash);
138            println!("   Created explicit merge commit preserving branch history");
139        }
140        _ => println!("   Unexpected merge result: {:?}", merge_status),
141    }
142
143    // Show the commit history
144    println!("\n5. Recent commit history:");
145    let commits = repo.recent_commits(3)?;
146    for (i, commit) in commits.iter().enumerate() {
147        println!(
148            "   {}: {} - {}",
149            i + 1,
150            commit.hash.short(),
151            commit.message.subject
152        );
153    }
154
155    Ok(())
156}
157
158fn demonstrate_merge_conflicts(repo: &Repository, temp_dir: &std::path::Path) -> Result<()> {
159    println!("\n--- Demonstrating Merge Conflicts ---\n");
160
161    // Create conflicting branch
162    println!("1. Creating branch with conflicting changes...");
163    repo.checkout_new("feature/conflict", None)?;
164
165    // Modify the same file differently
166    let readme_path = temp_dir.join("README.md");
167    fs::write(
168        &readme_path,
169        "# Project\n\nFeature branch changes\n\n## Updates\nAdded documentation",
170    )?;
171    repo.add(&["README.md"])?;
172    let feature_commit = repo.commit("Update README from feature branch")?;
173    println!("   Feature commit: {}", feature_commit);
174
175    // Switch back to master and make conflicting change
176    println!("\n2. Making conflicting change on master...");
177    let branches = repo.branches()?;
178    let master_branch = branches.find("master").unwrap();
179    repo.checkout(master_branch)?;
180
181    fs::write(
182        &readme_path,
183        "# Project\n\nMaster branch changes\n\n## Updates\nAdded documentation",
184    )?;
185    repo.add(&["README.md"])?;
186    let master_conflict_commit = repo.commit("Update README from master")?;
187    println!("   Master commit: {}", master_conflict_commit);
188
189    // Attempt merge (will have conflicts)
190    println!("\n3. Attempting merge (will have conflicts)...");
191    let merge_status = repo.merge("feature/conflict")?;
192
193    match merge_status {
194        MergeStatus::Conflicts(files) => {
195            println!("   ⚠️  Merge conflicts detected!");
196            println!("   Conflicted files:");
197            for file in &files {
198                println!("     - {}", file.display());
199            }
200
201            // Check merge in progress
202            if repo.merge_in_progress()? {
203                println!("   ✓ Merge in progress status detected");
204            }
205
206            // Show conflict markers in file
207            println!("\n4. Conflict markers in README.md:");
208            let content = fs::read_to_string(&readme_path)?;
209            for (i, line) in content.lines().enumerate() {
210                if line.starts_with("<<<<<<< ")
211                    || line.starts_with("======= ")
212                    || line.starts_with(">>>>>>> ")
213                {
214                    println!("     {}: {} <-- conflict marker", i + 1, line);
215                } else {
216                    println!("     {}: {}", i + 1, line);
217                }
218            }
219
220            // Abort the merge
221            println!("\n5. Aborting merge...");
222            repo.abort_merge()?;
223            println!("   ✓ Merge aborted successfully");
224
225            // Verify merge is no longer in progress
226            if !repo.merge_in_progress()? {
227                println!("   ✓ Repository is back to clean state");
228            }
229        }
230        _ => println!("   Unexpected merge result: {:?}", merge_status),
231    }
232
233    Ok(())
234}
235
236fn demonstrate_merge_status_and_abort(repo: &Repository, temp_dir: &std::path::Path) -> Result<()> {
237    println!("\n--- Demonstrating Merge Status and Options ---\n");
238
239    // Create a simple feature branch
240    println!("1. Creating simple feature branch...");
241    repo.checkout_new("feature/simple", None)?;
242
243    let simple_path = temp_dir.join("simple.txt");
244    fs::write(&simple_path, "Simple feature content")?;
245    repo.add(&["simple.txt"])?;
246    repo.commit("Add simple feature")?;
247
248    // Switch back to master
249    let branches = repo.branches()?;
250    let master_branch = branches.find("master").unwrap();
251    repo.checkout(master_branch)?;
252
253    // Test merge with different options
254    println!("\n2. Testing merge with custom options...");
255    let options = MergeOptions::new()
256        .with_fast_forward(FastForwardMode::Auto)
257        .with_message("Integrate simple feature".to_string());
258
259    let merge_status = repo.merge_with_options("feature/simple", options)?;
260
261    match merge_status {
262        MergeStatus::FastForward(hash) => {
263            println!("   ✓ Fast-forward merge completed: {}", hash);
264        }
265        MergeStatus::Success(hash) => {
266            println!("   ✓ Merge commit created: {}", hash);
267        }
268        MergeStatus::UpToDate => {
269            println!("   ✓ Already up to date");
270        }
271        MergeStatus::Conflicts(_) => {
272            println!("   ⚠️  Unexpected conflicts");
273        }
274    }
275
276    // Show final repository state
277    println!("\n3. Final repository state:");
278    let status = repo.status()?;
279    println!(
280        "   Working directory clean: {}",
281        status.staged_files().count() == 0 && status.unstaged_files().count() == 0
282    );
283
284    let commits = repo.recent_commits(5)?;
285    println!("   Recent commits:");
286    for (i, commit) in commits.iter().enumerate() {
287        println!(
288            "     {}: {} - {}",
289            i + 1,
290            commit.hash.short(),
291            commit.message.subject
292        );
293    }
294
295    Ok(())
296}
More examples
Hide additional examples
examples/commit_history.rs (line 86)
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

pub fn by_author(&self, author: &str) -> impl Iterator<Item = &Commit>

Get commits by a specific author

Source

pub fn since(&self, date: DateTime<Utc>) -> impl Iterator<Item = &Commit>

Get commits since a specific date

Examples found in repository?
examples/commit_history.rs (line 113)
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

pub fn until(&self, date: DateTime<Utc>) -> impl Iterator<Item = &Commit>

Get commits until a specific date

Source

pub fn with_message_containing( &self, text: &str, ) -> impl Iterator<Item = &Commit>

Get commits with message containing text

Examples found in repository?
examples/commit_history.rs (line 105)
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

pub fn merges_only(&self) -> impl Iterator<Item = &Commit>

Get only merge commits

Examples found in repository?
examples/commit_history.rs (line 166)
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

pub fn no_merges(&self) -> impl Iterator<Item = &Commit>

Get commits excluding merges

Examples found in repository?
examples/commit_history.rs (line 167)
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

pub fn find_by_hash(&self, hash: &Hash) -> Option<&Commit>

Find commit by full hash

Examples found in repository?
examples/commit_history.rs (line 194)
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

pub fn find_by_short_hash(&self, short: &str) -> Option<&Commit>

Find commit by short hash

Examples found in repository?
examples/commit_history.rs (line 199)
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

pub fn is_empty(&self) -> bool

Check if the log is empty

Source

pub fn len(&self) -> usize

Get the count of commits

Examples found in repository?
examples/commit_history.rs (line 83)
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

pub fn first(&self) -> Option<&Commit>

Get the first (most recent) commit

Examples found in repository?
examples/commit_history.rs (line 174)
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

pub fn last(&self) -> Option<&Commit>

Get the last (oldest) commit

Examples found in repository?
examples/commit_history.rs (line 182)
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}

Trait Implementations§

Source§

impl Clone for CommitLog

Source§

fn clone(&self) -> CommitLog

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for CommitLog

Source§

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

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

impl Display for CommitLog

Source§

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

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

impl PartialEq for CommitLog

Source§

fn eq(&self, other: &CommitLog) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl StructuralPartialEq for CommitLog

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

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

Source§

fn into(self) -> U

Calls U::from(self).

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

Source§

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

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

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

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

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

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

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

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.