Skip to content

Commit b856877

Browse files
authored
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
2 parents 36a4696 + a8921a1 commit b856877

17 files changed

+144
-53
lines changed

pkg/gui/controllers/branches_controller.go

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,13 @@ func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*ty
100100
DisplayOnScreen: true,
101101
},
102102
{
103-
Key: opts.GetKey(opts.Config.Branches.RebaseBranch),
104-
Handler: opts.Guards.OutsideFilterMode(self.rebase),
105-
GetDisabledReason: self.require(
106-
self.singleItemSelected(self.notRebasingOntoSelf),
107-
),
108-
Description: self.c.Tr.RebaseBranch,
109-
Tooltip: self.c.Tr.RebaseBranchTooltip,
110-
DisplayOnScreen: true,
103+
Key: opts.GetKey(opts.Config.Branches.RebaseBranch),
104+
Handler: opts.Guards.OutsideFilterMode(self.withItem(self.rebase)),
105+
GetDisabledReason: self.require(self.singleItemSelected()),
106+
Description: self.c.Tr.RebaseBranch,
107+
Tooltip: self.c.Tr.RebaseBranchTooltip,
108+
OpensMenu: true,
109+
DisplayOnScreen: true,
111110
},
112111
{
113112
Key: opts.GetKey(opts.Config.Branches.MergeIntoCurrentBranch),
@@ -633,19 +632,8 @@ func (self *BranchesController) merge() error {
633632
return self.c.Helpers().MergeAndRebase.MergeRefIntoCheckedOutBranch(selectedBranchName)
634633
}
635634

636-
func (self *BranchesController) rebase() error {
637-
selectedBranchName := self.context().GetSelected().Name
638-
return self.c.Helpers().MergeAndRebase.RebaseOntoRef(selectedBranchName)
639-
}
640-
641-
func (self *BranchesController) notRebasingOntoSelf(branch *models.Branch) *types.DisabledReason {
642-
selectedBranchName := branch.Name
643-
checkedOutBranch := self.c.Helpers().Refs.GetCheckedOutRef().Name
644-
if selectedBranchName == checkedOutBranch {
645-
return &types.DisabledReason{Text: self.c.Tr.CantRebaseOntoSelf}
646-
}
647-
648-
return nil
635+
func (self *BranchesController) rebase(branch *models.Branch) error {
636+
return self.c.Helpers().MergeAndRebase.RebaseOntoRef(branch.Name)
649637
}
650638

651639
func (self *BranchesController) fastForward(branch *models.Branch) error {

pkg/gui/controllers/helpers/merge_and_rebase_helper.go

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -234,11 +234,29 @@ func (self *MergeAndRebaseHelper) PromptToContinueRebase() error {
234234
}
235235

236236
func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error {
237-
checkedOutBranch := self.refsHelper.GetCheckedOutRef().Name
237+
checkedOutBranch := self.refsHelper.GetCheckedOutRef()
238+
checkedOutBranchName := self.refsHelper.GetCheckedOutRef().Name
239+
var disabledReason, baseBranchDisabledReason *types.DisabledReason
240+
if checkedOutBranchName == ref {
241+
disabledReason = &types.DisabledReason{Text: self.c.Tr.CantRebaseOntoSelf}
242+
}
243+
244+
baseBranch, err := self.c.Git().Loaders.BranchLoader.GetBaseBranch(checkedOutBranch, self.refsHelper.c.Model().MainBranches)
245+
if err != nil {
246+
return err
247+
}
248+
if baseBranch == "" {
249+
baseBranch = self.c.Tr.CouldNotDetermineBaseBranch
250+
baseBranchDisabledReason = &types.DisabledReason{Text: self.c.Tr.CouldNotDetermineBaseBranch}
251+
}
252+
238253
menuItems := []*types.MenuItem{
239254
{
240-
Label: self.c.Tr.SimpleRebase,
241-
Key: 's',
255+
Label: utils.ResolvePlaceholderString(self.c.Tr.SimpleRebase,
256+
map[string]string{"ref": ref},
257+
),
258+
Key: 's',
259+
DisabledReason: disabledReason,
242260
OnPress: func() error {
243261
self.c.LogAction(self.c.Tr.Actions.RebaseBranch)
244262
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(task gocui.Task) error {
@@ -258,9 +276,12 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error {
258276
},
259277
},
260278
{
261-
Label: self.c.Tr.InteractiveRebase,
262-
Key: 'i',
263-
Tooltip: self.c.Tr.InteractiveRebaseTooltip,
279+
Label: utils.ResolvePlaceholderString(self.c.Tr.InteractiveRebase,
280+
map[string]string{"ref": ref},
281+
),
282+
Key: 'i',
283+
DisabledReason: disabledReason,
284+
Tooltip: self.c.Tr.InteractiveRebaseTooltip,
264285
OnPress: func() error {
265286
self.c.LogAction(self.c.Tr.Actions.RebaseBranch)
266287
baseCommit := self.c.Modes().MarkedBaseCommit.GetHash()
@@ -279,15 +300,39 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error {
279300
return self.c.PushContext(self.c.Contexts().LocalCommits)
280301
},
281302
},
303+
{
304+
Label: utils.ResolvePlaceholderString(self.c.Tr.RebaseOntoBaseBranch,
305+
map[string]string{"baseBranch": ShortBranchName(baseBranch)},
306+
),
307+
Key: 'b',
308+
DisabledReason: baseBranchDisabledReason,
309+
Tooltip: self.c.Tr.RebaseOntoBaseBranchTooltip,
310+
OnPress: func() error {
311+
self.c.LogAction(self.c.Tr.Actions.RebaseBranch)
312+
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(task gocui.Task) error {
313+
baseCommit := self.c.Modes().MarkedBaseCommit.GetHash()
314+
var err error
315+
if baseCommit != "" {
316+
err = self.c.Git().Rebase.RebaseBranchFromBaseCommit(baseBranch, baseCommit)
317+
} else {
318+
err = self.c.Git().Rebase.RebaseBranch(baseBranch)
319+
}
320+
err = self.CheckMergeOrRebase(err)
321+
if err == nil {
322+
return self.ResetMarkedBaseCommit()
323+
}
324+
return err
325+
})
326+
},
327+
},
282328
}
283329

284330
title := utils.ResolvePlaceholderString(
285331
lo.Ternary(self.c.Modes().MarkedBaseCommit.GetHash() != "",
286332
self.c.Tr.RebasingFromBaseCommitTitle,
287333
self.c.Tr.RebasingTitle),
288334
map[string]string{
289-
"checkedOutBranch": checkedOutBranch,
290-
"ref": ref,
335+
"checkedOutBranch": checkedOutBranchName,
291336
},
292337
)
293338

pkg/i18n/english.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,9 @@ type TranslationSet struct {
289289
RebasingFromBaseCommitTitle string
290290
SimpleRebase string
291291
InteractiveRebase string
292+
RebaseOntoBaseBranch string
292293
InteractiveRebaseTooltip string
294+
RebaseOntoBaseBranchTooltip string
293295
MustSelectTodoCommits string
294296
ConfirmMerge string
295297
FwdNoUpstream string
@@ -1253,11 +1255,13 @@ func EnglishTranslationSet() TranslationSet {
12531255
KeybindingsMenuSectionLocal: "Local",
12541256
KeybindingsMenuSectionGlobal: "Global",
12551257
KeybindingsMenuSectionNavigation: "Navigation",
1256-
RebasingTitle: "Rebase '{{.checkedOutBranch}}' onto '{{.ref}}'",
1257-
RebasingFromBaseCommitTitle: "Rebase '{{.checkedOutBranch}}' from marked base onto '{{.ref}}'",
1258-
SimpleRebase: "Simple rebase",
1259-
InteractiveRebase: "Interactive rebase",
1258+
RebasingTitle: "Rebase '{{.checkedOutBranch}}'",
1259+
RebasingFromBaseCommitTitle: "Rebase '{{.checkedOutBranch}}' from marked base",
1260+
SimpleRebase: "Simple rebase onto '{{.ref}}'",
1261+
InteractiveRebase: "Interactive rebase onto '{{.ref}}'",
1262+
RebaseOntoBaseBranch: "Rebase onto base branch ({{.baseBranch}})",
12601263
InteractiveRebaseTooltip: "Begin an interactive rebase with a break at the start, so you can update the TODO commits before continuing.",
1264+
RebaseOntoBaseBranchTooltip: "Rebase the checked out branch onto its base branch (i.e. the closest main branch).",
12611265
MustSelectTodoCommits: "When rebasing, this action only works on a selection of TODO commits.",
12621266
ConfirmMerge: "Are you sure you want to merge '{{.selectedBranch}}' into '{{.checkedOutBranch}}'?",
12631267
FwdNoUpstream: "Cannot fast-forward a branch with no upstream",
@@ -1443,7 +1447,7 @@ func EnglishTranslationSet() TranslationSet {
14431447
ViewUpstreamResetOptions: "Reset checked-out branch onto {{.upstream}}",
14441448
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.",
14451449
ViewUpstreamRebaseOptions: "Rebase checked-out branch onto {{.upstream}}",
1446-
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.",
1450+
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.",
14471451
UpstreamGenericName: "upstream of selected branch",
14481452
SetUpstreamTitle: "Set upstream branch",
14491453
SetUpstreamMessage: "Are you sure you want to set the upstream branch of '{{.checkedOut}}' to '{{.selected}}'",

pkg/i18n/polish.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,10 @@ func polishTranslationSet() TranslationSet {
274274
KeybindingsMenuSectionLocal: "Lokalne",
275275
KeybindingsMenuSectionGlobal: "Globalne",
276276
KeybindingsMenuSectionNavigation: "Nawigacja",
277-
RebasingTitle: "Rebase '{{.checkedOutBranch}}' na '{{.ref}}'",
278-
RebasingFromBaseCommitTitle: "Rebase '{{.checkedOutBranch}}' od oznaczonego commita bazowego na '{{.ref}}'",
279-
SimpleRebase: "Prosty rebase",
280-
InteractiveRebase: "Interaktywny rebase",
277+
RebasingTitle: "Rebase '{{.checkedOutBranch}}'",
278+
RebasingFromBaseCommitTitle: "Rebase '{{.checkedOutBranch}}' od oznaczonego commita bazowego",
279+
SimpleRebase: "Prosty rebase na '{{.ref}}'",
280+
InteractiveRebase: "Interaktywny rebase na '{{.ref}}'",
281281
InteractiveRebaseTooltip: "Rozpocznij interaktywny rebase z przerwaniem na początku, abyś mógł zaktualizować commity TODO przed kontynuacją.",
282282
MustSelectTodoCommits: "Podczas rebase ta akcja działa tylko na zaznaczonych commitach TODO.",
283283
ConfirmMerge: "Czy na pewno chcesz scalić '{{.selectedBranch}}' z '{{.checkedOutBranch}}'?",

pkg/i18n/russian.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,9 @@ func RussianTranslationSet() TranslationSet {
226226
ConflictsResolved: "Все конфликты слияния разрешены. Продолжить?",
227227
Continue: "Продолжить",
228228
Keybindings: "Связки клавиш",
229-
RebasingTitle: "Перебазировать '{{.checkedOutBranch}}' на '{{.ref}}'",
230-
SimpleRebase: "Простая перебазировка",
231-
InteractiveRebase: "Интерактивная перебазировка",
229+
RebasingTitle: "Перебазировать '{{.checkedOutBranch}}'",
230+
SimpleRebase: "Простая перебазировка на '{{.ref}}'",
231+
InteractiveRebase: "Интерактивная перебазировка на '{{.ref}}'",
232232
InteractiveRebaseTooltip: "Начать интерактивную перебазировку с перерыва в начале, чтобы можно было обновить TODO коммиты, прежде чем продолжить.",
233233
ConfirmMerge: "Вы уверены, что хотите to merge '{{.selectedBranch}}' into '{{.checkedOutBranch}}'?",
234234
FwdNoUpstream: "Невозможно перемотать ветку без upstream-ветки",

pkg/i18n/traditional_chinese.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,9 @@ func traditionalChineseTranslationSet() TranslationSet {
256256
ConflictsResolved: "所有合併衝突都已解決。是否繼續?",
257257
Continue: "確認",
258258
Keybindings: "鍵盤快捷鍵",
259-
RebasingTitle: "將 '{{.checkedOutBranch}}' 變基至 '{{.ref}}'",
260-
SimpleRebase: "簡單變基",
261-
InteractiveRebase: "互動變基",
259+
RebasingTitle: "將 '{{.checkedOutBranch}}'",
260+
SimpleRebase: "簡單變基 變基至 '{{.ref}}'",
261+
InteractiveRebase: "互動變基 變基至 '{{.ref}}'",
262262
InteractiveRebaseTooltip: "開始一個互動變基,以中斷開始,這樣你可以在繼續之前更新TODO提交",
263263
ConfirmMerge: "是否將 '{{.selectedBranch}}' 合併至 '{{.checkedOutBranch}}' ?",
264264
FwdNoUpstream: "無法快進無上游分支",

pkg/integration/tests/branch/rebase.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ var Rebase = NewIntegrationTest(NewIntegrationTestArgs{
3131
Press(keys.Branches.RebaseBranch)
3232

3333
t.ExpectPopup().Menu().
34-
Title(Equals("Rebase 'first-change-branch' onto 'second-change-branch'")).
34+
Title(Equals("Rebase 'first-change-branch'")).
3535
Select(Contains("Simple rebase")).
3636
Confirm()
3737

pkg/integration/tests/branch/rebase_abort_on_conflict.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ var RebaseAbortOnConflict = NewIntegrationTest(NewIntegrationTestArgs{
3131
Press(keys.Branches.RebaseBranch)
3232

3333
t.ExpectPopup().Menu().
34-
Title(Equals("Rebase 'first-change-branch' onto 'second-change-branch'")).
34+
Title(Equals("Rebase 'first-change-branch'")).
3535
Select(Contains("Simple rebase")).
3636
Confirm()
3737

pkg/integration/tests/branch/rebase_and_drop.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ var RebaseAndDrop = NewIntegrationTest(NewIntegrationTestArgs{
3737
Press(keys.Branches.RebaseBranch)
3838

3939
t.ExpectPopup().Menu().
40-
Title(Equals("Rebase 'first-change-branch' onto 'second-change-branch'")).
40+
Title(Equals("Rebase 'first-change-branch'")).
4141
Select(Contains("Simple rebase")).
4242
Confirm()
4343

pkg/integration/tests/branch/rebase_cancel_on_conflict.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ var RebaseCancelOnConflict = NewIntegrationTest(NewIntegrationTestArgs{
3131
Press(keys.Branches.RebaseBranch)
3232

3333
t.ExpectPopup().Menu().
34-
Title(Equals("Rebase 'first-change-branch' onto 'second-change-branch'")).
34+
Title(Equals("Rebase 'first-change-branch'")).
3535
Select(Contains("Simple rebase")).
3636
Confirm()
3737

0 commit comments

Comments
 (0)