From 6dd43d4822d8ff89e22b8d9baa0f04665c3b40ad Mon Sep 17 00:00:00 2001 From: ejaszczuk Date: Fri, 4 Jun 2021 23:32:16 +0200 Subject: [PATCH 1/3] Add undo_last_commit command --- asyncgit/src/sync/utils.rs | 15 +++++++++++++++ src/keys.rs | 2 ++ src/strings.rs | 10 ++++++++++ src/tabs/status.rs | 15 +++++++++++++++ 4 files changed, 42 insertions(+) diff --git a/asyncgit/src/sync/utils.rs b/asyncgit/src/sync/utils.rs index e1c245eb34..7ff5525bd0 100644 --- a/asyncgit/src/sync/utils.rs +++ b/asyncgit/src/sync/utils.rs @@ -149,6 +149,21 @@ pub fn stage_add_all(repo_path: &str, pattern: &str) -> Result<()> { Ok(()) } +/// Undo last commit in repo +pub fn undo_last_commit(repo_path: &str) -> Result<()> { + let repo = repo(repo_path)?; + let previous_commit = repo.revparse_single("HEAD~")?; + + Repository::reset( + &repo, + &previous_commit, + git2::ResetType::Soft, + None, + )?; + + Ok(()) +} + /// stage a removed file pub fn stage_addremoved(repo_path: &str, path: &Path) -> Result<()> { scope_time!("stage_addremoved"); diff --git a/src/keys.rs b/src/keys.rs index d424785fb8..252c6c154b 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -81,6 +81,7 @@ pub struct KeyConfig { pub force_push: KeyEvent, pub pull: KeyEvent, pub abort_merge: KeyEvent, + pub undo_commit: KeyEvent, } #[rustfmt::skip] @@ -147,6 +148,7 @@ impl Default for KeyConfig { pull: KeyEvent { code: KeyCode::Char('f'), modifiers: KeyModifiers::empty()}, abort_merge: KeyEvent { code: KeyCode::Char('M'), modifiers: KeyModifiers::SHIFT}, open_file_tree: KeyEvent { code: KeyCode::Char('F'), modifiers: KeyModifiers::SHIFT}, + undo_commit: KeyEvent { code: KeyCode::Char('u'), modifiers: KeyModifiers::empty()}, } } } diff --git a/src/strings.rs b/src/strings.rs index b1eeabf191..68fafde45d 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -579,6 +579,16 @@ pub mod commands { CMD_GROUP_GENERAL, ) } + pub fn undo_commit(key_config: &SharedKeyConfig) -> CommandText { + CommandText::new( + format!( + "Undo commit [{}]", + key_config.get_hint(key_config.undo_commit), + ), + "undo last commit", + CMD_GROUP_GENERAL, + ) + } pub fn commit_open(key_config: &SharedKeyConfig) -> CommandText { CommandText::new( format!( diff --git a/src/tabs/status.rs b/src/tabs/status.rs index 068a3f02e4..49806a5f37 100644 --- a/src/tabs/status.rs +++ b/src/tabs/status.rs @@ -468,6 +468,10 @@ impl Status { } } + fn undo_last_commit(&self) { + sync::utils::undo_last_commit(CWD).unwrap(); + } + fn branch_compare(&mut self) { self.git_branch_state = self.git_branch_name.last().and_then(|branch| { @@ -579,6 +583,12 @@ impl Component for Status { !focus_on_diff, )); + out.push(CommandInfo::new( + strings::commands::undo_commit(&self.key_config), + true, + !focus_on_diff, + )); + out.push(CommandInfo::new( strings::commands::abort_merge(&self.key_config), true, @@ -687,6 +697,11 @@ impl Component for Status { { self.pull(); Ok(EventState::Consumed) + } else if k == self.key_config.undo_commit + && !self.is_focus_on_diff() + { + self.undo_last_commit(); + Ok(EventState::Consumed) } else if k == self.key_config.abort_merge && Self::can_abort_merge() { From d3b88e4ecbeda087ccc0326f9d42747e35f35331 Mon Sep 17 00:00:00 2001 From: ejaszczuk Date: Sun, 6 Jun 2021 16:12:34 +0200 Subject: [PATCH 2/3] Add tests and update queue --- CHANGELOG.md | 1 + asyncgit/src/sync/utils.rs | 48 ++++++++++++++++++++++++++++++++++++++ src/keys.rs | 2 +- src/strings.rs | 2 +- src/tabs/status.rs | 11 +++++++-- vim_style_key_config.ron | 1 + 6 files changed, 61 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf6972ef2c..157f8d02e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Added - honor `config.showUntrackedFiles` improving speed with a lot of untracked items ([#752](https://github.com/extrawurst/gitui/issues/752)) - improve performance when opening filetree-tab ([#756](https://github.com/extrawurst/gitui/issues/756)) +- undo-last-commit command under `[U]` key [[@remique](https://github.com/remique)] ([#758](https://github.com/extrawurst/gitui/issues/758)) ## Fixed - wrong file with same name shown in file tree ([#748](https://github.com/extrawurst/gitui/issues/748)) diff --git a/asyncgit/src/sync/utils.rs b/asyncgit/src/sync/utils.rs index 7ff5525bd0..d33c5ab1b0 100644 --- a/asyncgit/src/sync/utils.rs +++ b/asyncgit/src/sync/utils.rs @@ -297,6 +297,54 @@ mod tests { Ok(()) } + #[test] + fn test_undo_commit_empty_repo() -> Result<()> { + let (_td, repo) = repo_init().unwrap(); + let root = repo.path().parent().unwrap(); + let repo_path = root.as_os_str().to_str().unwrap(); + + // expect to fail + assert!(undo_last_commit(repo_path).is_err()); + + Ok(()) + } + + #[test] + fn test_undo_commit() -> Result<()> { + let (_td, repo) = repo_init().unwrap(); + let root = repo.path().parent().unwrap(); + let repo_path = root.as_os_str().to_str().unwrap(); + let file_path = Path::new("file1.txt"); + + let status_count = |s: StatusType| -> usize { + get_status(repo_path, s).unwrap().len() + }; + + let full_path = &root.join(file_path); + File::create(full_path) + .unwrap() + .write_all(b"first commit content") + .unwrap(); + + stage_add_file(repo_path, file_path).unwrap(); + commit(repo_path, "first commit").unwrap(); + + let second_file_path = Path::new("file2.txt"); + let full_path = &root.join(second_file_path); + File::create(full_path) + .unwrap() + .write_all(b"second commit content") + .unwrap(); + stage_add_file(repo_path, second_file_path).unwrap(); + commit(repo_path, "second commit").unwrap(); + + // Should be 1 file in staged + assert!(undo_last_commit(repo_path).is_ok()); + assert_eq!(status_count(StatusType::Stage), 1); + + Ok(()) + } + #[test] fn test_not_staging_untracked_folder() -> Result<()> { let (_td, repo) = repo_init().unwrap(); diff --git a/src/keys.rs b/src/keys.rs index 252c6c154b..9ace952513 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -145,10 +145,10 @@ impl Default for KeyConfig { select_tag: KeyEvent { code: KeyCode::Enter, modifiers: KeyModifiers::empty()}, push: KeyEvent { code: KeyCode::Char('p'), modifiers: KeyModifiers::empty()}, force_push: KeyEvent { code: KeyCode::Char('P'), modifiers: KeyModifiers::SHIFT}, + undo_commit: KeyEvent { code: KeyCode::Char('U'), modifiers: KeyModifiers::SHIFT}, pull: KeyEvent { code: KeyCode::Char('f'), modifiers: KeyModifiers::empty()}, abort_merge: KeyEvent { code: KeyCode::Char('M'), modifiers: KeyModifiers::SHIFT}, open_file_tree: KeyEvent { code: KeyCode::Char('F'), modifiers: KeyModifiers::SHIFT}, - undo_commit: KeyEvent { code: KeyCode::Char('u'), modifiers: KeyModifiers::empty()}, } } } diff --git a/src/strings.rs b/src/strings.rs index 68fafde45d..ee57e7b6c0 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -582,7 +582,7 @@ pub mod commands { pub fn undo_commit(key_config: &SharedKeyConfig) -> CommandText { CommandText::new( format!( - "Undo commit [{}]", + "Undo Commit [{}]", key_config.get_hint(key_config.undo_commit), ), "undo last commit", diff --git a/src/tabs/status.rs b/src/tabs/status.rs index 49806a5f37..cc76c31698 100644 --- a/src/tabs/status.rs +++ b/src/tabs/status.rs @@ -7,7 +7,7 @@ use crate::{ FileTreeItemKind, }, keys::SharedKeyConfig, - queue::{Action, InternalEvent, Queue, ResetItem}, + queue::{Action, InternalEvent, NeedsUpdate, Queue, ResetItem}, strings, try_or_popup, ui::style::SharedTheme, }; @@ -469,7 +469,11 @@ impl Status { } fn undo_last_commit(&self) { - sync::utils::undo_last_commit(CWD).unwrap(); + try_or_popup!( + self, + "undo commit failed:", + sync::utils::undo_last_commit(CWD) + ); } fn branch_compare(&mut self) { @@ -701,6 +705,9 @@ impl Component for Status { && !self.is_focus_on_diff() { self.undo_last_commit(); + self.queue.borrow_mut().push_back( + InternalEvent::Update(NeedsUpdate::ALL), + ); Ok(EventState::Consumed) } else if k == self.key_config.abort_merge && Self::can_abort_merge() diff --git a/vim_style_key_config.ron b/vim_style_key_config.ron index f23e1d958a..8fc1575904 100644 --- a/vim_style_key_config.ron +++ b/vim_style_key_config.ron @@ -34,6 +34,7 @@ // Also just plain text characters will not work because the commit // msg editor will interpret them as text input open_commit_editor: ( code: Char('e'), modifiers: ( bits: 2,),), + undo_commit: ( code: Char('U'), modifiers: ( bits: 1,),), move_left: ( code: Char('h'), modifiers: ( bits: 0,),), move_right: ( code: Char('l'), modifiers: ( bits: 0,),), From c299e9abc173a573637776b5c1d54cc1906816dd Mon Sep 17 00:00:00 2001 From: ejaszczuk Date: Mon, 7 Jun 2021 15:09:52 +0200 Subject: [PATCH 3/3] Change CHANGELOG and unittests --- CHANGELOG.md | 4 ++- asyncgit/src/sync/utils.rs | 51 ++++++++++++++++---------------------- 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e80ec36a6..69ff87de3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added +- undo-last-commit command under `[U]` key [[@remique](https://github.com/remique)] ([#758](https://github.com/extrawurst/gitui/issues/758)) + ## [0.16.1] - 2021-06-06 ## Added @@ -15,7 +18,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - indicator for longer commit message than displayed ([#773](https://github.com/extrawurst/gitui/issues/773)) ![msg-len](assets/long-msg-indicator.gif) -- undo-last-commit command under `[U]` key [[@remique](https://github.com/remique)] ([#758](https://github.com/extrawurst/gitui/issues/758)) ## Fixed - wrong file with same name shown in file tree ([#748](https://github.com/extrawurst/gitui/issues/748)) diff --git a/asyncgit/src/sync/utils.rs b/asyncgit/src/sync/utils.rs index d33c5ab1b0..1fbafff525 100644 --- a/asyncgit/src/sync/utils.rs +++ b/asyncgit/src/sync/utils.rs @@ -221,9 +221,11 @@ mod tests { use super::*; use crate::sync::{ commit, + diff::get_diff, status::{get_status, StatusType}, tests::{ - debug_cmd_print, get_statuses, repo_init, repo_init_empty, + debug_cmd_print, get_statuses, repo_init, + repo_init_empty, write_commit_file, }, }; use std::{ @@ -298,51 +300,40 @@ mod tests { } #[test] - fn test_undo_commit_empty_repo() -> Result<()> { + fn test_undo_commit_empty_repo() { let (_td, repo) = repo_init().unwrap(); let root = repo.path().parent().unwrap(); let repo_path = root.as_os_str().to_str().unwrap(); // expect to fail assert!(undo_last_commit(repo_path).is_err()); - - Ok(()) } #[test] - fn test_undo_commit() -> Result<()> { + fn test_undo_commit() { let (_td, repo) = repo_init().unwrap(); let root = repo.path().parent().unwrap(); let repo_path = root.as_os_str().to_str().unwrap(); - let file_path = Path::new("file1.txt"); - - let status_count = |s: StatusType| -> usize { - get_status(repo_path, s).unwrap().len() - }; - - let full_path = &root.join(file_path); - File::create(full_path) - .unwrap() - .write_all(b"first commit content") - .unwrap(); - stage_add_file(repo_path, file_path).unwrap(); - commit(repo_path, "first commit").unwrap(); + // write commit file test.txt + let c1 = + write_commit_file(&repo, "test.txt", "content1", "c1"); + let _c2 = + write_commit_file(&repo, "test.txt", "content2", "c2"); + assert!(undo_last_commit(repo_path).is_ok()); - let second_file_path = Path::new("file2.txt"); - let full_path = &root.join(second_file_path); - File::create(full_path) - .unwrap() - .write_all(b"second commit content") - .unwrap(); - stage_add_file(repo_path, second_file_path).unwrap(); - commit(repo_path, "second commit").unwrap(); + // Make sure that HEAD points to c1 + assert_eq!(c1, get_head_repo(&repo).unwrap()); - // Should be 1 file in staged - assert!(undo_last_commit(repo_path).is_ok()); - assert_eq!(status_count(StatusType::Stage), 1); + // Make sure that now we have 1 file staged + assert_eq!(get_statuses(repo_path), (0, 1)); - Ok(()) + // And that file is test.txt + let diff = get_diff(repo_path, "test.txt", true).unwrap(); + assert_eq!( + diff.hunks[0].lines[0].content, + String::from("@@ -1 +1 @@\n") + ); } #[test]