Skip to content

Add pop stash command on Staches tab #628

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions assets/vim_style_key_config.ron
Original file line number Diff line number Diff line change
Expand Up @@ -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,),),

Expand Down
4 changes: 3 additions & 1 deletion asyncgit/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down
83 changes: 82 additions & 1 deletion asyncgit/src/sync/stash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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};

Expand Down Expand Up @@ -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"
);
}
}
19 changes: 19 additions & 0 deletions asyncgit/src/sync/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,25 @@ pub(crate) fn repo_write_file(
Ok(())
}

#[cfg(test)]
pub(crate) fn repo_read_file(
repo: &Repository,
file: &str,
) -> Result<String> {
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::*;
Expand Down
4 changes: 2 additions & 2 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/components/reset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
2 changes: 2 additions & 0 deletions src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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()},
Expand Down
1 change: 1 addition & 0 deletions src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub enum Action {
ResetHunk(String, u64),
ResetLines(String, Vec<DiffLinePosition>),
StashDrop(CommitId),
StashPop(CommitId),
DeleteBranch(String),
ForcePush(String, bool),
PullMerge { incoming: usize, rebase: bool },
Expand Down
23 changes: 22 additions & 1 deletion src/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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 {
Expand Down
47 changes: 45 additions & 2 deletions src/tabs/stashlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern seems pretty common: we run a git command synchronously and queue a InternalEvent when it succeeds or a ShowErrorMsg otherwise. I will take a look in a more DRY approach for the common cases. I don't want to make it part of this PR though.

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 {
Expand All @@ -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,
Expand Down Expand Up @@ -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()
Expand Down