allow rebase of a branch (#816)

This commit is contained in:
Stephan Dilly 2021-08-26 20:31:37 +02:00
parent f51a3a953a
commit a8654329ec
9 changed files with 201 additions and 119 deletions

View file

@ -11,6 +11,9 @@ pub enum Error {
#[error("git: no head found")]
NoHead,
#[error("git: conflict during rebase")]
RebaseConflict,
#[error("git: remote url not found")]
UnknownRemote,

View file

@ -2,7 +2,7 @@
use crate::{
error::{Error, Result},
sync::utils,
sync::{rebase::conflict_free_rebase, utils},
};
use git2::BranchType;
use scopetime::scope_time;
@ -27,34 +27,7 @@ pub fn merge_upstream_rebase(
let annotated_upstream =
repo.find_annotated_commit(upstream_commit.id())?;
let mut rebase =
repo.rebase(None, Some(&annotated_upstream), None, None)?;
let signature =
crate::sync::commit::signature_allow_undefined_name(&repo)?;
while let Some(op) = rebase.next() {
let _op = op?;
// dbg!(op.id());
if repo.index()?.has_conflicts() {
rebase.abort()?;
return Err(Error::Generic(String::from(
"conflicts while merging",
)));
}
rebase.commit(None, &signature, None)?;
}
if repo.index()?.has_conflicts() {
rebase.abort()?;
return Err(Error::Generic(String::from(
"conflicts while merging",
)));
}
rebase.finish(Some(&signature))?;
conflict_free_rebase(&repo, &annotated_upstream)?;
Ok(())
}

View file

@ -8,6 +8,8 @@ use crate::{
use git2::{BranchType, Commit, MergeOptions, Repository};
use scopetime::scope_time;
use super::rebase::conflict_free_rebase;
///
pub fn mergehead_ids(repo_path: &str) -> Result<Vec<CommitId>> {
scope_time!("mergehead_ids");
@ -51,6 +53,17 @@ pub fn merge_branch(repo_path: &str, branch: &str) -> Result<()> {
Ok(())
}
///
pub fn rebase_branch(repo_path: &str, branch: &str) -> Result<()> {
scope_time!("rebase_branch");
let repo = utils::repo(repo_path)?;
rebase_branch_repo(&repo, branch)?;
Ok(())
}
///
pub fn merge_branch_repo(
repo: &Repository,
@ -75,6 +88,21 @@ pub fn merge_branch_repo(
Ok(())
}
///
pub fn rebase_branch_repo(
repo: &Repository,
branch_name: &str,
) -> Result<()> {
let branch = repo.find_branch(branch_name, BranchType::Local)?;
let annotated =
repo.reference_to_annotated_commit(&branch.into_reference())?;
conflict_free_rebase(repo, &annotated)?;
Ok(())
}
///
pub fn merge_msg(repo_path: &str) -> Result<String> {
scope_time!("merge_msg");

View file

@ -18,6 +18,7 @@ mod ignore;
mod logwalker;
mod merge;
mod patches;
mod rebase;
pub mod remotes;
mod reset;
mod staging;
@ -57,7 +58,8 @@ pub use hunks::{reset_hunk, stage_hunk, unstage_hunk};
pub use ignore::add_to_ignore;
pub use logwalker::{LogWalker, LogWalkerFilter};
pub use merge::{
abort_merge, merge_branch, merge_commit, merge_msg, mergehead_ids,
abort_merge, merge_branch, merge_commit, merge_msg,
mergehead_ids, rebase_branch,
};
pub use remotes::{
get_default_remote, get_remotes, push::AsyncProgress,

View file

@ -0,0 +1,28 @@
use crate::error::{Error, Result};
/// rebase attempt which aborts and undo's rebase if any conflict appears
pub fn conflict_free_rebase(
repo: &git2::Repository,
commit: &git2::AnnotatedCommit,
) -> Result<()> {
let mut rebase = repo.rebase(None, Some(commit), None, None)?;
let signature =
crate::sync::commit::signature_allow_undefined_name(repo)?;
while let Some(op) = rebase.next() {
let _op = op?;
// dbg!(op.id());
if repo.index()?.has_conflicts() {
rebase.abort()?;
return Err(Error::RebaseConflict);
}
rebase.commit(None, &signature, None)?;
}
if repo.index()?.has_conflicts() {
rebase.abort()?;
return Err(Error::RebaseConflict);
}
rebase.finish(Some(&signature))?;
Ok(())
}

View file

@ -180,6 +180,14 @@ impl Component for BranchListComponent {
self.local,
));
out.push(CommandInfo::new(
strings::commands::branch_popup_rebase(
&self.key_config,
),
!self.selection_is_cur_branch(),
self.local,
));
out.push(CommandInfo::new(
strings::commands::rename_branch_popup(
&self.key_config,
@ -191,100 +199,91 @@ impl Component for BranchListComponent {
visibility_blocking(self)
}
//TODO: cleanup
#[allow(clippy::cognitive_complexity)]
fn event(&mut self, ev: Event) -> Result<EventState> {
if self.visible {
if let Event::Key(e) = ev {
if e == self.key_config.exit_popup {
self.hide();
} else if e == self.key_config.move_down {
return self
.move_selection(ScrollType::Up)
.map(Into::into);
} else if e == self.key_config.move_up {
return self
.move_selection(ScrollType::Down)
.map(Into::into);
} else if e == self.key_config.page_down {
return self
.move_selection(ScrollType::PageDown)
.map(Into::into);
} else if e == self.key_config.page_up {
return self
.move_selection(ScrollType::PageUp)
.map(Into::into);
} else if e == self.key_config.tab_toggle {
self.local = !self.local;
self.update_branches()?;
} else if e == self.key_config.enter {
try_or_popup!(
self,
"switch branch error:",
self.switch_to_selected_branch()
);
} else if e == self.key_config.create_branch
&& self.local
{
self.queue.push(InternalEvent::CreateBranch);
} else if e == self.key_config.rename_branch
&& self.valid_selection()
{
let cur_branch =
&self.branches[self.selection as usize];
self.queue.push(InternalEvent::RenameBranch(
cur_branch.reference.clone(),
cur_branch.name.clone(),
));
if !self.visible {
return Ok(EventState::NotConsumed);
}
self.update_branches()?;
} else if e == self.key_config.delete_branch
&& !self.selection_is_cur_branch()
&& self.valid_selection()
{
self.queue.push(InternalEvent::ConfirmAction(
Action::DeleteBranch(
self.branches[self.selection as usize]
.reference
.clone(),
self.local,
),
));
} else if e == self.key_config.merge_branch
&& !self.selection_is_cur_branch()
&& self.valid_selection()
{
try_or_popup!(
self,
"merge branch error:",
self.merge_branch()
);
self.queue.push(InternalEvent::Update(
NeedsUpdate::ALL,
));
} else if e == self.key_config.move_right
&& self.valid_selection()
{
self.hide();
if let Some(b) = self.get_selected() {
self.queue.push(
InternalEvent::InspectCommit(b, None),
);
}
} else if e == self.key_config.compare_commits
&& self.valid_selection()
{
self.hide();
if let Some(b) = self.get_selected() {
self.queue.push(
InternalEvent::CompareCommits(b, None),
);
}
if let Event::Key(e) = ev {
if e == self.key_config.exit_popup {
self.hide();
} else if e == self.key_config.move_down {
return self
.move_selection(ScrollType::Up)
.map(Into::into);
} else if e == self.key_config.move_up {
return self
.move_selection(ScrollType::Down)
.map(Into::into);
} else if e == self.key_config.page_down {
return self
.move_selection(ScrollType::PageDown)
.map(Into::into);
} else if e == self.key_config.page_up {
return self
.move_selection(ScrollType::PageUp)
.map(Into::into);
} else if e == self.key_config.tab_toggle {
self.local = !self.local;
self.update_branches()?;
} else if e == self.key_config.enter {
try_or_popup!(
self,
"switch branch error:",
self.switch_to_selected_branch()
);
} else if e == self.key_config.create_branch && self.local
{
self.queue.push(InternalEvent::CreateBranch);
} else if e == self.key_config.rename_branch
&& self.valid_selection()
{
self.rename_branch();
} else if e == self.key_config.delete_branch
&& !self.selection_is_cur_branch()
&& self.valid_selection()
{
self.delete_branch();
} else if e == self.key_config.merge_branch
&& !self.selection_is_cur_branch()
&& self.valid_selection()
{
try_or_popup!(
self,
"merge branch error:",
self.merge_branch()
);
} else if e == self.key_config.rebase_branch
&& !self.selection_is_cur_branch()
&& self.valid_selection()
{
try_or_popup!(
self,
"rebase error:",
self.rebase_branch()
);
} else if e == self.key_config.move_right
&& self.valid_selection()
{
self.hide();
if let Some(b) = self.get_selected() {
self.queue
.push(InternalEvent::InspectCommit(b, None));
}
} else if e == self.key_config.compare_commits
&& self.valid_selection()
{
self.hide();
if let Some(b) = self.get_selected() {
self.queue
.push(InternalEvent::CompareCommits(b, None));
}
}
Ok(EventState::Consumed)
} else {
Ok(EventState::NotConsumed)
}
Ok(EventState::Consumed)
}
fn is_visible(&self) -> bool {
@ -368,6 +367,20 @@ impl BranchListComponent {
self.branches.get(usize::from(self.selection))
{
sync::merge_branch(CWD, &branch.name)?;
self.queue.push(InternalEvent::Update(NeedsUpdate::ALL));
}
Ok(())
}
fn rebase_branch(&self) -> Result<()> {
if let Some(branch) =
self.branches.get(usize::from(self.selection))
{
sync::rebase_branch(CWD, &branch.name)?;
self.queue.push(InternalEvent::Update(NeedsUpdate::ALL));
}
Ok(())
@ -623,4 +636,23 @@ impl BranchListComponent {
Ok(())
}
fn rename_branch(&mut self) {
let cur_branch = &self.branches[self.selection as usize];
self.queue.push(InternalEvent::RenameBranch(
cur_branch.reference.clone(),
cur_branch.name.clone(),
));
}
fn delete_branch(&mut self) {
self.queue.push(InternalEvent::ConfirmAction(
Action::DeleteBranch(
self.branches[self.selection as usize]
.reference
.clone(),
self.local,
),
));
}
}

View file

@ -76,6 +76,7 @@ pub struct KeyConfig {
pub select_branch: KeyEvent,
pub delete_branch: KeyEvent,
pub merge_branch: KeyEvent,
pub rebase_branch: KeyEvent,
pub compare_commits: KeyEvent,
pub tags: KeyEvent,
pub delete_tag: KeyEvent,
@ -147,6 +148,7 @@ impl Default for KeyConfig {
select_branch: KeyEvent { code: KeyCode::Char('b'), modifiers: KeyModifiers::empty()},
delete_branch: KeyEvent { code: KeyCode::Char('D'), modifiers: KeyModifiers::SHIFT},
merge_branch: KeyEvent { code: KeyCode::Char('m'), modifiers: KeyModifiers::empty()},
rebase_branch: KeyEvent { code: KeyCode::Char('R'), modifiers: KeyModifiers::SHIFT},
compare_commits: KeyEvent { code: KeyCode::Char('C'), modifiers: KeyModifiers::SHIFT},
tags: KeyEvent { code: KeyCode::Char('T'), modifiers: KeyModifiers::SHIFT},
delete_tag: KeyEvent { code: KeyCode::Char('D'), modifiers: KeyModifiers::SHIFT},

View file

@ -1059,6 +1059,19 @@ pub mod commands {
)
}
pub fn branch_popup_rebase(
key_config: &SharedKeyConfig,
) -> CommandText {
CommandText::new(
format!(
"Rebase [{}]",
key_config.get_hint(key_config.rebase_branch),
),
"rebase a branch",
CMD_GROUP_BRANCHES,
)
}
pub fn compare_with_head(
key_config: &SharedKeyConfig,
) -> CommandText {

View file

@ -84,6 +84,7 @@
select_branch: ( code: Char('b'), modifiers: ( bits: 0,),),
delete_branch: ( code: Char('D'), modifiers: ( bits: 1,),),
merge_branch: ( code: Char('m'), modifiers: ( bits: 0,),),
rebase_branch: ( code: Char('R'), modifiers: ( bits: 1,),),
abort_merge: ( code: Char('M'), modifiers: ( bits: 1,),),
compare_commits: ( code: Char('C'), modifiers: ( bits: 1,),),