mirror of
https://github.com/jesseduffield/lazygit.git
synced 2024-11-24 08:52:21 +02:00
Add command to rebase onto base branch (#3615)
- **PR Description** In the rebase menu, add a command "Rebase onto base branch". This makes it more convenient to rebase onto master (or main), because - you don't need to bring your local version of the base branch up to date first - you don't have to remember which of your main branches (e.g. "main", "devel", or "1.0-hotfixes") your current branch is based on. This is sitting on top of #3614. Closes #3546. - **Please check if the PR fulfills these requirements** * [x] Cheatsheets are up-to-date (run `go generate ./...`) * [x] Code has been formatted (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting)) * [x] Tests have been added/updated (see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md) for the integration test guide) * [x] Text is internationalised (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation)) * [x] Docs have been updated if necessary * [x] You've read through your own file changes for silly mistakes etc
This commit is contained in:
commit
b85687797d
@ -100,14 +100,13 @@ func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*ty
|
||||
DisplayOnScreen: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Branches.RebaseBranch),
|
||||
Handler: opts.Guards.OutsideFilterMode(self.rebase),
|
||||
GetDisabledReason: self.require(
|
||||
self.singleItemSelected(self.notRebasingOntoSelf),
|
||||
),
|
||||
Description: self.c.Tr.RebaseBranch,
|
||||
Tooltip: self.c.Tr.RebaseBranchTooltip,
|
||||
DisplayOnScreen: true,
|
||||
Key: opts.GetKey(opts.Config.Branches.RebaseBranch),
|
||||
Handler: opts.Guards.OutsideFilterMode(self.withItem(self.rebase)),
|
||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||
Description: self.c.Tr.RebaseBranch,
|
||||
Tooltip: self.c.Tr.RebaseBranchTooltip,
|
||||
OpensMenu: true,
|
||||
DisplayOnScreen: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Branches.MergeIntoCurrentBranch),
|
||||
@ -633,19 +632,8 @@ func (self *BranchesController) merge() error {
|
||||
return self.c.Helpers().MergeAndRebase.MergeRefIntoCheckedOutBranch(selectedBranchName)
|
||||
}
|
||||
|
||||
func (self *BranchesController) rebase() error {
|
||||
selectedBranchName := self.context().GetSelected().Name
|
||||
return self.c.Helpers().MergeAndRebase.RebaseOntoRef(selectedBranchName)
|
||||
}
|
||||
|
||||
func (self *BranchesController) notRebasingOntoSelf(branch *models.Branch) *types.DisabledReason {
|
||||
selectedBranchName := branch.Name
|
||||
checkedOutBranch := self.c.Helpers().Refs.GetCheckedOutRef().Name
|
||||
if selectedBranchName == checkedOutBranch {
|
||||
return &types.DisabledReason{Text: self.c.Tr.CantRebaseOntoSelf}
|
||||
}
|
||||
|
||||
return nil
|
||||
func (self *BranchesController) rebase(branch *models.Branch) error {
|
||||
return self.c.Helpers().MergeAndRebase.RebaseOntoRef(branch.Name)
|
||||
}
|
||||
|
||||
func (self *BranchesController) fastForward(branch *models.Branch) error {
|
||||
|
@ -234,11 +234,29 @@ func (self *MergeAndRebaseHelper) PromptToContinueRebase() error {
|
||||
}
|
||||
|
||||
func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error {
|
||||
checkedOutBranch := self.refsHelper.GetCheckedOutRef().Name
|
||||
checkedOutBranch := self.refsHelper.GetCheckedOutRef()
|
||||
checkedOutBranchName := self.refsHelper.GetCheckedOutRef().Name
|
||||
var disabledReason, baseBranchDisabledReason *types.DisabledReason
|
||||
if checkedOutBranchName == ref {
|
||||
disabledReason = &types.DisabledReason{Text: self.c.Tr.CantRebaseOntoSelf}
|
||||
}
|
||||
|
||||
baseBranch, err := self.c.Git().Loaders.BranchLoader.GetBaseBranch(checkedOutBranch, self.refsHelper.c.Model().MainBranches)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if baseBranch == "" {
|
||||
baseBranch = self.c.Tr.CouldNotDetermineBaseBranch
|
||||
baseBranchDisabledReason = &types.DisabledReason{Text: self.c.Tr.CouldNotDetermineBaseBranch}
|
||||
}
|
||||
|
||||
menuItems := []*types.MenuItem{
|
||||
{
|
||||
Label: self.c.Tr.SimpleRebase,
|
||||
Key: 's',
|
||||
Label: utils.ResolvePlaceholderString(self.c.Tr.SimpleRebase,
|
||||
map[string]string{"ref": ref},
|
||||
),
|
||||
Key: 's',
|
||||
DisabledReason: disabledReason,
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.RebaseBranch)
|
||||
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(task gocui.Task) error {
|
||||
@ -258,9 +276,12 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error {
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.InteractiveRebase,
|
||||
Key: 'i',
|
||||
Tooltip: self.c.Tr.InteractiveRebaseTooltip,
|
||||
Label: utils.ResolvePlaceholderString(self.c.Tr.InteractiveRebase,
|
||||
map[string]string{"ref": ref},
|
||||
),
|
||||
Key: 'i',
|
||||
DisabledReason: disabledReason,
|
||||
Tooltip: self.c.Tr.InteractiveRebaseTooltip,
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.RebaseBranch)
|
||||
baseCommit := self.c.Modes().MarkedBaseCommit.GetHash()
|
||||
@ -279,6 +300,31 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error {
|
||||
return self.c.PushContext(self.c.Contexts().LocalCommits)
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: utils.ResolvePlaceholderString(self.c.Tr.RebaseOntoBaseBranch,
|
||||
map[string]string{"baseBranch": ShortBranchName(baseBranch)},
|
||||
),
|
||||
Key: 'b',
|
||||
DisabledReason: baseBranchDisabledReason,
|
||||
Tooltip: self.c.Tr.RebaseOntoBaseBranchTooltip,
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.RebaseBranch)
|
||||
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(task gocui.Task) error {
|
||||
baseCommit := self.c.Modes().MarkedBaseCommit.GetHash()
|
||||
var err error
|
||||
if baseCommit != "" {
|
||||
err = self.c.Git().Rebase.RebaseBranchFromBaseCommit(baseBranch, baseCommit)
|
||||
} else {
|
||||
err = self.c.Git().Rebase.RebaseBranch(baseBranch)
|
||||
}
|
||||
err = self.CheckMergeOrRebase(err)
|
||||
if err == nil {
|
||||
return self.ResetMarkedBaseCommit()
|
||||
}
|
||||
return err
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
title := utils.ResolvePlaceholderString(
|
||||
@ -286,8 +332,7 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error {
|
||||
self.c.Tr.RebasingFromBaseCommitTitle,
|
||||
self.c.Tr.RebasingTitle),
|
||||
map[string]string{
|
||||
"checkedOutBranch": checkedOutBranch,
|
||||
"ref": ref,
|
||||
"checkedOutBranch": checkedOutBranchName,
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -289,7 +289,9 @@ type TranslationSet struct {
|
||||
RebasingFromBaseCommitTitle string
|
||||
SimpleRebase string
|
||||
InteractiveRebase string
|
||||
RebaseOntoBaseBranch string
|
||||
InteractiveRebaseTooltip string
|
||||
RebaseOntoBaseBranchTooltip string
|
||||
MustSelectTodoCommits string
|
||||
ConfirmMerge string
|
||||
FwdNoUpstream string
|
||||
@ -1253,11 +1255,13 @@ func EnglishTranslationSet() TranslationSet {
|
||||
KeybindingsMenuSectionLocal: "Local",
|
||||
KeybindingsMenuSectionGlobal: "Global",
|
||||
KeybindingsMenuSectionNavigation: "Navigation",
|
||||
RebasingTitle: "Rebase '{{.checkedOutBranch}}' onto '{{.ref}}'",
|
||||
RebasingFromBaseCommitTitle: "Rebase '{{.checkedOutBranch}}' from marked base onto '{{.ref}}'",
|
||||
SimpleRebase: "Simple rebase",
|
||||
InteractiveRebase: "Interactive rebase",
|
||||
RebasingTitle: "Rebase '{{.checkedOutBranch}}'",
|
||||
RebasingFromBaseCommitTitle: "Rebase '{{.checkedOutBranch}}' from marked base",
|
||||
SimpleRebase: "Simple rebase onto '{{.ref}}'",
|
||||
InteractiveRebase: "Interactive rebase onto '{{.ref}}'",
|
||||
RebaseOntoBaseBranch: "Rebase onto base branch ({{.baseBranch}})",
|
||||
InteractiveRebaseTooltip: "Begin an interactive rebase with a break at the start, so you can update the TODO commits before continuing.",
|
||||
RebaseOntoBaseBranchTooltip: "Rebase the checked out branch onto its base branch (i.e. the closest main branch).",
|
||||
MustSelectTodoCommits: "When rebasing, this action only works on a selection of TODO commits.",
|
||||
ConfirmMerge: "Are you sure you want to merge '{{.selectedBranch}}' into '{{.checkedOutBranch}}'?",
|
||||
FwdNoUpstream: "Cannot fast-forward a branch with no upstream",
|
||||
@ -1443,7 +1447,7 @@ func EnglishTranslationSet() TranslationSet {
|
||||
ViewUpstreamResetOptions: "Reset checked-out branch onto {{.upstream}}",
|
||||
ViewUpstreamResetOptionsTooltip: "View options for resetting the checked-out branch onto {{upstream}}. Note: this will not reset the selected branch onto the upstream, it will reset the checked-out branch onto the upstream.",
|
||||
ViewUpstreamRebaseOptions: "Rebase checked-out branch onto {{.upstream}}",
|
||||
ViewUpstreamRebaseOptionsTooltip: "View options for rebasing the checked-out branch onto {{upstream}}. Note: this will not rebase the selected branch onto the upstream, it will rebased the checked-out branch onto the upstream.",
|
||||
ViewUpstreamRebaseOptionsTooltip: "View options for rebasing the checked-out branch onto {{upstream}}. Note: this will not rebase the selected branch onto the upstream, it will rebase the checked-out branch onto the upstream.",
|
||||
UpstreamGenericName: "upstream of selected branch",
|
||||
SetUpstreamTitle: "Set upstream branch",
|
||||
SetUpstreamMessage: "Are you sure you want to set the upstream branch of '{{.checkedOut}}' to '{{.selected}}'",
|
||||
|
@ -274,10 +274,10 @@ func polishTranslationSet() TranslationSet {
|
||||
KeybindingsMenuSectionLocal: "Lokalne",
|
||||
KeybindingsMenuSectionGlobal: "Globalne",
|
||||
KeybindingsMenuSectionNavigation: "Nawigacja",
|
||||
RebasingTitle: "Rebase '{{.checkedOutBranch}}' na '{{.ref}}'",
|
||||
RebasingFromBaseCommitTitle: "Rebase '{{.checkedOutBranch}}' od oznaczonego commita bazowego na '{{.ref}}'",
|
||||
SimpleRebase: "Prosty rebase",
|
||||
InteractiveRebase: "Interaktywny rebase",
|
||||
RebasingTitle: "Rebase '{{.checkedOutBranch}}'",
|
||||
RebasingFromBaseCommitTitle: "Rebase '{{.checkedOutBranch}}' od oznaczonego commita bazowego",
|
||||
SimpleRebase: "Prosty rebase na '{{.ref}}'",
|
||||
InteractiveRebase: "Interaktywny rebase na '{{.ref}}'",
|
||||
InteractiveRebaseTooltip: "Rozpocznij interaktywny rebase z przerwaniem na początku, abyś mógł zaktualizować commity TODO przed kontynuacją.",
|
||||
MustSelectTodoCommits: "Podczas rebase ta akcja działa tylko na zaznaczonych commitach TODO.",
|
||||
ConfirmMerge: "Czy na pewno chcesz scalić '{{.selectedBranch}}' z '{{.checkedOutBranch}}'?",
|
||||
|
@ -226,9 +226,9 @@ func RussianTranslationSet() TranslationSet {
|
||||
ConflictsResolved: "Все конфликты слияния разрешены. Продолжить?",
|
||||
Continue: "Продолжить",
|
||||
Keybindings: "Связки клавиш",
|
||||
RebasingTitle: "Перебазировать '{{.checkedOutBranch}}' на '{{.ref}}'",
|
||||
SimpleRebase: "Простая перебазировка",
|
||||
InteractiveRebase: "Интерактивная перебазировка",
|
||||
RebasingTitle: "Перебазировать '{{.checkedOutBranch}}'",
|
||||
SimpleRebase: "Простая перебазировка на '{{.ref}}'",
|
||||
InteractiveRebase: "Интерактивная перебазировка на '{{.ref}}'",
|
||||
InteractiveRebaseTooltip: "Начать интерактивную перебазировку с перерыва в начале, чтобы можно было обновить TODO коммиты, прежде чем продолжить.",
|
||||
ConfirmMerge: "Вы уверены, что хотите to merge '{{.selectedBranch}}' into '{{.checkedOutBranch}}'?",
|
||||
FwdNoUpstream: "Невозможно перемотать ветку без upstream-ветки",
|
||||
|
@ -256,9 +256,9 @@ func traditionalChineseTranslationSet() TranslationSet {
|
||||
ConflictsResolved: "所有合併衝突都已解決。是否繼續?",
|
||||
Continue: "確認",
|
||||
Keybindings: "鍵盤快捷鍵",
|
||||
RebasingTitle: "將 '{{.checkedOutBranch}}' 變基至 '{{.ref}}'",
|
||||
SimpleRebase: "簡單變基",
|
||||
InteractiveRebase: "互動變基",
|
||||
RebasingTitle: "將 '{{.checkedOutBranch}}'",
|
||||
SimpleRebase: "簡單變基 變基至 '{{.ref}}'",
|
||||
InteractiveRebase: "互動變基 變基至 '{{.ref}}'",
|
||||
InteractiveRebaseTooltip: "開始一個互動變基,以中斷開始,這樣你可以在繼續之前更新TODO提交",
|
||||
ConfirmMerge: "是否將 '{{.selectedBranch}}' 合併至 '{{.checkedOutBranch}}' ?",
|
||||
FwdNoUpstream: "無法快進無上游分支",
|
||||
|
@ -31,7 +31,7 @@ var Rebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Press(keys.Branches.RebaseBranch)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Rebase 'first-change-branch' onto 'second-change-branch'")).
|
||||
Title(Equals("Rebase 'first-change-branch'")).
|
||||
Select(Contains("Simple rebase")).
|
||||
Confirm()
|
||||
|
||||
|
@ -31,7 +31,7 @@ var RebaseAbortOnConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Press(keys.Branches.RebaseBranch)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Rebase 'first-change-branch' onto 'second-change-branch'")).
|
||||
Title(Equals("Rebase 'first-change-branch'")).
|
||||
Select(Contains("Simple rebase")).
|
||||
Confirm()
|
||||
|
||||
|
@ -37,7 +37,7 @@ var RebaseAndDrop = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Press(keys.Branches.RebaseBranch)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Rebase 'first-change-branch' onto 'second-change-branch'")).
|
||||
Title(Equals("Rebase 'first-change-branch'")).
|
||||
Select(Contains("Simple rebase")).
|
||||
Confirm()
|
||||
|
||||
|
@ -31,7 +31,7 @@ var RebaseCancelOnConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Press(keys.Branches.RebaseBranch)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Rebase 'first-change-branch' onto 'second-change-branch'")).
|
||||
Title(Equals("Rebase 'first-change-branch'")).
|
||||
Select(Contains("Simple rebase")).
|
||||
Confirm()
|
||||
|
||||
|
@ -42,7 +42,7 @@ var RebaseCopiedBranch = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Press(keys.Branches.RebaseBranch).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Rebase 'branch2' onto 'master'")).
|
||||
Title(Equals("Rebase 'branch2'")).
|
||||
Select(Contains("Simple rebase")).
|
||||
Confirm()
|
||||
})
|
||||
|
@ -40,7 +40,7 @@ var RebaseDoesNotAutosquash = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Press(keys.Branches.RebaseBranch)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Rebase 'my-branch' onto 'master'")).
|
||||
Title(Equals("Rebase 'my-branch'")).
|
||||
Select(Contains("Simple rebase")).
|
||||
Confirm()
|
||||
|
||||
|
@ -61,7 +61,7 @@ var RebaseFromMarkedBase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Press(keys.Branches.RebaseBranch)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Rebase 'active-branch' from marked base onto 'target-branch'")).
|
||||
Title(Equals("Rebase 'active-branch' from marked base")).
|
||||
Select(Contains("Simple rebase")).
|
||||
Confirm()
|
||||
|
||||
|
53
pkg/integration/tests/branch/rebase_onto_base_branch.go
Normal file
53
pkg/integration/tests/branch/rebase_onto_base_branch.go
Normal file
@ -0,0 +1,53 @@
|
||||
package branch
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var RebaseOntoBaseBranch = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Rebase the current branch onto its base branch",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.UserConfig.Gui.ShowDivergenceFromBaseBranch = "arrowAndNumber"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.
|
||||
EmptyCommit("master 1").
|
||||
EmptyCommit("master 2").
|
||||
EmptyCommit("master 3").
|
||||
NewBranchFrom("feature", "master^").
|
||||
EmptyCommit("feature 1").
|
||||
EmptyCommit("feature 2")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().Lines(
|
||||
Contains("feature 2"),
|
||||
Contains("feature 1"),
|
||||
Contains("master 2"),
|
||||
Contains("master 1"),
|
||||
)
|
||||
|
||||
t.Views().Branches().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("feature ↓1").IsSelected(),
|
||||
Contains("master"),
|
||||
).
|
||||
Press(keys.Branches.RebaseBranch)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Rebase 'feature'")).
|
||||
Select(Contains("Rebase onto base branch (master)")).
|
||||
Confirm()
|
||||
|
||||
t.Views().Commits().Lines(
|
||||
Contains("feature 2"),
|
||||
Contains("feature 1"),
|
||||
Contains("master 3"),
|
||||
Contains("master 2"),
|
||||
Contains("master 1"),
|
||||
)
|
||||
},
|
||||
})
|
@ -67,7 +67,7 @@ var RebaseToUpstream = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Select(Contains("Rebase checked-out branch onto origin/master...")).
|
||||
Confirm()
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Rebase 'target' onto 'origin/master'")).
|
||||
Title(Equals("Rebase 'target'")).
|
||||
Select(Contains("Simple rebase")).
|
||||
Confirm()
|
||||
})
|
||||
|
@ -39,7 +39,7 @@ var AdvancedInteractiveRebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Press(keys.Branches.RebaseBranch)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals(fmt.Sprintf("Rebase '%s' onto '%s'", TOP_BRANCH, BASE_BRANCH))).
|
||||
Title(Equals(fmt.Sprintf("Rebase '%s'", TOP_BRANCH))).
|
||||
Select(Contains("Interactive rebase")).
|
||||
Confirm()
|
||||
t.Views().Commits().
|
||||
|
@ -51,6 +51,7 @@ var tests = []*components.IntegrationTest{
|
||||
branch.RebaseCopiedBranch,
|
||||
branch.RebaseDoesNotAutosquash,
|
||||
branch.RebaseFromMarkedBase,
|
||||
branch.RebaseOntoBaseBranch,
|
||||
branch.RebaseToUpstream,
|
||||
branch.Rename,
|
||||
branch.Reset,
|
||||
|
Loading…
Reference in New Issue
Block a user