1use chrono::{Duration, Utc};
2use rustic_git::{LogOptions, Repository, Result};
3use std::{env, fs};
4
5fn main() -> Result<()> {
6 let test_path = env::temp_dir().join("rustic_git_commit_history_example");
7
8 if test_path.exists() {
10 fs::remove_dir_all(&test_path).unwrap();
11 }
12
13 let repo = Repository::init(&test_path, false)?;
15 println!("Created repository at: {}", test_path.display());
16
17 println!("\n=== Building Commit History ===");
19
20 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 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 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 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 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 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 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 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 println!("\n=== Advanced Filtering ===");
103
104 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 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 println!("\n=== LogOptions Advanced Queries ===");
118
119 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 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 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 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 println!("\n=== Search Operations ===");
192
193 if let Some(found) = all_commits.find_by_hash(&commit2) {
195 println!("Found commit by full hash: {}", found.message.subject);
196 }
197
198 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 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 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 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 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 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 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 fs::remove_dir_all(&test_path).unwrap();
304 println!("\nCleaned up test repository");
305
306 Ok(())
307}