use git2::{Repository, Status};
use std::path::Path;
use crate::error::{GitGardenerError, Result};
#[derive(Debug, Clone, PartialEq)]
pub enum WorktreeStatus {
Clean,
Dirty,
Ahead,
Behind,
Diverged,
}
#[derive(Debug, Clone)]
pub struct GitStatus {
pub working_tree_status: WorktreeStatus,
pub has_staged_changes: bool,
pub has_unstaged_changes: bool,
pub last_commit_time: Option<i64>,
pub ahead_count: u32,
pub behind_count: u32,
}
impl GitStatus {
pub fn from_path(path: &Path) -> Result<Self> {
let repo = Repository::open(path).map_err(|e| {
GitGardenerError::Custom(format!("Failed to open repository: {}", e))
})?;
Self::from_repository(&repo)
}
pub fn from_repository(repo: &Repository) -> Result<Self> {
let statuses = repo.statuses(None).map_err(|e| {
GitGardenerError::Custom(format!("Failed to get repository status: {}", e))
})?;
let mut has_staged_changes = false;
let mut has_unstaged_changes = false;
for status_entry in statuses.iter() {
let flags = status_entry.status();
if flags.intersects(
Status::INDEX_NEW | Status::INDEX_MODIFIED | Status::INDEX_DELETED | Status::INDEX_RENAMED | Status::INDEX_TYPECHANGE
) {
has_staged_changes = true;
}
if flags.intersects(
Status::WT_MODIFIED | Status::WT_DELETED | Status::WT_TYPECHANGE | Status::WT_RENAMED | Status::WT_NEW
) {
has_unstaged_changes = true;
}
}
let working_tree_status = if has_staged_changes || has_unstaged_changes {
WorktreeStatus::Dirty
} else {
WorktreeStatus::Clean
};
let last_commit_time = Self::get_last_commit_time(repo);
let ahead_count = 0;
let behind_count = 0;
Ok(GitStatus {
working_tree_status,
has_staged_changes,
has_unstaged_changes,
last_commit_time,
ahead_count,
behind_count,
})
}
fn get_last_commit_time(repo: &Repository) -> Option<i64> {
repo.head().ok()
.and_then(|reference| reference.target())
.and_then(|oid| repo.find_commit(oid).ok())
.map(|commit| commit.time().seconds())
}
}