g-cli 0.1.0

Git that talks back. A human-friendly CLI wrapper for Git.
use colored::Colorize;

use crate::git;

pub fn run() {
    let entries = git::reflog_entries(10);

    if entries.is_empty() {
        println!();
        println!("  {} No recent actions found.", "".yellow());
        println!();
        return;
    }

    println!();
    println!("  {}:", "Recent actions".bold());
    println!();

    let mut count = 0;
    for (_hash, description, time) in &entries {
        let explained = humanize_reflog(description);
        if let Some(msg) = explained {
            count += 1;
            println!(
                "  {}  {}",
                format!("{})", count).dimmed(),
                msg
            );
            println!("     {}", time.dimmed());
            println!();

            if count >= 5 {
                break;
            }
        }
    }

    if count == 0 {
        println!("  {} No meaningful actions to explain.", "".yellow());
        println!();
        return;
    }

    // Current state
    let branch = git::current_branch();
    let files = git::parse_status();
    println!("  {}:", "Current state".bold());
    if files.is_empty() {
        println!("    {} Clean working directory", "".green());
    } else {
        let staged = files.iter().filter(|f| f.staged).count();
        let unstaged = files.iter().filter(|f| !f.staged).count();
        println!(
            "    {} {} staged, {} unstaged change(s)",
            "".yellow(),
            staged,
            unstaged
        );
    }
    println!("    On branch: {}", branch.cyan());

    if let Some((ahead, behind)) = git::ahead_behind() {
        if ahead > 0 {
            println!("    {} {} commit(s) ahead of remote", "".green(), ahead);
        }
        if behind > 0 {
            println!("    {} {} commit(s) behind remote", "".yellow(), behind);
        }
        if ahead == 0 && behind == 0 {
            println!("    {} Up to date with remote", "".green());
        }
    }

    println!();
}

fn humanize_reflog(entry: &str) -> Option<String> {
    let entry = entry.trim();

    if let Some(rest) = entry.strip_prefix("commit: ") {
        return Some(format!("You committed: \"{}\"", rest.bold()));
    }
    if let Some(rest) = entry.strip_prefix("commit (initial): ") {
        return Some(format!("You made the initial commit: \"{}\"", rest.bold()));
    }
    if let Some(rest) = entry.strip_prefix("commit (amend): ") {
        return Some(format!("You amended the last commit: \"{}\"", rest.bold()));
    }
    if let Some(rest) = entry.strip_prefix("commit (merge): ") {
        return Some(format!("You merged: \"{}\"", rest.bold()));
    }
    if entry.starts_with("pull") {
        return Some("You pulled from remote".to_string());
    }
    if entry.starts_with("checkout: moving from") {
        let parts: Vec<&str> = entry.split(" to ").collect();
        if parts.len() == 2 {
            return Some(format!("You switched to branch {}", parts[1].bold().cyan()));
        }
        return Some("You switched branches".to_string());
    }
    if entry.starts_with("merge ") {
        let branch = entry.strip_prefix("merge ").unwrap_or("unknown");
        return Some(format!("You merged branch {} into your current branch", branch.bold().cyan()));
    }
    if entry.starts_with("reset: moving to") {
        return Some("You reset your branch".to_string());
    }
    if entry.starts_with("rebase") {
        return Some("You rebased your branch".to_string());
    }
    if entry.starts_with("clone") {
        return Some("You cloned the repository".to_string());
    }
    if entry.starts_with("branch: Created from") {
        return Some("You created a new branch".to_string());
    }

    // Fallback: show raw entry if it has substance
    if !entry.is_empty() {
        Some(format!("{}", entry.dimmed()))
    } else {
        None
    }
}