gzy 1.2.0

Make Git commits faster and easier
use clap::{Parser, Subcommand};
use std::process::Command;

#[derive(Parser)]
#[command(
    name = "gzy",
    about = "Make Git commits faster and easier"
)]
struct CLI {
    #[command(subcommand)]
    commands: Cmd,
}

#[derive(Subcommand)]
enum Cmd {
    Add { file: String },
    Commit { msg: String },
    Branch { branch_name: String },
    Remote { url: String },

    Push {
        branch: Option<String>,

        #[arg(short = 'u', long = "set-upstream")]
        upstream: bool,

        #[arg(short = 'f', long = "force")]
        force: bool,
    },

    Clone { clone_url: String },

    Checkout {
        branch: String,

        #[arg(short = 'b', long = "create")]
        create: bool,
    },

    Conflict {
        strategy: String,
    },

    Sync {
        base: String,
        branch: Option<String>,
    },

    Init,
    Status,
    Log,
}

fn main() {
    let cli = CLI::parse();

    match &cli.commands {
        Cmd::Add { file } => {
            git(vec!["add".into(), file.clone()]);
        }

        Cmd::Commit { msg } => {
            git(vec!["commit".into(), "-m".into(), msg.clone()]);
        }

        Cmd::Branch { branch_name } => {
            git(vec!["branch".into(), "-M".into(), branch_name.clone()]);
        }

        Cmd::Remote { url } => {
            git(vec![
                "remote".into(),
                "add".into(),
                "origin".into(),
                url.clone(),
            ]);
        }

        Cmd::Push { branch, upstream, force } => {
            let mut args: Vec<String> = vec!["push".into()];

            if *upstream {
                args.push("-u".into());
                args.push("origin".into());
            }

            if let Some(branch) = branch {
                args.push(branch.clone());
            }

            if *force {
                args.push("--force".into());
            }

            git(args);
        }

        Cmd::Clone { clone_url } => {
            git(vec!["clone".into(), clone_url.clone()]);
        }

        Cmd::Checkout { branch, create } => {
            if *create {
                git(vec!["checkout".into(), "-b".into(), branch.clone()]);
            } else {
                git(vec!["checkout".into(), branch.clone()]);
            }
        }

        Cmd::Conflict { strategy } => match strategy.as_str() {
            "ours" => {
                git(vec!["checkout".into(), "--ours".into(), ".".into()]);
                git(vec!["add".into(), ".".into()]);
                git(vec![
                    "commit".into(),
                    "-m".into(),
                    "Resolve conflicts (ours)".into(),
                ]);
            }
            "theirs" => {
                git(vec!["checkout".into(), "--theirs".into(), ".".into()]);
                git(vec!["add".into(), ".".into()]);
                git(vec![
                    "commit".into(),
                    "-m".into(),
                    "Resolve conflicts (theirs)".into(),
                ]);
            }
            "abort" => {
                git(vec!["merge".into(), "--abort".into()]);
            }
            _ => {
                eprintln!("strategy must be: ours | theirs | abort");
            }
        },

        Cmd::Sync { base, branch } => {
            git(vec!["fetch".into(), "upstream".into()]);
            git(vec!["checkout".into(), base.clone()]);
            git(vec![
                "rebase".into(),
                format!("upstream/{}", base),
            ]);

            let target = match branch {
                Some(b) => b.clone(),
                None => {
                    let output = Command::new("git")
                        .args(["branch", "--show-current"])
                        .output()
                        .expect("failed to get current branch");
                    String::from_utf8_lossy(&output.stdout).trim().to_string()
                }
            };

            git(vec!["checkout".into(), target.clone()]);
            git(vec!["rebase".into(), base.clone()]);
            git(vec![
                "push".into(),
                "origin".into(),
                target,
                "--force-with-lease".into(),
            ]);
        }

        Cmd::Init => git(vec!["init".into()]),
        Cmd::Status => git(vec!["status".into()]),
        Cmd::Log => git(vec!["log".into()]),
    }
}

fn git(args: Vec<String>) {
    Command::new("git")
        .args(args)
        .status()
        .expect("Failed to execute git command");
}