eazygit 0.5.1

A fast TUI for Git with staging, conflicts, rebase, and palette-first UX
Documentation
use crate::commands::{Command, CommandResult};
use crate::services::GitService;
use crate::app::{AppState, Action, reducer};
use crate::errors::CommandError;
use crate::git::parsers::diff::DiffParseResult;
use tracing::instrument;
use super::builder::{build_hunk_patch, file_header};

pub struct StageHunkCommand {
    pub hunk_index: usize,
    pub parsed: DiffParseResult,
    pub path: String,
    pub raw_diff: String,
}

impl Command for StageHunkCommand {
    #[instrument(skip(self, git, state))]
    fn execute(
        &self,
        git: &GitService,
        state: &AppState,
    ) -> Result<CommandResult, CommandError> {
        let mut new_state = state.clone();
        new_state = reducer(new_state, Action::SetOpStatus(Some("stage hunk…".into())));
        let patch = if let Some(p) = self
            .parsed
            .files
            .first()
            .and_then(|f| f.hunks.get(self.hunk_index))
            .map(|h| build_hunk_patch(&self.path, h))
        {
            p
        } else {
            // fallback: apply full diff
            format!("{}{}", file_header(&self.path), self.raw_diff)
        };
        match git.apply_patch(&state.repo_path, &patch, true, false) {
            Ok(_) => {
                new_state = reducer(new_state, Action::AppendOpLog("hunk staged".into()));
                new_state = reducer(new_state, Action::SetRefreshing(true));
            }
            Err(e) => {
                new_state = reducer(new_state, Action::AppendOpLog(format!("stage hunk error: {e}")));
                new_state = reducer(new_state, Action::SetStatusError(Some(format!("stage hunk error: {e}"))));
            }
        }
        new_state = reducer(new_state, Action::SetOpStatus(None));
        Ok(CommandResult::StateUpdate(new_state))
    }
}

pub struct UnstageHunkCommand {
    pub hunk_index: usize,
    pub parsed: DiffParseResult,
    pub path: String,
    pub raw_diff: String,
}

impl Command for UnstageHunkCommand {
    #[instrument(skip(self, git, state))]
    fn execute(
        &self,
        git: &GitService,
        state: &AppState,
    ) -> Result<CommandResult, CommandError> {
        let mut new_state = state.clone();
        new_state = reducer(new_state, Action::SetOpStatus(Some("unstage hunk…".into())));
        let patch = if let Some(p) = self
            .parsed
            .files
            .first()
            .and_then(|f| f.hunks.get(self.hunk_index))
            .map(|h| build_hunk_patch(&self.path, h))
        {
            p
        } else {
            format!("{}{}", file_header(&self.path), self.raw_diff)
        };
        match git.apply_patch(&state.repo_path, &patch, true, true) {
            Ok(_) => {
                new_state = reducer(new_state, Action::AppendOpLog("hunk unstaged".into()));
                new_state = reducer(new_state, Action::SetRefreshing(true));
            }
            Err(e) => {
                new_state = reducer(new_state, Action::AppendOpLog(format!("unstage hunk error: {e}")));
                new_state = reducer(new_state, Action::SetStatusError(Some(format!("unstage hunk error: {e}"))));
            }
        }
        new_state = reducer(new_state, Action::SetOpStatus(None));
        Ok(CommandResult::StateUpdate(new_state))
    }
}