mirror of
https://github.com/gitui-org/gitui
synced 2026-05-23 00:48:35 +00:00
parent
0cdaabf9f8
commit
24da2f200e
12 changed files with 94 additions and 11 deletions
|
|
@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Added
|
||||
- Inspect stash commit in detail ([#121](https://github.com/extrawurst/gitui/issues/121))
|
||||
- Support reset/revert individual hunks ([#11](https://github.com/extrawurst/gitui/issues/11))
|
||||
- Commit Amend (`ctrl+a`) when in commit popup ([#89](https://github.com/extrawurst/gitui/issues/89))
|
||||
|
||||

|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ pub(crate) fn get_diff_raw<'a>(
|
|||
// diff against head
|
||||
if let Ok(ref_head) = repo.head() {
|
||||
let parent = repo.find_commit(
|
||||
//TODO: use new NoHead Error
|
||||
ref_head.target().ok_or_else(|| {
|
||||
let name = ref_head.name().unwrap_or("??");
|
||||
Error::Generic(
|
||||
|
|
|
|||
|
|
@ -32,6 +32,38 @@ pub fn stage_hunk(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
pub fn reset_hunk(
|
||||
repo_path: &str,
|
||||
file_path: String,
|
||||
hunk_hash: u64,
|
||||
) -> Result<()> {
|
||||
scope_time!("reset_hunk");
|
||||
|
||||
let repo = repo(repo_path)?;
|
||||
|
||||
let diff = get_diff_raw(&repo, &file_path, false, false)?;
|
||||
|
||||
let hunk_index = find_hunk_index(&diff, hunk_hash);
|
||||
if let Some(hunk_index) = hunk_index {
|
||||
let mut hunk_idx = 0;
|
||||
let mut opt = ApplyOptions::new();
|
||||
opt.hunk_callback(|_hunk| {
|
||||
let res = hunk_idx == hunk_index;
|
||||
hunk_idx += 1;
|
||||
res
|
||||
});
|
||||
|
||||
let diff = get_diff_raw(&repo, &file_path, false, true)?;
|
||||
|
||||
repo.apply(&diff, ApplyLocation::WorkDir, Some(&mut opt))?;
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::Generic("hunk not found".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
fn find_hunk_index(diff: &Diff, hunk_hash: u64) -> Option<usize> {
|
||||
let mut result = None;
|
||||
|
||||
|
|
@ -72,7 +104,6 @@ pub fn unstage_hunk(
|
|||
let diff_count_positive = diff.deltas().len();
|
||||
|
||||
let hunk_index = find_hunk_index(&diff, hunk_hash);
|
||||
|
||||
if hunk_index.is_none() {
|
||||
return Err(Error::Generic("hunk not found".to_string()));
|
||||
}
|
||||
|
|
@ -97,12 +128,8 @@ pub fn unstage_hunk(
|
|||
|
||||
res
|
||||
});
|
||||
if repo
|
||||
.apply(&diff, ApplyLocation::Index, Some(&mut opt))
|
||||
.is_err()
|
||||
{
|
||||
return Err(Error::Generic("apply failed".to_string()));
|
||||
}
|
||||
|
||||
repo.apply(&diff, ApplyLocation::Index, Some(&mut opt))?;
|
||||
}
|
||||
|
||||
Ok(count == 1)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ pub use commit_files::get_commit_files;
|
|||
pub use commits_info::{get_commits_info, CommitId, CommitInfo};
|
||||
pub use diff::get_diff_commit;
|
||||
pub use hooks::{hooks_commit_msg, hooks_post_commit, HookResult};
|
||||
pub use hunks::{stage_hunk, unstage_hunk};
|
||||
pub use hunks::{reset_hunk, stage_hunk, unstage_hunk};
|
||||
pub use ignore::add_to_ignore;
|
||||
pub use logwalker::LogWalker;
|
||||
pub use reset::{reset_stage, reset_workdir};
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ pub fn reset_stage(repo_path: &str, path: &str) -> Result<()> {
|
|||
|
||||
if let Ok(reference) = head {
|
||||
let obj = repo.find_object(
|
||||
//TODO: use NoHead error type
|
||||
reference.target().ok_or_else(|| {
|
||||
Error::Generic(
|
||||
"can't get reference to symbolic reference,"
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ pub fn commit(repo_path: &str, msg: &str) -> Result<Oid> {
|
|||
let tree_id = index.write_tree()?;
|
||||
let tree = repo.find_tree(tree_id)?;
|
||||
|
||||
//TODO: use NoHead error
|
||||
let parents = if let Ok(reference) = repo.head() {
|
||||
let parent = repo.find_commit(
|
||||
reference.target().ok_or_else(|| {
|
||||
|
|
|
|||
|
|
@ -334,6 +334,10 @@ impl App {
|
|||
flags.insert(NeedsUpdate::ALL);
|
||||
}
|
||||
}
|
||||
Action::ResetHunk(path, hash) => {
|
||||
sync::reset_hunk(CWD, path, hash)?;
|
||||
flags.insert(NeedsUpdate::ALL);
|
||||
}
|
||||
},
|
||||
InternalEvent::ConfirmAction(action) => {
|
||||
self.reset.open(action)?;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use super::{CommandBlocking, DrawableComponent, ScrollType};
|
|||
use crate::{
|
||||
components::{CommandInfo, Component},
|
||||
keys,
|
||||
queue::{InternalEvent, Queue},
|
||||
queue::{Action, InternalEvent, Queue},
|
||||
strings,
|
||||
ui::{calc_scroll_top, style::Theme},
|
||||
};
|
||||
|
|
@ -284,9 +284,32 @@ impl DiffComponent {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn reset_hunk(&self) -> Result<()> {
|
||||
if let Some(hunk) = self.selected_hunk {
|
||||
let hash = self.diff.hunks[hunk].header_hash;
|
||||
|
||||
self.queue
|
||||
.as_ref()
|
||||
.expect("try using queue in immutable diff")
|
||||
.borrow_mut()
|
||||
.push_back(InternalEvent::ConfirmAction(
|
||||
Action::ResetHunk(
|
||||
self.current.path.clone(),
|
||||
hash,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_immutable(&self) -> bool {
|
||||
self.queue.is_none()
|
||||
}
|
||||
|
||||
const fn is_stage(&self) -> bool {
|
||||
self.current.is_stage
|
||||
}
|
||||
}
|
||||
|
||||
impl DrawableComponent for DiffComponent {
|
||||
|
|
@ -350,12 +373,17 @@ impl Component for DiffComponent {
|
|||
out.push(CommandInfo::new(
|
||||
commands::DIFF_HUNK_REMOVE,
|
||||
self.selected_hunk.is_some(),
|
||||
self.focused && self.current.is_stage,
|
||||
self.focused && self.is_stage(),
|
||||
));
|
||||
out.push(CommandInfo::new(
|
||||
commands::DIFF_HUNK_ADD,
|
||||
self.selected_hunk.is_some(),
|
||||
self.focused && !self.current.is_stage,
|
||||
self.focused && !self.is_stage(),
|
||||
));
|
||||
out.push(CommandInfo::new(
|
||||
commands::DIFF_HUNK_REVERT,
|
||||
self.selected_hunk.is_some(),
|
||||
self.focused && !self.is_stage(),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -394,6 +422,13 @@ impl Component for DiffComponent {
|
|||
self.add_hunk()?;
|
||||
Ok(true)
|
||||
}
|
||||
keys::DIFF_RESET_HUNK
|
||||
if !self.is_immutable()
|
||||
&& !self.is_stage() =>
|
||||
{
|
||||
self.reset_hunk()?;
|
||||
Ok(true)
|
||||
}
|
||||
_ => Ok(false),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,6 +148,10 @@ impl ResetComponent {
|
|||
strings::CONFIRM_TITLE_STASHDROP,
|
||||
strings::CONFIRM_MSG_STASHDROP,
|
||||
),
|
||||
Action::ResetHunk(_, _) => (
|
||||
strings::CONFIRM_TITLE_RESET,
|
||||
strings::CONFIRM_MSG_RESETHUNK,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ pub const STATUS_STAGE_FILE: KeyEvent = no_mod(KeyCode::Enter);
|
|||
pub const STATUS_STAGE_ALL: KeyEvent = no_mod(KeyCode::Char('a'));
|
||||
pub const STATUS_RESET_FILE: KeyEvent =
|
||||
with_mod(KeyCode::Char('D'), KeyModifiers::SHIFT);
|
||||
pub const DIFF_RESET_HUNK: KeyEvent = STATUS_RESET_FILE;
|
||||
pub const STATUS_IGNORE_FILE: KeyEvent = no_mod(KeyCode::Char('i'));
|
||||
pub const STASHING_SAVE: KeyEvent = no_mod(KeyCode::Char('s'));
|
||||
pub const STASHING_TOGGLE_UNTRACKED: KeyEvent =
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ pub struct ResetItem {
|
|||
///
|
||||
pub enum Action {
|
||||
Reset(ResetItem),
|
||||
ResetHunk(String, u64),
|
||||
StashDrop(CommitId),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ pub static CONFIRM_TITLE_RESET: &str = "Reset";
|
|||
pub static CONFIRM_TITLE_STASHDROP: &str = "Drop";
|
||||
pub static CONFIRM_MSG_RESET: &str = "confirm file reset?";
|
||||
pub static CONFIRM_MSG_STASHDROP: &str = "confirm stash drop?";
|
||||
pub static CONFIRM_MSG_RESETHUNK: &str = "confirm reset hunk?";
|
||||
|
||||
pub static LOG_TITLE: &str = "Commit";
|
||||
pub static STASHLIST_TITLE: &str = "Stashes";
|
||||
|
|
@ -100,6 +101,12 @@ pub mod commands {
|
|||
CMD_GROUP_DIFF,
|
||||
);
|
||||
///
|
||||
pub static DIFF_HUNK_REVERT: CommandText = CommandText::new(
|
||||
"Revert hunk [D]",
|
||||
"reverts selected hunk",
|
||||
CMD_GROUP_DIFF,
|
||||
);
|
||||
///
|
||||
pub static DIFF_HUNK_REMOVE: CommandText = CommandText::new(
|
||||
"Remove hunk [enter]",
|
||||
"removes selected hunk from stage",
|
||||
|
|
|
|||
Loading…
Reference in a new issue