use rustic_git::{FastForwardMode, MergeOptions, MergeStatus, Repository, Result};
use std::{env, fs};
fn main() -> Result<()> {
println!("=== Merge Operations Demo ===\n");
let temp_dir = env::temp_dir().join("rustic_git_merge_demo");
if temp_dir.exists() {
fs::remove_dir_all(&temp_dir)?;
}
println!("Working in temporary directory: {:?}\n", temp_dir);
let repo = Repository::init(&temp_dir, false)?;
repo.config().set_user("Example User", "example@test.com")?;
demonstrate_fast_forward_merge(&repo, &temp_dir)?;
demonstrate_no_fast_forward_merge(&repo, &temp_dir)?;
demonstrate_merge_conflicts(&repo, &temp_dir)?;
demonstrate_merge_status_and_abort(&repo, &temp_dir)?;
println!("\n=== Merge Operations Demo Complete ===");
fs::remove_dir_all(&temp_dir)?;
Ok(())
}
fn demonstrate_fast_forward_merge(repo: &Repository, temp_dir: &std::path::Path) -> Result<()> {
println!("--- Demonstrating Fast-Forward Merge ---\n");
println!("1. Creating initial commit on master...");
let file1_path = temp_dir.join("README.md");
fs::write(&file1_path, "# Project\n\nInitial content")?;
repo.add(&["README.md"])?;
let initial_commit = repo.commit("Initial commit")?;
println!(" Created commit: {}", initial_commit);
println!("\n2. Creating feature branch and adding commits...");
repo.checkout_new("feature/fast-forward", None)?;
let file2_path = temp_dir.join("feature.txt");
fs::write(&file2_path, "New feature implementation")?;
repo.add(&["feature.txt"])?;
let feature_commit = repo.commit("Add new feature")?;
println!(" Feature commit: {}", feature_commit);
println!("\n3. Switching back to master...");
let branches = repo.branches()?;
let master_branch = branches.find("master").unwrap();
repo.checkout(master_branch)?;
println!(" Switched to master");
println!("\n4. Performing fast-forward merge...");
let merge_status = repo.merge("feature/fast-forward")?;
match merge_status {
MergeStatus::FastForward(hash) => {
println!(" ✓ Fast-forward merge completed!");
println!(" New HEAD: {}", hash);
println!(" Both files are now present on master");
}
_ => println!(" Unexpected merge result: {:?}", merge_status),
}
println!(" Files in repository:");
for file in ["README.md", "feature.txt"] {
if temp_dir.join(file).exists() {
println!(" ✓ {}", file);
}
}
Ok(())
}
fn demonstrate_no_fast_forward_merge(repo: &Repository, temp_dir: &std::path::Path) -> Result<()> {
println!("\n--- Demonstrating No-Fast-Forward Merge ---\n");
println!("1. Adding commit to master...");
let readme_path = temp_dir.join("README.md");
fs::write(
&readme_path,
"# Project\n\nInitial content\n\n## Updates\nAdded documentation",
)?;
repo.add(&["README.md"])?;
let master_commit = repo.commit("Update documentation")?;
println!(" Master commit: {}", master_commit);
println!("\n2. Creating another feature branch...");
repo.checkout_new("feature/no-ff", None)?;
let config_path = temp_dir.join("config.yaml");
fs::write(&config_path, "app:\n name: example\n version: 1.0")?;
repo.add(&["config.yaml"])?;
let config_commit = repo.commit("Add configuration file")?;
println!(" Config commit: {}", config_commit);
println!("\n3. Switching back to master...");
let branches = repo.branches()?;
let master_branch = branches.find("master").unwrap();
repo.checkout(master_branch)?;
println!("\n4. Performing no-fast-forward merge...");
let options = MergeOptions::new()
.with_fast_forward(FastForwardMode::Never)
.with_message("Merge feature/no-ff into master".to_string());
let merge_status = repo.merge_with_options("feature/no-ff", options)?;
match merge_status {
MergeStatus::Success(hash) => {
println!(" ✓ Merge commit created!");
println!(" Merge commit: {}", hash);
println!(" Created explicit merge commit preserving branch history");
}
_ => println!(" Unexpected merge result: {:?}", merge_status),
}
println!("\n5. Recent commit history:");
let commits = repo.recent_commits(3)?;
for (i, commit) in commits.iter().enumerate() {
println!(
" {}: {} - {}",
i + 1,
commit.hash.short(),
commit.message.subject
);
}
Ok(())
}
fn demonstrate_merge_conflicts(repo: &Repository, temp_dir: &std::path::Path) -> Result<()> {
println!("\n--- Demonstrating Merge Conflicts ---\n");
println!("1. Creating branch with conflicting changes...");
repo.checkout_new("feature/conflict", None)?;
let readme_path = temp_dir.join("README.md");
fs::write(
&readme_path,
"# Project\n\nFeature branch changes\n\n## Updates\nAdded documentation",
)?;
repo.add(&["README.md"])?;
let feature_commit = repo.commit("Update README from feature branch")?;
println!(" Feature commit: {}", feature_commit);
println!("\n2. Making conflicting change on master...");
let branches = repo.branches()?;
let master_branch = branches.find("master").unwrap();
repo.checkout(master_branch)?;
fs::write(
&readme_path,
"# Project\n\nMaster branch changes\n\n## Updates\nAdded documentation",
)?;
repo.add(&["README.md"])?;
let master_conflict_commit = repo.commit("Update README from master")?;
println!(" Master commit: {}", master_conflict_commit);
println!("\n3. Attempting merge (will have conflicts)...");
let merge_status = repo.merge("feature/conflict")?;
match merge_status {
MergeStatus::Conflicts(files) => {
println!(" ⚠️ Merge conflicts detected!");
println!(" Conflicted files:");
for file in &files {
println!(" - {}", file.display());
}
if repo.merge_in_progress()? {
println!(" ✓ Merge in progress status detected");
}
println!("\n4. Conflict markers in README.md:");
let content = fs::read_to_string(&readme_path)?;
for (i, line) in content.lines().enumerate() {
if line.starts_with("<<<<<<< ")
|| line.starts_with("======= ")
|| line.starts_with(">>>>>>> ")
{
println!(" {}: {} <-- conflict marker", i + 1, line);
} else {
println!(" {}: {}", i + 1, line);
}
}
println!("\n5. Aborting merge...");
repo.abort_merge()?;
println!(" ✓ Merge aborted successfully");
if !repo.merge_in_progress()? {
println!(" ✓ Repository is back to clean state");
}
}
_ => println!(" Unexpected merge result: {:?}", merge_status),
}
Ok(())
}
fn demonstrate_merge_status_and_abort(repo: &Repository, temp_dir: &std::path::Path) -> Result<()> {
println!("\n--- Demonstrating Merge Status and Options ---\n");
println!("1. Creating simple feature branch...");
repo.checkout_new("feature/simple", None)?;
let simple_path = temp_dir.join("simple.txt");
fs::write(&simple_path, "Simple feature content")?;
repo.add(&["simple.txt"])?;
repo.commit("Add simple feature")?;
let branches = repo.branches()?;
let master_branch = branches.find("master").unwrap();
repo.checkout(master_branch)?;
println!("\n2. Testing merge with custom options...");
let options = MergeOptions::new()
.with_fast_forward(FastForwardMode::Auto)
.with_message("Integrate simple feature".to_string());
let merge_status = repo.merge_with_options("feature/simple", options)?;
match merge_status {
MergeStatus::FastForward(hash) => {
println!(" ✓ Fast-forward merge completed: {}", hash);
}
MergeStatus::Success(hash) => {
println!(" ✓ Merge commit created: {}", hash);
}
MergeStatus::UpToDate => {
println!(" ✓ Already up to date");
}
MergeStatus::Conflicts(_) => {
println!(" ⚠️ Unexpected conflicts");
}
}
println!("\n3. Final repository state:");
let status = repo.status()?;
println!(
" Working directory clean: {}",
status.staged_files().count() == 0 && status.unstaged_files().count() == 0
);
let commits = repo.recent_commits(5)?;
println!(" Recent commits:");
for (i, commit) in commits.iter().enumerate() {
println!(
" {}: {} - {}",
i + 1,
commit.hash.short(),
commit.message.subject
);
}
Ok(())
}