mirror of
https://github.com/jht5945/rust_util.git
synced 2025-12-27 07:30:05 +08:00
feat: add util_git
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rust_util"
|
||||
version = "0.6.18"
|
||||
version = "0.6.19"
|
||||
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
||||
edition = "2018"
|
||||
description = "Hatter's Rust Util"
|
||||
|
||||
@@ -15,6 +15,7 @@ pub mod util_file;
|
||||
pub mod util_time;
|
||||
pub mod util_net;
|
||||
pub mod util_term;
|
||||
pub mod util_git;
|
||||
|
||||
/// iff!(condition, result_when_true, result_when_false)
|
||||
#[macro_export] macro_rules! iff {
|
||||
|
||||
147
src/util_git.rs
Normal file
147
src/util_git.rs
Normal file
@@ -0,0 +1,147 @@
|
||||
use std::process::Command;
|
||||
use crate::XResult;
|
||||
use crate::util_msg;
|
||||
use crate::util_cmd::run_command_and_wait;
|
||||
|
||||
const LANG: &str = "LANG";
|
||||
const EN_US: &str = "en_US";
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct GitStatusChange {
|
||||
pub added: Vec<String>,
|
||||
pub modified: Vec<String>,
|
||||
pub deleted: Vec<String>,
|
||||
pub untracked: Vec<String>,
|
||||
}
|
||||
|
||||
impl GitStatusChange {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.added.is_empty() && self.modified.is_empty()
|
||||
&& self.deleted.is_empty() && self.untracked.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn git_status_change(working_dir: Option<&str>) -> XResult<GitStatusChange> {
|
||||
let git_status = git_status(working_dir)?;
|
||||
parse_git_status_change(&git_status)
|
||||
}
|
||||
|
||||
pub fn git_fetch_dry_run(working_dir: Option<&str>) -> XResult<bool> {
|
||||
let mut cmd = new_git_command(working_dir);
|
||||
cmd.args(vec!["fetch", "--dry-run"]);
|
||||
util_msg::print_info(&format!("Exec: {:?}", cmd));
|
||||
let output = cmd.output()?;
|
||||
let fetch_dry_run = String::from_utf8(output.stdout)?;
|
||||
Ok(fetch_dry_run.trim().is_empty())
|
||||
}
|
||||
|
||||
pub fn git_status(working_dir: Option<&str>) -> XResult<String> {
|
||||
let mut cmd = new_git_command(working_dir);
|
||||
cmd.arg("status");
|
||||
util_msg::print_info(&format!("Exec: {:?}", cmd));
|
||||
let output = cmd.output()?;
|
||||
let git_status = String::from_utf8(output.stdout)?;
|
||||
Ok(git_status)
|
||||
}
|
||||
|
||||
pub fn git_push(working_dir: Option<&str>) {
|
||||
let mut cmd = new_git_command(working_dir);
|
||||
cmd.arg("push");
|
||||
util_msg::print_info(&format!("Exec: {:?}", cmd));
|
||||
if let Err(e) = run_command_and_wait(&mut cmd) {
|
||||
util_msg::print_error(&format!("Run git push failed: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn git_add(working_dir: Option<&str>, files: &[String]) {
|
||||
let mut cmd = new_git_command(working_dir);
|
||||
cmd.arg("add");
|
||||
for f in files {
|
||||
cmd.arg(&f);
|
||||
}
|
||||
util_msg::print_info(&format!("Exec: {:?}", cmd));
|
||||
if let Err(e) = run_command_and_wait(&mut cmd) {
|
||||
util_msg::print_error(&format!("Run git add failed: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn git_commit(working_dir: Option<&str>, message: &str, files: &[String]) {
|
||||
let mut cmd = new_git_command(working_dir);
|
||||
cmd.arg("commit");
|
||||
cmd.arg("-m");
|
||||
cmd.arg(message);
|
||||
for f in files {
|
||||
cmd.arg(&f);
|
||||
}
|
||||
util_msg::print_info(&format!("Exec: {:?}", cmd));
|
||||
if let Err(e) = run_command_and_wait(&mut cmd) {
|
||||
util_msg::print_error(&format!("Run git commit failed: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_git_status_change(git_status: &str) -> XResult<GitStatusChange> {
|
||||
let mut git_status_change: GitStatusChange = Default::default();
|
||||
for ln in git_status.lines() {
|
||||
if ln.starts_with("\t") {
|
||||
let ln = ln.trim();
|
||||
if ln.starts_with("new file:") {
|
||||
let f = ln["new file:".len()..].trim();
|
||||
git_status_change.added.push(f.to_owned());
|
||||
} else if ln.starts_with("deleted:") {
|
||||
let f = ln["deleted:".len()..].trim();
|
||||
git_status_change.deleted.push(f.to_owned());
|
||||
} else if ln.starts_with("modified:") {
|
||||
let f = ln["modified:".len()..].trim();
|
||||
git_status_change.modified.push(f.to_owned());
|
||||
} else {
|
||||
git_status_change.untracked.push(ln.to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(git_status_change)
|
||||
}
|
||||
|
||||
fn new_git_command(working_dir: Option<&str>) -> Command {
|
||||
let mut cmd = Command::new("git");
|
||||
cmd.env(LANG, EN_US);
|
||||
if let Some(working_dir) = working_dir {
|
||||
cmd.current_dir(working_dir);
|
||||
}
|
||||
cmd
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_git_status() {
|
||||
let git_status = r#"On branch master
|
||||
Your branch is up to date with 'origin/master'.
|
||||
|
||||
Changes to be committed:
|
||||
(use "git reset HEAD <file>..." to unstage)
|
||||
|
||||
new file: src/util_git.rs
|
||||
|
||||
Changes not staged for commit:
|
||||
(use "git add/rm <file>..." to update what will be committed)
|
||||
(use "git checkout -- <file>..." to discard changes in working directory)
|
||||
|
||||
deleted: README.md
|
||||
modified: src/lib.rs
|
||||
|
||||
Untracked files:
|
||||
(use "git add <file>..." to include in what will be committed)
|
||||
|
||||
Test
|
||||
|
||||
H"#;
|
||||
let gsc = parse_git_status_change(git_status).unwrap();
|
||||
println!("{:#?}", gsc);
|
||||
assert_eq!(1, gsc.added.len());
|
||||
assert_eq!("src/util_git.rs", gsc.added[0]);
|
||||
assert_eq!(1, gsc.modified.len());
|
||||
assert_eq!("src/lib.rs", gsc.modified[0]);
|
||||
assert_eq!(1, gsc.deleted.len());
|
||||
assert_eq!("README.md", gsc.deleted[0]);
|
||||
assert_eq!(1, gsc.untracked.len());
|
||||
assert_eq!("Test", gsc.untracked[0]);
|
||||
}
|
||||
Reference in New Issue
Block a user