Skip to content

Commit

Permalink
Reset individual hunks (#125)
Browse files Browse the repository at this point in the history
closes #11
  • Loading branch information
Stephan Dilly committed Jun 14, 2020
1 parent 0cdaabf commit 24da2f2
Show file tree
Hide file tree
Showing 12 changed files with 94 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))

![](assets/amend.gif)
Expand Down
1 change: 1 addition & 0 deletions asyncgit/src/sync/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
41 changes: 34 additions & 7 deletions asyncgit/src/sync/hunks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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()));
}
Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion asyncgit/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
1 change: 1 addition & 0 deletions asyncgit/src/sync/reset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,"
Expand Down
1 change: 1 addition & 0 deletions asyncgit/src/sync/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(|| {
Expand Down
4 changes: 4 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand Down
41 changes: 38 additions & 3 deletions src/components/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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(),
));
}

Expand Down Expand Up @@ -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),
};
}
Expand Down
4 changes: 4 additions & 0 deletions src/components/reset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ impl ResetComponent {
strings::CONFIRM_TITLE_STASHDROP,
strings::CONFIRM_MSG_STASHDROP,
),
Action::ResetHunk(_, _) => (
strings::CONFIRM_TITLE_RESET,
strings::CONFIRM_MSG_RESETHUNK,
),
};
}

Expand Down
1 change: 1 addition & 0 deletions src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
1 change: 1 addition & 0 deletions src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub struct ResetItem {
///
pub enum Action {
Reset(ResetItem),
ResetHunk(String, u64),
StashDrop(CommitId),
}

Expand Down
7 changes: 7 additions & 0 deletions src/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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",
Expand Down

0 comments on commit 24da2f2

Please sign in to comment.