From b6c932d0af650a9d0797634cd1146719a78e6598 Mon Sep 17 00:00:00 2001 From: Richard Menzies <52405405+WizardOhio24@users.noreply.github.com> Date: Sun, 11 Oct 2020 13:30:44 +0100 Subject: [PATCH] Delete branch (#332) --- assets/vim_style_key_config.ron | 1 + asyncgit/src/sync/branch.rs | 64 +++++++++++++++++++++++++++++++++ asyncgit/src/sync/mod.rs | 4 +-- src/app.rs | 18 ++++++++-- src/components/reset.rs | 9 +++++ src/components/select_branch.rs | 37 +++++++++++++++++-- src/keys.rs | 2 ++ src/queue.rs | 2 ++ src/strings.rs | 23 ++++++++++++ 9 files changed, 154 insertions(+), 6 deletions(-) diff --git a/assets/vim_style_key_config.ron b/assets/vim_style_key_config.ron index d8194882..1ba5cdc6 100644 --- a/assets/vim_style_key_config.ron +++ b/assets/vim_style_key_config.ron @@ -66,6 +66,7 @@ copy: ( code: Char('y'), modifiers: ( bits: 0,),), create_branch: ( code: Char('c'), modifiers: ( bits: 0,),), select_branch: ( code: Char('b'), modifiers: ( bits: 0,),), + delete_branch: ( code: Char('D'), modifiers: ( bits: 1,),), push: ( code: Char('p'), modifiers: ( bits: 0,),), fetch: ( code: Char('f'), modifiers: ( bits: 0,),), ) diff --git a/asyncgit/src/sync/branch.rs b/asyncgit/src/sync/branch.rs index 5a99344b..3799568b 100644 --- a/asyncgit/src/sync/branch.rs +++ b/asyncgit/src/sync/branch.rs @@ -112,6 +112,24 @@ pub fn checkout_branch( } } +/// The user must not be on the branch for the branch to be deleted +pub fn delete_branch( + repo_path: &str, + branch_ref: &str, +) -> Result<()> { + scope_time!("delete_branch"); + + let repo = utils::repo(repo_path)?; + let branch_as_ref = repo.find_reference(branch_ref)?; + let mut branch = git2::Branch::wrap(branch_as_ref); + if !branch.is_head() { + branch.delete()?; + } else { + return Err(Error::Generic("You cannot be on the branch you want to delete, switch branch, then delete this branch".to_string())); + } + Ok(()) +} + /// creates a new branch pointing to current HEAD commit and updating HEAD to new branch pub fn create_branch(repo_path: &str, name: &str) -> Result<()> { scope_time!("create_branch"); @@ -254,3 +272,49 @@ mod tests_checkout { assert!(checkout_branch(repo_path, "refs/heads/test").is_ok()); } } + +#[cfg(test)] +mod test_delete_branch { + use super::*; + use crate::sync::tests::repo_init; + + #[test] + fn test_delete_branch() { + let (_td, repo) = repo_init().unwrap(); + let root = repo.path().parent().unwrap(); + let repo_path = root.as_os_str().to_str().unwrap(); + + create_branch(repo_path, "branch1").unwrap(); + create_branch(repo_path, "branch2").unwrap(); + + checkout_branch(repo_path, "refs/heads/branch1").unwrap(); + + assert_eq!( + repo.branches(None) + .unwrap() + .nth(1) + .unwrap() + .unwrap() + .0 + .name() + .unwrap() + .unwrap(), + "branch2" + ); + + delete_branch(repo_path, "refs/heads/branch2").unwrap(); + + assert_eq!( + repo.branches(None) + .unwrap() + .nth(1) + .unwrap() + .unwrap() + .0 + .name() + .unwrap() + .unwrap(), + "master" + ); + } +} diff --git a/asyncgit/src/sync/mod.rs b/asyncgit/src/sync/mod.rs index 8cf657a7..3f0dc369 100644 --- a/asyncgit/src/sync/mod.rs +++ b/asyncgit/src/sync/mod.rs @@ -19,8 +19,8 @@ pub mod utils; pub(crate) use branch::get_branch_name; pub use branch::{ - checkout_branch, create_branch, get_branches_to_display, - BranchForDisplay, + checkout_branch, create_branch, delete_branch, + get_branches_to_display, BranchForDisplay, }; pub use commit::{amend, commit, tag}; pub use commit_details::{ diff --git a/src/app.rs b/src/app.rs index 3febcdb6..fa3ac5e0 100644 --- a/src/app.rs +++ b/src/app.rs @@ -473,6 +473,20 @@ impl App { sync::reset_hunk(CWD, path, hash)?; flags.insert(NeedsUpdate::ALL); } + Action::DeleteBranch(branch_ref) => { + if let Err(e) = + sync::delete_branch(CWD, &branch_ref) + { + self.queue.borrow_mut().push_back( + InternalEvent::ShowErrorMsg( + e.to_string(), + ), + ) + } else { + flags.insert(NeedsUpdate::ALL); + self.select_branch_popup.hide(); + } + } }, InternalEvent::ConfirmAction(action) => { self.reset.open(action)?; @@ -593,15 +607,15 @@ impl App { self.commit.draw(f, size)?; self.stashmsg_popup.draw(f, size)?; - self.reset.draw(f, size)?; self.help.draw(f, size)?; self.inspect_commit_popup.draw(f, size)?; - self.msg.draw(f, size)?; self.external_editor_popup.draw(f, size)?; self.tag_commit_popup.draw(f, size)?; self.select_branch_popup.draw(f, size)?; self.create_branch_popup.draw(f, size)?; self.push_popup.draw(f, size)?; + self.reset.draw(f, size)?; + self.msg.draw(f, size)?; Ok(()) } diff --git a/src/components/reset.rs b/src/components/reset.rs index 63a853e9..2055964c 100644 --- a/src/components/reset.rs +++ b/src/components/reset.rs @@ -151,6 +151,15 @@ impl ResetComponent { strings::confirm_title_reset(&self.key_config), strings::confirm_msg_resethunk(&self.key_config), ), + Action::DeleteBranch(branch_ref) => ( + strings::confirm_title_delete_branch( + &self.key_config, + ), + strings::confirm_msg_delete_branch( + &self.key_config, + branch_ref.to_string(), + ), + ), }; } diff --git a/src/components/select_branch.rs b/src/components/select_branch.rs index 991db320..2f4eedba 100644 --- a/src/components/select_branch.rs +++ b/src/components/select_branch.rs @@ -4,7 +4,7 @@ use super::{ }; use crate::{ keys::SharedKeyConfig, - queue::{InternalEvent, NeedsUpdate, Queue}, + queue::{Action, InternalEvent, NeedsUpdate, Queue}, strings, ui, }; use asyncgit::{ @@ -45,7 +45,7 @@ impl DrawableComponent for SelectBranchComponent { // Render a scrolllist of branches inside a box if self.visible { - const SIZE: (u16, u16) = (50, 45); + const SIZE: (u16, u16) = (50, 20); let scroll_threshold = SIZE.1 / 3; let scroll = self.selection.saturating_sub(scroll_threshold); @@ -113,6 +113,14 @@ impl Component for SelectBranchComponent { true, true, )); + + out.push(CommandInfo::new( + strings::commands::delete_branch_popup( + &self.key_config, + ), + !self.selection_is_cur_branch(), + true, + )); } visibility_blocking(self) } @@ -142,6 +150,19 @@ impl Component for SelectBranchComponent { .borrow_mut() .push_back(InternalEvent::CreateBranch); self.hide(); + } else if e == self.key_config.delete_branch + && !self.selection_is_cur_branch() + { + self.queue.borrow_mut().push_back( + InternalEvent::ConfirmAction( + Action::DeleteBranch( + self.branch_names + [self.selection as usize] + .reference + .clone(), + ), + ), + ); } } @@ -200,6 +221,18 @@ impl SelectBranchComponent { Ok(()) } + /// + pub fn selection_is_cur_branch(&self) -> bool { + self.branch_names + .iter() + .enumerate() + .filter(|(index, b)| { + b.is_head && *index == self.selection as usize + }) + .count() + > 0 + } + /// fn move_selection(&mut self, inc: bool) { let mut new_selection = self.selection; diff --git a/src/keys.rs b/src/keys.rs index 9faad7a6..8a4169f5 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -61,6 +61,7 @@ pub struct KeyConfig { pub copy: KeyEvent, pub create_branch: KeyEvent, pub select_branch: KeyEvent, + pub delete_branch: KeyEvent, pub push: KeyEvent, pub fetch: KeyEvent, } @@ -113,6 +114,7 @@ impl Default for KeyConfig { copy: KeyEvent { code: KeyCode::Char('y'), modifiers: KeyModifiers::empty()}, create_branch: KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::NONE}, select_branch: KeyEvent { code: KeyCode::Char('b'), modifiers: KeyModifiers::NONE}, + delete_branch: KeyEvent{code: KeyCode::Char('D'), modifiers: KeyModifiers::SHIFT}, push: KeyEvent { code: KeyCode::Char('p'), modifiers: KeyModifiers::empty()}, fetch: KeyEvent { code: KeyCode::Char('f'), modifiers: KeyModifiers::empty()}, } diff --git a/src/queue.rs b/src/queue.rs index 6802434d..40e55274 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -28,6 +28,8 @@ pub enum Action { Reset(ResetItem), ResetHunk(String, u64), StashDrop(CommitId), + /// + DeleteBranch(String), } /// diff --git a/src/strings.rs b/src/strings.rs index 2ca6fb4a..f9e5919f 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -90,6 +90,17 @@ pub fn confirm_msg_resethunk( ) -> String { "confirm reset hunk?".to_string() } +pub fn confirm_title_delete_branch( + _key_config: &SharedKeyConfig, +) -> String { + "Delete Branch".to_string() +} +pub fn confirm_msg_delete_branch( + _key_config: &SharedKeyConfig, + branch_ref: String, +) -> String { + branch_ref + "\nconfirm delete branch?" +} pub fn log_title(_key_config: &SharedKeyConfig) -> String { "Commit".to_string() } @@ -604,6 +615,18 @@ pub mod commands { CMD_GROUP_GENERAL, ) } + pub fn delete_branch_popup( + key_config: &SharedKeyConfig, + ) -> CommandText { + CommandText::new( + format!( + "Delete [{}]", + get_hint(key_config.delete_branch), + ), + "delete a branch", + CMD_GROUP_GENERAL, + ) + } pub fn open_branch_select_popup( key_config: &SharedKeyConfig, ) -> CommandText {