use ipfrs_core::{Cid, CidBuilder};
use ipfrs_tensorlogic::{
ModelCommit, ModelDiff, ModelDiffer, ModelRepository, VersionControlError,
};
use std::collections::HashMap;
fn main() -> Result<(), VersionControlError> {
println!("=== Model Versioning Example ===\n");
println!("1. Initializing repository...");
let mut repo = ModelRepository::new();
let model_v1 = create_fake_cid("model_v1");
let model_v2 = create_fake_cid("model_v2");
let model_v3 = create_fake_cid("model_v3");
println!("\n2. Creating initial commit...");
let commit1_id = create_fake_cid("commit1");
let commit1 = ModelCommit::new(
commit1_id,
vec![],
model_v1,
"Initial model: baseline CNN".to_string(),
"alice@example.com".to_string(),
)
.with_metadata("learning_rate".to_string(), "0.001".to_string())
.with_metadata("batch_size".to_string(), "32".to_string());
repo.init(commit1.clone())?;
println!(" ✓ Created commit: {}", commit1.message);
println!(" ✓ Branch: main");
println!(
" ✓ Metadata: learning_rate={}, batch_size={}",
commit1.metadata.get("learning_rate").unwrap(),
commit1.metadata.get("batch_size").unwrap()
);
println!("\n3. Creating second commit on main...");
let commit2 = repo.commit(
model_v2,
"Improved accuracy: added batch normalization".to_string(),
"alice@example.com".to_string(),
)?;
println!(" ✓ Created commit: {}", commit2.message);
if !commit2.parents.is_empty() {
println!(" ✓ Parent: {}", commit2.parents[0]);
}
println!("\n4. Creating experimental branch...");
let experiment_start = repo.head_commit().unwrap().id;
repo.create_branch(
"experiment/larger-model".to_string(),
Some(experiment_start),
)?;
repo.checkout("experiment/larger-model")?;
println!(" ✓ Created branch: experiment/larger-model");
println!(" ✓ Starting from commit: {}", commit2.message);
println!("\n5. Making changes on experimental branch...");
let commit3 = repo.commit(
model_v3,
"Experiment: doubled model size".to_string(),
"alice@example.com".to_string(),
)?;
println!(" ✓ Created commit: {}", commit3.message);
println!("\n6. Comparing models (diff between different versions)...");
let model_a = create_sample_model("baseline");
let model_b = create_sample_model("improved");
let diff = ModelDiffer::diff(&model_a, &model_b);
display_model_diff(&diff);
println!("\n7. Switching back to main branch...");
repo.checkout("main")?;
println!(" ✓ Checked out main branch");
if let Some(head) = repo.head_commit() {
println!(" ✓ Current HEAD: {}", head.id);
}
println!("\n8. Listing all branches...");
let branches = repo.list_branches();
for branch in branches {
let is_current = repo.current_branch() == Some(&branch.name);
let marker = if is_current { "*" } else { " " };
println!(" {} {}", marker, branch.name);
}
println!("\n9. Checking merge possibility...");
let can_merge = repo.can_fast_forward("experiment/larger-model")?;
if can_merge {
println!(" ✓ Can fast-forward merge experiment/larger-model into main");
println!("\n10. Merging experimental branch into main...");
repo.merge_fast_forward("experiment/larger-model")?;
println!(" ✓ Successfully merged!");
if let Some(head) = repo.head_commit() {
println!(" ✓ Main branch now points to: {}", head.id);
}
} else {
println!(" ⚠ Cannot fast-forward merge (would require 3-way merge)");
}
println!("\n11. Creating production branch...");
repo.create_branch("production".to_string(), None)?;
println!(" ✓ Created production branch");
println!("\n12. Commit history:");
if let Some(head) = repo.head_commit() {
display_commit_history(&repo, &head.id);
}
println!("\n13. Checking out specific commit (detached HEAD)...");
let commit_to_checkout = commit2.id.to_string();
repo.checkout(&commit_to_checkout)?;
println!(" ✓ Checked out commit: {}", commit2.id);
println!(" ✓ State: Detached HEAD");
println!(" ⚠ Changes made here won't be associated with any branch");
println!("\n14. Returning to main branch...");
repo.checkout("main")?;
println!(" ✓ Back on main branch");
println!("\n=== Summary ===");
println!("Total branches: {}", repo.list_branches().len());
println!(
"Current branch: {}",
repo.current_branch().unwrap_or("detached")
);
if let Some(head) = repo.head_commit() {
println!("HEAD: {}", head.id);
}
println!("\n✓ Model versioning example completed successfully!");
Ok(())
}
fn create_fake_cid(data: &str) -> Cid {
CidBuilder::new()
.build(data.as_bytes())
.expect("Failed to build CID")
}
fn create_sample_model(variant: &str) -> HashMap<String, Vec<f32>> {
let mut model = HashMap::new();
match variant {
"baseline" => {
model.insert("layer1".to_string(), vec![1.0, 2.0, 3.0, 4.0]);
model.insert("layer2".to_string(), vec![0.5, 0.6, 0.7]);
model.insert("layer3".to_string(), vec![0.1, 0.2]);
}
"improved" => {
model.insert("layer1".to_string(), vec![1.1, 2.1, 3.1, 4.1]);
model.insert("layer2".to_string(), vec![0.55, 0.65, 0.75]);
model.insert("layer4".to_string(), vec![0.8, 0.9]); }
_ => {}
}
model
}
fn display_model_diff(diff: &ModelDiff) {
if !diff.added_layers.is_empty() {
println!(" Added layers:");
for name in &diff.added_layers {
println!(" + {}", name);
}
}
if !diff.removed_layers.is_empty() {
println!(" Removed layers:");
for name in &diff.removed_layers {
println!(" - {}", name);
}
}
if !diff.modified_layers.is_empty() {
println!(" Modified layers:");
for layer in &diff.modified_layers {
println!(
" ~ {} (L2: {:.4}, max: {:.4})",
layer.name, layer.l2_diff, layer.max_diff
);
}
}
if diff.added_layers.is_empty()
&& diff.removed_layers.is_empty()
&& diff.modified_layers.is_empty()
{
println!(" (no differences)");
}
}
fn display_commit_history(repo: &ModelRepository, start_cid: &Cid) {
let history = repo.get_history(start_cid, Some(10));
if history.is_empty() {
println!(" (no commits)");
return;
}
for commit in history {
println!(" * {} ({})", commit.message, commit.id);
println!(" Author: {}", commit.author);
println!(
" Date: {}",
chrono::DateTime::from_timestamp(commit.timestamp, 0)
.map(|dt| dt.format("%Y-%m-%d %H:%M:%S").to_string())
.unwrap_or_else(|| "unknown".to_string())
);
if !commit.metadata.is_empty() {
println!(" Metadata:");
for (key, value) in &commit.metadata {
println!(" {}: {}", key, value);
}
}
println!();
}
}