Rename branch (#334)

This commit is contained in:
Richard Menzies 2020-10-17 08:00:34 +01:00 committed by GitHub
parent 9297915ddd
commit 71880f96f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 301 additions and 3 deletions

View file

@ -65,6 +65,7 @@
commit_amend: ( code: Char('A'), modifiers: ( bits: 1,),),
copy: ( code: Char('y'), modifiers: ( bits: 0,),),
create_branch: ( code: Char('c'), modifiers: ( bits: 0,),),
rename_branch: ( code: Char('r'), 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,),),

View file

@ -130,6 +130,22 @@ pub fn delete_branch(
Ok(())
}
/// Rename the branch reference
pub fn rename_branch(
repo_path: &str,
branch_ref: &str,
new_name: &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);
branch.rename(new_name, true)?;
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");
@ -318,3 +334,49 @@ mod test_delete_branch {
);
}
}
#[cfg(test)]
mod test_rename_branch {
use super::*;
use crate::sync::tests::repo_init;
#[test]
fn test_rename_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();
checkout_branch(repo_path, "refs/heads/branch1").unwrap();
assert_eq!(
repo.branches(None)
.unwrap()
.nth(0)
.unwrap()
.unwrap()
.0
.name()
.unwrap()
.unwrap(),
"branch1"
);
rename_branch(repo_path, "refs/heads/branch1", "AnotherName")
.unwrap();
assert_eq!(
repo.branches(None)
.unwrap()
.nth(0)
.unwrap()
.unwrap()
.0
.name()
.unwrap()
.unwrap(),
"AnotherName"
);
}
}

View file

@ -20,7 +20,7 @@ pub mod utils;
pub(crate) use branch::get_branch_name;
pub use branch::{
checkout_branch, create_branch, delete_branch,
get_branches_to_display, BranchForDisplay,
get_branches_to_display, rename_branch, BranchForDisplay,
};
pub use commit::{amend, commit, tag};
pub use commit_details::{

View file

@ -6,8 +6,8 @@ use crate::{
Component, CreateBranchComponent, DrawableComponent,
ExternalEditorComponent, HelpComponent,
InspectCommitComponent, MsgComponent, PushComponent,
ResetComponent, SelectBranchComponent, StashMsgComponent,
TagCommitComponent,
RenameBranchComponent, ResetComponent, SelectBranchComponent,
StashMsgComponent, TagCommitComponent,
},
input::{Input, InputEvent, InputState},
keys::{KeyConfig, SharedKeyConfig},
@ -46,6 +46,7 @@ pub struct App {
push_popup: PushComponent,
tag_commit_popup: TagCommitComponent,
create_branch_popup: CreateBranchComponent,
rename_branch_popup: RenameBranchComponent,
select_branch_popup: SelectBranchComponent,
cmdbar: RefCell<CommandBar>,
tab: usize,
@ -118,6 +119,11 @@ impl App {
theme.clone(),
key_config.clone(),
),
rename_branch_popup: RenameBranchComponent::new(
queue.clone(),
theme.clone(),
key_config.clone(),
),
select_branch_popup: SelectBranchComponent::new(
queue.clone(),
theme.clone(),
@ -342,6 +348,7 @@ impl App {
push_popup,
tag_commit_popup,
create_branch_popup,
rename_branch_popup,
select_branch_popup,
help,
revlog,
@ -509,6 +516,10 @@ impl App {
InternalEvent::CreateBranch => {
self.create_branch_popup.open()?;
}
InternalEvent::RenameBranch(branch_ref, cur_name) => {
self.rename_branch_popup
.open(branch_ref, cur_name)?;
}
InternalEvent::SelectBranch => {
self.select_branch_popup.open()?;
}
@ -588,6 +599,7 @@ impl App {
|| self.create_branch_popup.is_visible()
|| self.push_popup.is_visible()
|| self.select_branch_popup.is_visible()
|| self.rename_branch_popup.is_visible()
}
fn draw_popups<B: Backend>(
@ -613,6 +625,7 @@ impl App {
self.tag_commit_popup.draw(f, size)?;
self.select_branch_popup.draw(f, size)?;
self.create_branch_popup.draw(f, size)?;
self.rename_branch_popup.draw(f, size)?;
self.push_popup.draw(f, size)?;
self.reset.draw(f, size)?;
self.msg.draw(f, size)?;

View file

@ -11,6 +11,7 @@ mod help;
mod inspect_commit;
mod msg;
mod push;
mod rename_branch;
mod reset;
mod select_branch;
mod stashmsg;
@ -34,6 +35,7 @@ pub use help::HelpComponent;
pub use inspect_commit::InspectCommitComponent;
pub use msg::MsgComponent;
pub use push::PushComponent;
pub use rename_branch::RenameBranchComponent;
pub use reset::ResetComponent;
pub use select_branch::SelectBranchComponent;
pub use stashmsg::StashMsgComponent;

View file

@ -0,0 +1,166 @@
use super::{
textinput::TextInputComponent, visibility_blocking,
CommandBlocking, CommandInfo, Component, DrawableComponent,
};
use crate::{
keys::SharedKeyConfig,
queue::{InternalEvent, NeedsUpdate, Queue},
strings,
ui::style::SharedTheme,
};
use anyhow::Result;
use asyncgit::{
sync::{self},
CWD,
};
use crossterm::event::Event;
use tui::{backend::Backend, layout::Rect, Frame};
pub struct RenameBranchComponent {
input: TextInputComponent,
branch_ref: Option<String>,
queue: Queue,
key_config: SharedKeyConfig,
}
impl DrawableComponent for RenameBranchComponent {
fn draw<B: Backend>(
&self,
f: &mut Frame<B>,
rect: Rect,
) -> Result<()> {
self.input.draw(f, rect)?;
Ok(())
}
}
impl Component for RenameBranchComponent {
fn commands(
&self,
out: &mut Vec<CommandInfo>,
force_all: bool,
) -> CommandBlocking {
if self.is_visible() || force_all {
self.input.commands(out, force_all);
out.push(CommandInfo::new(
strings::commands::rename_branch_confirm_msg(
&self.key_config,
),
true,
true,
));
}
visibility_blocking(self)
}
fn event(&mut self, ev: Event) -> Result<bool> {
if self.is_visible() {
if self.input.event(ev)? {
return Ok(true);
}
if let Event::Key(e) = ev {
if e == self.key_config.enter {
self.rename_branch();
}
return Ok(true);
}
}
Ok(false)
}
fn is_visible(&self) -> bool {
self.input.is_visible()
}
fn hide(&mut self) {
self.input.hide()
}
fn show(&mut self) -> Result<()> {
self.input.show()?;
Ok(())
}
}
impl RenameBranchComponent {
///
pub fn new(
queue: Queue,
theme: SharedTheme,
key_config: SharedKeyConfig,
) -> Self {
Self {
queue,
input: TextInputComponent::new(
theme,
key_config.clone(),
&strings::rename_branch_popup_title(&key_config),
&strings::rename_branch_popup_msg(&key_config),
),
branch_ref: None,
key_config,
}
}
///
pub fn open(
&mut self,
branch_ref: String,
cur_name: String,
) -> Result<()> {
self.branch_ref = None;
self.branch_ref = Some(branch_ref);
self.input.set_text(cur_name);
self.show()?;
Ok(())
}
///
pub fn rename_branch(&mut self) {
if let Some(br) = &self.branch_ref {
let res = sync::rename_branch(
CWD,
br,
self.input.get_text().as_str(),
);
match res {
Ok(_) => {
self.queue.borrow_mut().push_back(
InternalEvent::Update(NeedsUpdate::ALL),
);
self.hide();
self.queue
.borrow_mut()
.push_back(InternalEvent::SelectBranch);
}
Err(e) => {
log::error!("create branch: {}", e,);
self.queue.borrow_mut().push_back(
InternalEvent::ShowErrorMsg(format!(
"rename branch error:\n{}",
e,
)),
);
}
}
} else {
log::error!("create branch: No branch selected");
self.queue
.borrow_mut()
.push_back(InternalEvent::ShowErrorMsg(
"rename branch error: No branch selected to rename"
.to_string(),
));
}
self.input.clear();
}
}

View file

@ -121,6 +121,14 @@ impl Component for SelectBranchComponent {
!self.selection_is_cur_branch(),
true,
));
out.push(CommandInfo::new(
strings::commands::rename_branch_popup(
&self.key_config,
),
true,
true,
));
}
visibility_blocking(self)
}
@ -150,6 +158,16 @@ impl Component for SelectBranchComponent {
.borrow_mut()
.push_back(InternalEvent::CreateBranch);
self.hide();
} else if e == self.key_config.rename_branch {
let cur_branch =
&self.branch_names[self.selection as usize];
self.queue.borrow_mut().push_back(
InternalEvent::RenameBranch(
cur_branch.reference.clone(),
cur_branch.name.clone(),
),
);
self.hide();
} else if e == self.key_config.delete_branch
&& !self.selection_is_cur_branch()
{

View file

@ -60,6 +60,7 @@ pub struct KeyConfig {
pub commit_amend: KeyEvent,
pub copy: KeyEvent,
pub create_branch: KeyEvent,
pub rename_branch: KeyEvent,
pub select_branch: KeyEvent,
pub delete_branch: KeyEvent,
pub push: KeyEvent,
@ -113,6 +114,7 @@ impl Default for KeyConfig {
commit_amend: KeyEvent { code: KeyCode::Char('a'), modifiers: KeyModifiers::CONTROL},
copy: KeyEvent { code: KeyCode::Char('y'), modifiers: KeyModifiers::empty()},
create_branch: KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::NONE},
rename_branch: KeyEvent { code: KeyCode::Char('r'), 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()},

View file

@ -54,6 +54,8 @@ pub enum InternalEvent {
///
CreateBranch,
///
RenameBranch(String, String),
///
SelectBranch,
///
OpenExternalEditor(Option<String>),

View file

@ -140,6 +140,17 @@ pub fn create_branch_popup_msg(
"type branch name".to_string()
}
pub fn rename_branch_popup_title(
_key_config: &SharedKeyConfig,
) -> String {
"Rename Branch".to_string()
}
pub fn rename_branch_popup_msg(
_key_config: &SharedKeyConfig,
) -> String {
"new branch name".to_string()
}
pub mod commit {
use crate::keys::SharedKeyConfig;
pub fn details_author(_key_config: &SharedKeyConfig) -> String {
@ -615,6 +626,27 @@ pub mod commands {
CMD_GROUP_GENERAL,
)
}
pub fn rename_branch_confirm_msg(
key_config: &SharedKeyConfig,
) -> CommandText {
CommandText::new(
format!("Rename Branch [{}]", get_hint(key_config.enter),),
"rename branch",
CMD_GROUP_GENERAL,
)
}
pub fn rename_branch_popup(
key_config: &SharedKeyConfig,
) -> CommandText {
CommandText::new(
format!(
"Rename Branch [{}]",
get_hint(key_config.rename_branch),
),
"rename branch",
CMD_GROUP_GENERAL,
)
}
pub fn delete_branch_popup(
key_config: &SharedKeyConfig,
) -> CommandText {