use std::process::Command;
use crate::error::{GwError, Result};
use crate::output;
fn git_run(args: &[&str], verbose: bool) -> Result<()> {
if verbose {
output::action(&format!("git {}", args.join(" ")));
}
let output = Command::new("git")
.args(args)
.output()
.map_err(|e| GwError::GitCommandFailed(format!("Failed to execute git: {e}")))?;
if output.status.success() {
Ok(())
} else {
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
Err(GwError::GitCommandFailed(stderr))
}
}
fn git_output(args: &[&str], verbose: bool) -> Result<String> {
if verbose {
output::action(&format!("git {}", args.join(" ")));
}
let output = Command::new("git")
.args(args)
.output()
.map_err(|e| GwError::GitCommandFailed(format!("Failed to execute git: {e}")))?;
if output.status.success() {
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
} else {
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
Err(GwError::GitCommandFailed(stderr))
}
}
pub fn fetch_prune(verbose: bool) -> Result<()> {
git_run(&["fetch", "--prune", "--quiet"], verbose)
}
pub fn checkout(branch: &str, verbose: bool) -> Result<()> {
git_run(&["checkout", branch, "--quiet"], verbose)
}
pub fn checkout_new_branch(branch: &str, start_point: &str, verbose: bool) -> Result<()> {
git_run(&["checkout", "-b", branch, start_point, "--quiet"], verbose)
}
pub fn pull_ff_only(remote: &str, branch: &str, verbose: bool) -> Result<()> {
git_run(&["pull", remote, branch, "--ff-only", "--quiet"], verbose)
}
pub fn pull(remote: &str, branch: &str, verbose: bool) -> Result<()> {
let result = git_run(&["pull", remote, branch, "--ff-only", "--quiet"], verbose);
if result.is_err() {
git_run(&["pull", remote, branch, "--quiet"], verbose)?;
}
Ok(())
}
pub fn delete_branch(branch: &str, verbose: bool) -> Result<()> {
git_run(&["branch", "-d", branch], verbose)
}
pub fn force_delete_branch(branch: &str, verbose: bool) -> Result<()> {
git_run(&["branch", "-D", branch], verbose)
}
#[allow(dead_code)]
pub fn delete_remote_branch(branch: &str, verbose: bool) -> Result<()> {
git_run(&["push", "origin", "--delete", branch], verbose)
}
pub fn log_commits(from: &str, to: &str, verbose: bool) -> Result<Vec<String>> {
let output = git_output(&["log", &format!("{from}..{to}"), "--oneline"], verbose)?;
Ok(output.lines().map(String::from).collect())
}
pub fn add_all(verbose: bool) -> Result<()> {
git_run(&["add", "-A"], verbose)
}
pub fn commit(message: &str, verbose: bool) -> Result<()> {
git_run(&["commit", "-m", message], verbose)
}
pub fn reset_soft(target: &str, verbose: bool) -> Result<()> {
git_run(&["reset", "--soft", target], verbose)
}
pub fn discard_all_changes(verbose: bool) -> Result<()> {
git_run(&["reset", "--hard", "HEAD"], verbose)?;
git_run(&["clean", "-fd"], verbose)
}
pub fn rebase(target: &str, verbose: bool) -> Result<()> {
git_run(&["rebase", target], verbose)
}
pub fn force_push_with_lease(branch: &str, verbose: bool) -> Result<()> {
git_run(&["push", "--force-with-lease", "origin", branch], verbose)
}
pub fn worktree_add(path: &str, branch: &str, start_point: &str, verbose: bool) -> Result<()> {
git_run(
&["worktree", "add", "-b", branch, path, start_point],
verbose,
)
}
pub fn worktree_remove(path: &str, verbose: bool) -> Result<()> {
git_run(&["worktree", "remove", "--force", path], verbose)
}
pub fn worktree_prune(verbose: bool) -> Result<()> {
git_run(&["worktree", "prune"], verbose)
}
pub fn git_run_in_dir(dir: &str, args: &[&str], verbose: bool) -> Result<()> {
if verbose {
output::action(&format!("git -C {} {}", dir, args.join(" ")));
}
let output = Command::new("git")
.args(args)
.current_dir(dir)
.output()
.map_err(|e| GwError::GitCommandFailed(format!("Failed to execute git: {e}")))?;
if output.status.success() {
Ok(())
} else {
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
Err(GwError::GitCommandFailed(stderr))
}
}