git.rs
1 use anyhow::Result; 2 use colored::Colorize; 3 use std::process::{Command, Output}; 4 5 pub fn git(args: &[&str]) -> Output { 6 let log = format!("+> git {}", args.join(" ")); 7 println!("{}", log.bright_black()); 8 9 Command::new("git") 10 .args(args) 11 .output() 12 .expect("failed to run git command, make sure git is installed on your machine") 13 } 14 15 // get every commit message between the two versions 16 pub fn diff(old_version: &str, new_version: &str) -> String { 17 let references = format!("{old_version}..{new_version}"); 18 19 let output = git(&["log", "--oneline", "--pretty=format:%s (%h)", &references]); 20 let output = String::from_utf8_lossy(&output.stdout).to_string(); 21 22 let mut lines = output.lines().collect::<Vec<_>>(); 23 24 // reverse the lines to get the oldest commit first 25 lines.reverse(); 26 27 // remove the last line because it's the version commit 28 lines.pop(); 29 30 lines 31 .iter() 32 .map(|line| format!("* {line}")) 33 .collect::<Vec<_>>() 34 .join("\n") 35 } 36 37 pub fn origin_url() -> String { 38 let output = git(&["remote", "get-url", "origin"]); 39 let url = String::from_utf8_lossy(&output.stdout); 40 41 url.trim().to_string() 42 } 43 44 pub fn branch_name() -> String { 45 let output = git(&["rev-parse", "--abbrev-ref", "HEAD"]); 46 let branch_name = String::from_utf8_lossy(&output.stdout); 47 branch_name.trim().to_string() 48 } 49 50 pub fn is_repo_dirty() -> Result<bool> { 51 let output = git(&["status", "--porcelain"]); 52 53 if !output.status.success() { 54 return Err(anyhow::anyhow!("failed to check repository status")); 55 } 56 57 // If there's any output, the repo is dirty 58 Ok(!output.stdout.is_empty()) 59 } 60 61 pub fn is_behind_upstream(branch_name: &str) -> Result<bool> { 62 let fetch = git(&["fetch"]); 63 if !fetch.status.success() { 64 return Err(anyhow::anyhow!("failed to fetch from remote")); 65 } 66 67 let upstream = format!("origin/{branch_name}"); 68 let output = git(&["rev-list", "--count", &format!("HEAD..{upstream}")]); 69 70 if !output.status.success() { 71 return Err(anyhow::anyhow!( 72 "failed to check if branch is behind remote" 73 )); 74 } 75 76 let behind_count = String::from_utf8_lossy(&output.stdout) 77 .trim() 78 .parse::<u32>() 79 .unwrap_or(0); 80 81 Ok(behind_count > 0) 82 } 83 84 /// Get all tags of the repository, latest first to oldest tag. 85 pub fn tags() -> Vec<String> { 86 let output = git(&["tag", "--sort=-v:refname"]); 87 let output = String::from_utf8_lossy(&output.stdout).to_string(); 88 89 output.lines().map(|line| line.into()).collect::<Vec<_>>() 90 }