mirror of
https://github.com/gitui-org/gitui
synced 2026-05-23 00:48:35 +00:00
allow rebase of a branch (#816)
This commit is contained in:
parent
f51a3a953a
commit
a8654329ec
9 changed files with 201 additions and 119 deletions
|
|
@ -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,
|
||||
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
28
asyncgit/src/sync/rebase.rs
Normal file
28
asyncgit/src/sync/rebase.rs
Normal 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(())
|
||||
}
|
||||
|
|
@ -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,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,),),
|
||||
|
|
|
|||
Loading…
Reference in a new issue