gitu 0.41.0

A git client inspired by Magit
Documentation
use super::OpTrait;
use crate::{
    Action,
    app::{App, State},
    git::{self, diff::PatchMode},
    gitu_diff::Status,
    item_data::ItemData,
    term::Term,
};
use std::{path::PathBuf, process::Command, rc::Rc};

pub(crate) struct Unstage;
impl OpTrait for Unstage {
    fn get_action(&self, target: &ItemData) -> Option<Action> {
        let action = match target {
            ItemData::AllStaged(_) => unstage_staged(),
            ItemData::Delta { diff, file_i } => {
                let diff_header = &diff.file_diffs[*file_i].header;
                let file_path = match diff_header.status {
                    Status::Deleted => &diff_header.old_file,
                    _ => &diff_header.new_file,
                };

                unstage_file(file_path.fmt(&diff.text).into_owned().into())
            }
            ItemData::Hunk {
                diff,
                file_i,
                hunk_i,
            } => unstage_patch(diff.format_hunk_patch(*file_i, *hunk_i).into_bytes()),
            ItemData::HunkLine {
                diff,
                file_i,
                hunk_i,
                line_i,
                ..
            } => unstage_line(
                diff.format_line_patch(
                    *file_i,
                    *hunk_i,
                    *line_i..(*line_i + 1),
                    PatchMode::Reverse,
                )
                .into_bytes(),
            ),
            _ => return None,
        };

        Some(action)
    }
    fn is_target_op(&self) -> bool {
        true
    }

    fn display(&self, _state: &State) -> String {
        "Unstage".into()
    }
}

fn unstage_staged() -> Action {
    Rc::new(move |app: &mut App, term: &mut Term| {
        let mut cmd = Command::new("git");
        cmd.args(["reset", "HEAD", "--"]);

        app.close_menu();
        app.run_cmd(term, &[], cmd)
    })
}

fn unstage_file(file: PathBuf) -> Action {
    Rc::new(move |app: &mut App, term: &mut Term| {
        app.close_menu();
        app.run_cmd(term, &[], git::restore_index(&file))
    })
}

fn unstage_patch(input: Vec<u8>) -> Action {
    Rc::new(move |app: &mut App, term: &mut Term| {
        let mut cmd = Command::new("git");
        cmd.args(["apply", "--cached", "--reverse"]);

        app.close_menu();
        app.run_cmd(term, &input, cmd)
    })
}

fn unstage_line(input: Vec<u8>) -> Action {
    Rc::new(move |app: &mut App, term: &mut Term| {
        let mut cmd = Command::new("git");
        cmd.args(["apply", "--cached", "--reverse", "--recount"]);

        app.close_menu();
        app.run_cmd(term, &input, cmd)
    })
}