fix: Add missing GitRepo, StagedChanges, ChangedFile structs to git.rs
Some checks failed
CI / lint (push) Failing after 3s
CI / build (push) Has been skipped
CI / test (push) Has been skipped

This commit is contained in:
2026-02-01 12:28:36 +00:00
parent 614ea3ea35
commit a204e6c900

View File

@@ -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
}
} }