fix: Add missing GitRepo, StagedChanges, ChangedFile structs to git.rs
This commit is contained in:
249
src/git.rs
249
src/git.rs
@@ -3,93 +3,56 @@ use std::process::Command;
|
|||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GitCommit {
|
pub struct GitRepo {
|
||||||
|
repo_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct StagedChanges {
|
||||||
|
pub files: Vec<ChangedFile>,
|
||||||
|
pub diff_text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ChangedFile {
|
||||||
|
pub path: String,
|
||||||
pub old_path: Option<String>,
|
pub old_path: Option<String>,
|
||||||
pub new_path: Option<String>,
|
pub status: FileStatus,
|
||||||
pub sha: String,
|
|
||||||
pub message: String,
|
|
||||||
pub author: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_recent_commits(limit: usize) -> Result<Vec<GitCommit>> {
|
|
||||||
let output = Command::new("git")
|
|
||||||
.args(&[
|
|
||||||
"log",
|
|
||||||
"--pretty=format:%H|%s|%an",
|
|
||||||
&format!("-n{}", limit),
|
|
||||||
])
|
|
||||||
.output()
|
|
||||||
.context("Failed to execute git log")?;
|
|
||||||
|
|
||||||
if !output.status.success() {
|
|
||||||
return Err(anyhow::anyhow!("Git log command failed"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
||||||
let mut commits = Vec::new();
|
|
||||||
|
|
||||||
for line in stdout.lines() {
|
|
||||||
let parts: Vec<&str> = line.splitn(3, "|").collect();
|
|
||||||
if parts.len() >= 3 {
|
|
||||||
commits.push(GitCommit {
|
|
||||||
old_path: None,
|
|
||||||
new_path: None,
|
|
||||||
sha: parts[0].to_string(),
|
|
||||||
message: parts[1].to_string(),
|
|
||||||
author: parts[2].to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(commits)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_diff_from_sha(sha: &str) -> Result<Vec<Change>> {
|
|
||||||
let output = Command::new("git")
|
|
||||||
.args(&["show", "--stat", sha])
|
|
||||||
.output()
|
|
||||||
.context("Failed to execute git show")?;
|
|
||||||
|
|
||||||
if !output.status.success() {
|
|
||||||
return Err(anyhow::anyhow!("Git show command failed"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
||||||
let mut changes = Vec::new();
|
|
||||||
|
|
||||||
for line in stdout.lines() {
|
|
||||||
if line.contains("|") {
|
|
||||||
let parts: Vec<&str> = line.split("|").collect();
|
|
||||||
if parts.len() == 2 {
|
|
||||||
let path_parts: Vec<&str> = parts[0].split(" -> ").collect();
|
|
||||||
let (old_path, new_path) = if path_parts.len() == 2 {
|
|
||||||
(Some(path_parts[0].to_string()), Some(path_parts[1].to_string()))
|
|
||||||
} else {
|
|
||||||
(None, Some(parts[0].to_string()))
|
|
||||||
};
|
|
||||||
|
|
||||||
changes.push(Change {
|
|
||||||
old_path,
|
|
||||||
new_path,
|
|
||||||
additions: 0,
|
|
||||||
deletions: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(changes)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Change {
|
|
||||||
pub old_path: Option<String>,
|
|
||||||
pub new_path: Option<String>,
|
|
||||||
pub additions: usize,
|
pub additions: usize,
|
||||||
pub deletions: usize,
|
pub deletions: usize,
|
||||||
|
pub is_new: bool,
|
||||||
|
pub is_deleted: bool,
|
||||||
|
pub is_renamed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_staged_diff() -> Result<Vec<Change>> {
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum FileStatus {
|
||||||
|
Added,
|
||||||
|
Deleted,
|
||||||
|
Modified,
|
||||||
|
Renamed,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GitRepo {
|
||||||
|
pub fn find_repo() -> Result<Self> {
|
||||||
|
let output = Command::new("git")
|
||||||
|
.args(&["rev-parse", "--show-toplevel"])
|
||||||
|
.output()
|
||||||
|
.context("Failed to execute git rev-parse")?;
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(anyhow::anyhow!("Not in a git repository"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let repo_path = String::from_utf8_lossy(&output.stdout)
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
Ok(GitRepo { repo_path })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_staged_changes(&self) -> Result<StagedChanges> {
|
||||||
let output = Command::new("git")
|
let output = Command::new("git")
|
||||||
.args(&["diff", "--cached", "--stat"])
|
.args(&["diff", "--cached", "--stat"])
|
||||||
.output()
|
.output()
|
||||||
@@ -103,70 +66,96 @@ pub fn get_staged_diff() -> Result<Vec<Change>> {
|
|||||||
let mut changes = Vec::new();
|
let mut changes = Vec::new();
|
||||||
|
|
||||||
for line in stdout.lines() {
|
for line in stdout.lines() {
|
||||||
let parts: Vec<&str> = line.split("|").collect();
|
if line.is_empty() || !line.contains('|') {
|
||||||
if parts.len() == 2 {
|
continue;
|
||||||
let path_parts: Vec<&str> = parts[0].split(" -> ").collect();
|
}
|
||||||
let (old_path, new_path) = if path_parts.len() == 2 {
|
|
||||||
(Some(path_parts[0].to_string()), Some(path_parts[1].to_string()))
|
let parts: Vec<&str> = line.split('|').collect();
|
||||||
|
if parts.len() != 2 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = parts[0].trim().to_string();
|
||||||
|
let stat_part = parts[1].trim().to_string();
|
||||||
|
|
||||||
|
let (status, additions, deletions) = if stat_part.contains("+") && stat_part.contains("-") {
|
||||||
|
let add_parts: Vec<&str> = stat_part.split('+').collect();
|
||||||
|
let del_parts: Vec<&str> = add_parts[1].split('-').collect();
|
||||||
|
let adds: usize = add_parts[0].trim().parse().unwrap_or(0);
|
||||||
|
let dels: usize = del_parts[0].trim().parse().unwrap_or(0);
|
||||||
|
(FileStatus::Modified, adds, dels)
|
||||||
|
} else if stat_part.contains("+") {
|
||||||
|
let adds: usize = stat_part.replace("+", "").trim().parse().unwrap_or(0);
|
||||||
|
(FileStatus::Added, adds, 0)
|
||||||
|
} else if stat_part.contains("-") {
|
||||||
|
let dels: usize = stat_part.replace("-", "").trim().parse().unwrap_or(0);
|
||||||
|
(FileStatus::Deleted, 0, dels)
|
||||||
|
} else if stat_part.contains("=>") {
|
||||||
|
(FileStatus::Renamed, 0, 0)
|
||||||
} else {
|
} else {
|
||||||
(None, Some(parts[0].to_string()))
|
(FileStatus::Unknown, 0, 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
let stat_parts: Vec<&str> = parts[1].trim().split(" ").collect();
|
let is_new = matches!(status, FileStatus::Added);
|
||||||
let mut additions = 0;
|
let is_deleted = matches!(status, FileStatus::Deleted);
|
||||||
let mut deletions = 0;
|
let is_renamed = matches!(status, FileStatus::Renamed);
|
||||||
|
|
||||||
for stat in stat_parts {
|
let old_path = if is_renamed {
|
||||||
if stat.contains('+') && stat.parse::<usize>().is_ok() {
|
Some(path.split("=>").next().unwrap_or("").trim().to_string())
|
||||||
additions = stat.replace('+', "").parse().unwrap_or(0);
|
} else {
|
||||||
}
|
None
|
||||||
if stat.contains('-') && stat.parse::<usize>().is_ok() {
|
};
|
||||||
deletions = stat.replace('-', "").parse().unwrap_or(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
changes.push(Change {
|
changes.push(ChangedFile {
|
||||||
|
path: if is_renamed {
|
||||||
|
path.split("=>").nth(1).unwrap_or("").trim().to_string()
|
||||||
|
} else {
|
||||||
|
path
|
||||||
|
},
|
||||||
old_path,
|
old_path,
|
||||||
new_path,
|
status,
|
||||||
additions,
|
additions,
|
||||||
deletions,
|
deletions,
|
||||||
|
is_new,
|
||||||
|
is_deleted,
|
||||||
|
is_renamed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(changes)
|
let diff_output = Command::new("git")
|
||||||
}
|
.args(&["diff", "--cached"])
|
||||||
|
|
||||||
pub fn get_diff_stats() -> Result<(usize, usize)> {
|
|
||||||
let output = Command::new("git")
|
|
||||||
.args(&["diff", "--shortstat"])
|
|
||||||
.output()
|
.output()
|
||||||
.context("Failed to execute git diff")?;
|
.context("Failed to execute git diff --cached")?;
|
||||||
|
|
||||||
|
let diff_text = if diff_output.status.success() {
|
||||||
|
String::from_utf8_lossy(&diff_output.stdout).to_string()
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(StagedChanges { files: changes, diff_text })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_commit(&self, message: &str, dry_run: bool) -> Result<()> {
|
||||||
|
if dry_run {
|
||||||
|
println!("[DRY RUN] Would commit with message: {}", message);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = Command::new("git")
|
||||||
|
.args(&["commit", "-m", message])
|
||||||
|
.output()
|
||||||
|
.context("Failed to execute git commit")?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(anyhow::anyhow!("Git diff command failed"));
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
return Err(anyhow::anyhow!("Git commit failed: {}", stderr));
|
||||||
}
|
}
|
||||||
|
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
Ok(())
|
||||||
let mut insertions = 0;
|
|
||||||
let mut deletions = 0;
|
|
||||||
|
|
||||||
for line in stdout.lines() {
|
|
||||||
let parts: Vec<&str> = line.split(",").collect();
|
|
||||||
for part in parts {
|
|
||||||
let part = part.trim();
|
|
||||||
if let Some(insertions_str) = part.strip_suffix(" insertions(+)") {
|
|
||||||
if let Ok(count) = insertions_str.trim().parse::<usize>() {
|
|
||||||
insertions = count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(deletions_str) = part.strip_suffix(" deletions(-)") {
|
|
||||||
if let Ok(count) = deletions_str.trim().parse::<usize>() {
|
|
||||||
deletions = count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((insertions, deletions))
|
pub fn has_config(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user