use rustic_git::{Repository, Result, TagOptions, TagType};
use std::env;
use std::fs;
fn main() -> Result<()> {
println!("Rustic Git - Tag Operations Example\n");
let repo_path = env::temp_dir().join("rustic_git_tag_example");
if repo_path.exists() {
fs::remove_dir_all(&repo_path).expect("Failed to clean up previous example");
}
println!("Initializing repository at: {}", repo_path.display());
let repo = Repository::init(&repo_path, false)?;
repo.config()
.set_user("Tag Demo User", "tags@example.com")?;
println!("\nCreating initial commits...");
fs::write(
repo_path.join("README.md"),
"# Tag Demo Project\n\nDemonstrating Git tag operations.\n",
)?;
repo.add(&["README.md"])?;
let first_commit_hash = repo.commit("Initial commit: Add README")?;
println!("Created commit: {}", first_commit_hash.short());
fs::create_dir_all(repo_path.join("src"))?;
fs::write(
repo_path.join("src/main.rs"),
"fn main() {\n println!(\"Hello, tags!\");\n}\n",
)?;
repo.add(&["src/main.rs"])?;
let second_commit_hash = repo.commit("Add main.rs with hello world")?;
println!("Created commit: {}", second_commit_hash.short());
fs::write(
repo_path.join("src/lib.rs"),
"//! Tag demo library\n\npub fn greet(name: &str) -> String {\n format!(\"Hello, {}!\", name)\n}\n",
)?;
repo.add(&["src/lib.rs"])?;
let third_commit_hash = repo.commit("Add library with greet function")?;
println!("Created commit: {}", third_commit_hash.short());
println!("\n=== Creating Tags ===");
println!("\n1. Creating lightweight tags:");
let v0_1_0 = repo.create_tag("v0.1.0", Some(&first_commit_hash))?;
println!(
"Created lightweight tag: {} -> {} ({})",
v0_1_0.name,
v0_1_0.hash.short(),
v0_1_0.tag_type
);
let v0_2_0 = repo.create_tag("v0.2.0", Some(&second_commit_hash))?;
println!(
"Created lightweight tag: {} -> {} ({})",
v0_2_0.name,
v0_2_0.hash.short(),
v0_2_0.tag_type
);
println!("\n2. Creating annotated tags:");
let options =
TagOptions::new().with_message("First stable release with basic functionality".to_string());
let v1_0_0 = repo.create_tag_with_options("v1.0.0", Some(&third_commit_hash), options)?;
println!(
"Created annotated tag: {} -> {} ({})",
v1_0_0.name,
v1_0_0.hash.short(),
v1_0_0.tag_type
);
if let Some(message) = &v1_0_0.message {
println!(" Message: {}", message);
}
let latest_options = TagOptions::new().with_message("Latest development version".to_string());
let latest_tag = repo.create_tag_with_options("latest", None, latest_options)?;
println!(
"Created annotated tag on HEAD: {} -> {} ({})",
latest_tag.name,
latest_tag.hash.short(),
latest_tag.tag_type
);
println!("\n3. Creating feature and release candidate tags:");
let feature_options = TagOptions::new().with_message("Feature branch snapshot".to_string());
repo.create_tag_with_options("feature/demo", None, feature_options)?;
let rc_options = TagOptions::new().with_message("Release candidate for v1.1.0".to_string());
repo.create_tag_with_options("v1.1.0-rc1", None, rc_options)?;
repo.create_tag("v0.3.0", None)?;
repo.create_tag("v0.9.0", None)?;
println!("\n=== Tag Listing and Filtering ===");
let tags = repo.tags()?;
println!("\nAll tags ({} total):", tags.len());
for tag in tags.iter() {
let type_marker = match tag.tag_type {
TagType::Lightweight => "L",
TagType::Annotated => "A",
};
println!(" [{}] {} -> {}", type_marker, tag.name, tag.hash.short());
if let Some(message) = &tag.message {
println!(" Message: {}", message.lines().next().unwrap_or(""));
}
}
println!("\nLightweight tags ({} total):", tags.lightweight_count());
for tag in tags.lightweight() {
println!(" {} -> {}", tag.name, tag.hash.short());
}
println!("\nAnnotated tags ({} total):", tags.annotated_count());
for tag in tags.annotated() {
println!(" {} -> {}", tag.name, tag.hash.short());
if let Some(message) = &tag.message {
println!(" Message: {}", message.lines().next().unwrap_or(""));
}
}
println!("\n=== Tag Searching ===");
if let Some(tag) = tags.find("v1.0.0") {
println!("\nFound tag 'v1.0.0':");
println!(" Type: {}", tag.tag_type);
println!(" Hash: {}", tag.hash.short());
if let Some(message) = &tag.message {
println!(" Message: {}", message);
}
}
let version_tags: Vec<_> = tags.find_containing("v").collect();
println!(
"\nVersion tags (containing 'v'): {} found",
version_tags.len()
);
for tag in &version_tags {
println!(" {}", tag.name);
}
let rc_tags: Vec<_> = tags.find_containing("rc").collect();
println!("\nRelease candidate tags: {} found", rc_tags.len());
for tag in &rc_tags {
println!(" {}", tag.name);
}
let tags_for_third_commit: Vec<_> = tags.for_commit(&third_commit_hash).collect();
println!(
"\nTags pointing to commit {}: {} found",
third_commit_hash.short(),
tags_for_third_commit.len()
);
for tag in &tags_for_third_commit {
println!(" {}", tag.name);
}
println!("\n=== Tag Details ===");
let detailed_tag = repo.show_tag("v1.0.0")?;
println!("\nDetailed information for 'v1.0.0':");
println!(" Name: {}", detailed_tag.name);
println!(" Type: {}", detailed_tag.tag_type);
println!(" Commit: {}", detailed_tag.hash);
println!(" Short hash: {}", detailed_tag.hash.short());
if let Some(message) = &detailed_tag.message {
println!(" Message: {}", message);
}
if let Some(tagger) = &detailed_tag.tagger {
println!(" Tagger: {}", tagger);
println!(
" Tagged at: {}",
tagger.timestamp.format("%Y-%m-%d %H:%M:%S UTC")
);
}
if let Some(timestamp) = &detailed_tag.timestamp {
println!(" Timestamp: {}", timestamp.format("%Y-%m-%d %H:%M:%S UTC"));
}
println!("\n=== Tag Operations ===");
println!("\n1. Testing tag overwrite:");
match repo.create_tag("latest", None) {
Ok(_) => println!(" ERROR: Should have failed to create existing tag"),
Err(e) => println!(" Expected error creating existing tag: {}", e),
}
let force_options = TagOptions::new()
.with_force()
.with_message("Forcefully updated latest tag".to_string());
match repo.create_tag_with_options("latest", None, force_options) {
Ok(tag) => println!(" Successfully force-created tag: {}", tag.name),
Err(e) => println!(" Error force-creating tag: {}", e),
}
println!("\n2. Testing tag deletion:");
repo.create_tag("temp-tag", None)?;
println!(" Created temporary tag: temp-tag");
let tags_before = repo.tags()?;
let temp_exists_before = tags_before.find("temp-tag").is_some();
println!(" Temp tag exists before deletion: {}", temp_exists_before);
repo.delete_tag("temp-tag")?;
println!(" Deleted temp-tag");
let tags_after = repo.tags()?;
let temp_exists_after = tags_after.find("temp-tag").is_some();
println!(" Temp tag exists after deletion: {}", temp_exists_after);
println!("\n=== Summary ===");
let final_tags = repo.tags()?;
println!("\nFinal repository state:");
println!(" Total tags: {}", final_tags.len());
println!(" Lightweight tags: {}", final_tags.lightweight_count());
println!(" Annotated tags: {}", final_tags.annotated_count());
println!("\nTag creation options demonstrated:");
println!(" ✓ Lightweight tags (simple references)");
println!(" ✓ Annotated tags (with messages and metadata)");
println!(" ✓ Tags on specific commits");
println!(" ✓ Tags on current HEAD");
println!(" ✓ Force tag creation/overwrite");
println!("\nTag listing and filtering demonstrated:");
println!(" ✓ List all tags");
println!(" ✓ Filter by tag type (lightweight/annotated)");
println!(" ✓ Search by name patterns");
println!(" ✓ Find tags by commit hash");
println!(" ✓ Show detailed tag information");
println!("\nTag management demonstrated:");
println!(" ✓ Tag creation with options");
println!(" ✓ Tag deletion");
println!(" ✓ Error handling for duplicate tags");
println!("\nCleaning up example repository...");
fs::remove_dir_all(&repo_path)?;
println!("Tag operations example completed successfully!");
Ok(())
}