From 5ec4097a8900a807513226ceb5b22368dfca4ace Mon Sep 17 00:00:00 2001 From: brunohgouveia Date: Fri, 9 Apr 2021 23:33:04 +0100 Subject: [PATCH] Add pop stash command on Staches tab --- assets/vim_style_key_config.ron | 1 + asyncgit/src/sync/mod.rs | 4 +- asyncgit/src/sync/stash.rs | 83 ++++++++++++++++++++++++++++++++- asyncgit/src/sync/utils.rs | 19 ++++++++ src/app.rs | 4 +- src/components/reset.rs | 4 ++ src/keys.rs | 2 + src/queue.rs | 1 + src/strings.rs | 23 ++++++++- src/tabs/stashlist.rs | 47 ++++++++++++++++++- 10 files changed, 181 insertions(+), 7 deletions(-) diff --git a/assets/vim_style_key_config.ron b/assets/vim_style_key_config.ron index ddb87d4fbe..2e05689cbd 100644 --- a/assets/vim_style_key_config.ron +++ b/assets/vim_style_key_config.ron @@ -59,6 +59,7 @@ stashing_toggle_untracked: ( code: Char('u'), modifiers: ( bits: 0,),), stashing_toggle_index: ( code: Char('m'), modifiers: ( bits: 0,),), + stash_apply: ( code: Char('a'), modifiers: ( bits: 0,),), stash_open: ( code: Char('l'), modifiers: ( bits: 0,),), stash_drop: ( code: Char('D'), modifiers: ( bits: 1,),), diff --git a/asyncgit/src/sync/mod.rs b/asyncgit/src/sync/mod.rs index c31048fe79..b2dd2e1133 100644 --- a/asyncgit/src/sync/mod.rs +++ b/asyncgit/src/sync/mod.rs @@ -51,7 +51,9 @@ pub use remotes::{ }; pub use reset::{reset_stage, reset_workdir}; pub use staging::{discard_lines, stage_lines}; -pub use stash::{get_stashes, stash_apply, stash_drop, stash_save}; +pub use stash::{ + get_stashes, stash_apply, stash_drop, stash_pop, stash_save, +}; pub use state::{repo_state, RepoState}; pub use tags::{get_tags, CommitTags, Tags}; pub use utils::{ diff --git a/asyncgit/src/sync/stash.rs b/asyncgit/src/sync/stash.rs index 6a87e665b7..a975a7094c 100644 --- a/asyncgit/src/sync/stash.rs +++ b/asyncgit/src/sync/stash.rs @@ -44,6 +44,19 @@ pub fn stash_drop(repo_path: &str, stash_id: CommitId) -> Result<()> { Ok(()) } +/// +pub fn stash_pop(repo_path: &str, stash_id: CommitId) -> Result<()> { + scope_time!("stash_pop"); + + let mut repo = repo(repo_path)?; + + let index = get_stash_index(&mut repo, stash_id.into())?; + + repo.stash_pop(index, None)?; + + Ok(()) +} + /// pub fn stash_apply( repo_path: &str, @@ -122,7 +135,7 @@ mod tests { debug_cmd_print, get_statuses, repo_init, write_commit_file, }, - utils::repo_write_file, + utils::{repo_read_file, repo_write_file}, }; use std::{fs::File, io::Write, path::Path}; @@ -286,4 +299,72 @@ mod tests { assert!(res.is_ok()); } + + #[test] + fn test_stash_pop_no_conflict() { + let (_td, repo) = repo_init().unwrap(); + let root = repo.path().parent().unwrap(); + let repo_path = root.as_os_str().to_str().unwrap(); + + write_commit_file(&repo, "test.txt", "test", "c1"); + + repo_write_file(&repo, "test.txt", "test2").unwrap(); + + let id = + stash_save(repo_path, Some("foo"), true, false).unwrap(); + + let res = stash_pop(repo_path, id); + + assert!(res.is_ok()); + assert_eq!( + repo_read_file(&repo, "test.txt").unwrap(), + "test2" + ); + } + + #[test] + fn test_stash_pop_conflict() { + let (_td, repo) = repo_init().unwrap(); + let root = repo.path().parent().unwrap(); + let repo_path = root.as_os_str().to_str().unwrap(); + + repo_write_file(&repo, "test.txt", "test").unwrap(); + + let id = + stash_save(repo_path, Some("foo"), true, false).unwrap(); + + repo_write_file(&repo, "test.txt", "test2").unwrap(); + + let res = stash_pop(repo_path, id); + + assert!(res.is_err()); + assert_eq!( + repo_read_file(&repo, "test.txt").unwrap(), + "test2" + ); + } + + #[test] + fn test_stash_pop_conflict_after_commit() { + let (_td, repo) = repo_init().unwrap(); + let root = repo.path().parent().unwrap(); + let repo_path = root.as_os_str().to_str().unwrap(); + + write_commit_file(&repo, "test.txt", "test", "c1"); + + repo_write_file(&repo, "test.txt", "test2").unwrap(); + + let id = + stash_save(repo_path, Some("foo"), true, false).unwrap(); + + repo_write_file(&repo, "test.txt", "test3").unwrap(); + + let res = stash_pop(repo_path, id); + + assert!(res.is_err()); + assert_eq!( + repo_read_file(&repo, "test.txt").unwrap(), + "test3" + ); + } } diff --git a/asyncgit/src/sync/utils.rs b/asyncgit/src/sync/utils.rs index 9ffadbce0a..944ea00ac2 100644 --- a/asyncgit/src/sync/utils.rs +++ b/asyncgit/src/sync/utils.rs @@ -187,6 +187,25 @@ pub(crate) fn repo_write_file( Ok(()) } +#[cfg(test)] +pub(crate) fn repo_read_file( + repo: &Repository, + file: &str, +) -> Result { + use std::io::Read; + + let dir = work_dir(repo)?.join(file); + let file_path = dir.to_str().ok_or_else(|| { + Error::Generic(String::from("invalid file path")) + })?; + + let mut file = File::open(file_path)?; + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer)?; + + Ok(String::from_utf8(buffer)?) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/app.rs b/src/app.rs index 600f1a89c7..771845541c 100644 --- a/src/app.rs +++ b/src/app.rs @@ -494,8 +494,8 @@ impl App { flags.insert(NeedsUpdate::ALL); } } - Action::StashDrop(s) => { - if StashList::drop(s) { + Action::StashDrop(_) | Action::StashPop(_) => { + if self.stashlist_tab.action_confirmed(&action) { flags.insert(NeedsUpdate::ALL); } } diff --git a/src/components/reset.rs b/src/components/reset.rs index 0db0fa305c..b31a6799f4 100644 --- a/src/components/reset.rs +++ b/src/components/reset.rs @@ -147,6 +147,10 @@ impl ResetComponent { ), strings::confirm_msg_stashdrop(&self.key_config), ), + Action::StashPop(_) => ( + strings::confirm_title_stashpop(&self.key_config), + strings::confirm_msg_stashpop(&self.key_config), + ), Action::ResetHunk(_, _) => ( strings::confirm_title_reset(&self.key_config), strings::confirm_msg_resethunk(&self.key_config), diff --git a/src/keys.rs b/src/keys.rs index 8b96b183dc..9eed5b598e 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -56,6 +56,7 @@ pub struct KeyConfig { pub stashing_save: KeyEvent, pub stashing_toggle_untracked: KeyEvent, pub stashing_toggle_index: KeyEvent, + pub stash_apply: KeyEvent, pub stash_open: KeyEvent, pub stash_drop: KeyEvent, pub cmd_bar_toggle: KeyEvent, @@ -112,6 +113,7 @@ impl Default for KeyConfig { stashing_save: KeyEvent { code: KeyCode::Char('s'), modifiers: KeyModifiers::empty()}, stashing_toggle_untracked: KeyEvent { code: KeyCode::Char('u'), modifiers: KeyModifiers::empty()}, stashing_toggle_index: KeyEvent { code: KeyCode::Char('i'), modifiers: KeyModifiers::empty()}, + stash_apply: KeyEvent { code: KeyCode::Char('a'), modifiers: KeyModifiers::empty()}, stash_open: KeyEvent { code: KeyCode::Right, modifiers: KeyModifiers::empty()}, stash_drop: KeyEvent { code: KeyCode::Char('D'), modifiers: KeyModifiers::SHIFT}, cmd_bar_toggle: KeyEvent { code: KeyCode::Char('.'), modifiers: KeyModifiers::empty()}, diff --git a/src/queue.rs b/src/queue.rs index 9df50d7b99..a5955d4048 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -29,6 +29,7 @@ pub enum Action { ResetHunk(String, u64), ResetLines(String, Vec), StashDrop(CommitId), + StashPop(CommitId), DeleteBranch(String), ForcePush(String, bool), PullMerge { incoming: usize, rebase: bool }, diff --git a/src/strings.rs b/src/strings.rs index 7b1a92ab61..afca9450a7 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -96,6 +96,11 @@ pub fn confirm_title_stashdrop( ) -> String { "Drop".to_string() } +pub fn confirm_title_stashpop( + _key_config: &SharedKeyConfig, +) -> String { + "Pop".to_string() +} pub fn confirm_title_merge( _key_config: &SharedKeyConfig, rebase: bool, @@ -134,6 +139,10 @@ pub fn confirm_msg_stashdrop( ) -> String { "confirm stash drop?".to_string() } +pub fn confirm_msg_stashpop(_key_config: &SharedKeyConfig) -> String { + "The stash will be applied and then remove from the stash list. Confirm stash pop?" + .to_string() +} pub fn confirm_msg_resethunk( _key_config: &SharedKeyConfig, ) -> String { @@ -748,7 +757,7 @@ pub mod commands { CommandText::new( format!( "Apply [{}]", - key_config.get_hint(key_config.enter), + key_config.get_hint(key_config.stash_apply), ), "apply selected stash", CMD_GROUP_STASHES, @@ -766,6 +775,18 @@ pub mod commands { CMD_GROUP_STASHES, ) } + pub fn stashlist_pop( + key_config: &SharedKeyConfig, + ) -> CommandText { + CommandText::new( + format!( + "Pop [{}]", + key_config.get_hint(key_config.enter), + ), + "pop selected stash", + CMD_GROUP_STASHES, + ) + } pub fn stashlist_inspect( key_config: &SharedKeyConfig, ) -> CommandText { diff --git a/src/tabs/stashlist.rs b/src/tabs/stashlist.rs index 5d7e12967e..9db6f9a05f 100644 --- a/src/tabs/stashlist.rs +++ b/src/tabs/stashlist.rs @@ -83,6 +83,14 @@ impl StashList { } } + fn pop_stash(&mut self) { + if let Some(e) = self.list.selected_entry() { + self.queue.borrow_mut().push_back( + InternalEvent::ConfirmAction(Action::StashPop(e.id)), + ); + } + } + fn inspect(&mut self) { if let Some(e) = self.list.selected_entry() { self.queue @@ -91,10 +99,38 @@ impl StashList { } } - /// - pub fn drop(id: CommitId) -> bool { + /// Called when a pending stash action has been confirmed + pub fn action_confirmed(&self, action: &Action) -> bool { + match *action { + Action::StashDrop(id) => Self::drop(id), + Action::StashPop(id) => self.pop(id), + _ => false, + } + } + + fn drop(id: CommitId) -> bool { sync::stash_drop(CWD, id).is_ok() } + + fn pop(&self, id: CommitId) -> bool { + match sync::stash_pop(CWD, id) { + Ok(_) => { + self.queue + .borrow_mut() + .push_back(InternalEvent::TabSwitch); + true + } + Err(e) => { + self.queue.borrow_mut().push_back( + InternalEvent::ShowErrorMsg(format!( + "stash pop error:\n{}", + e, + )), + ); + true + } + } + } } impl DrawableComponent for StashList { @@ -120,6 +156,11 @@ impl Component for StashList { let selection_valid = self.list.selected_entry().is_some(); + out.push(CommandInfo::new( + strings::commands::stashlist_pop(&self.key_config), + selection_valid, + true, + )); out.push(CommandInfo::new( strings::commands::stashlist_apply(&self.key_config), selection_valid, @@ -150,6 +191,8 @@ impl Component for StashList { if let Event::Key(k) = ev { if k == self.key_config.enter { + self.pop_stash() + } else if k == self.key_config.stash_apply { self.apply_stash() } else if k == self.key_config.stash_drop { self.drop_stash()