diff --git a/docs/keybindings/Keybindings_en.md b/docs/keybindings/Keybindings_en.md index 3357d3619..a0e811f66 100644 --- a/docs/keybindings/Keybindings_en.md +++ b/docs/keybindings/Keybindings_en.md @@ -84,6 +84,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ <c-j>: Move commit down one <c-k>: Move commit up one v: Paste commits (cherry-pick) + B: Mark commit as base commit for rebase A: Amend commit with staged changes a: Set/Reset commit author t: Revert commit diff --git a/docs/keybindings/Keybindings_ja.md b/docs/keybindings/Keybindings_ja.md index 1adf2de3a..d34463b81 100644 --- a/docs/keybindings/Keybindings_ja.md +++ b/docs/keybindings/Keybindings_ja.md @@ -103,6 +103,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ <c-j>: コミットを1つ下に移動 <c-k>: コミットを1つ上に移動 v: コミットを貼り付け (cherry-pick) + B: Mark commit as base commit for rebase A: ステージされた変更でamendコミット a: Set/Reset commit author t: コミットをrevert diff --git a/docs/keybindings/Keybindings_ko.md b/docs/keybindings/Keybindings_ko.md index 837043947..5446c24aa 100644 --- a/docs/keybindings/Keybindings_ko.md +++ b/docs/keybindings/Keybindings_ko.md @@ -265,6 +265,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ <c-j>: 커밋을 1개 아래로 이동 <c-k>: 커밋을 1개 위로 이동 v: 커밋을 붙여넣기 (cherry-pick) + B: Mark commit as base commit for rebase A: Amend commit with staged changes a: Set/Reset commit author t: 커밋 되돌리기 diff --git a/docs/keybindings/Keybindings_nl.md b/docs/keybindings/Keybindings_nl.md index 13010a4d1..1593fc56d 100644 --- a/docs/keybindings/Keybindings_nl.md +++ b/docs/keybindings/Keybindings_nl.md @@ -143,6 +143,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ <c-j>: Verplaats commit 1 naar beneden <c-k>: Verplaats commit 1 naar boven v: Plak commits (cherry-pick) + B: Mark commit as base commit for rebase A: Wijzig commit met staged veranderingen a: Set/Reset commit author t: Commit ongedaan maken diff --git a/docs/keybindings/Keybindings_pl.md b/docs/keybindings/Keybindings_pl.md index 9876d26a7..01a7a621c 100644 --- a/docs/keybindings/Keybindings_pl.md +++ b/docs/keybindings/Keybindings_pl.md @@ -69,6 +69,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ <c-j>: Przenieś commit 1 w dół <c-k>: Przenieś commit 1 w górę v: Wklej commity (przebieranie) + B: Mark commit as base commit for rebase A: Popraw commit zmianami z poczekalni a: Set/Reset commit author t: Odwróć commit diff --git a/docs/keybindings/Keybindings_ru.md b/docs/keybindings/Keybindings_ru.md index fa59d9949..6ae627bc9 100644 --- a/docs/keybindings/Keybindings_ru.md +++ b/docs/keybindings/Keybindings_ru.md @@ -151,6 +151,7 @@ _Связки клавиш_ <c-j>: Переместить коммит вниз на один <c-k>: Переместить коммит вверх на один v: Вставить отобранные коммиты (cherry-pick) + B: Mark commit as base commit for rebase A: Править последний коммит с проиндексированными изменениями a: Установить/убрать автора коммита t: Отменить коммит diff --git a/docs/keybindings/Keybindings_zh-CN.md b/docs/keybindings/Keybindings_zh-CN.md index 1fcfe987f..924607639 100644 --- a/docs/keybindings/Keybindings_zh-CN.md +++ b/docs/keybindings/Keybindings_zh-CN.md @@ -147,6 +147,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ <c-j>: 下移提交 <c-k>: 上移提交 v: 粘贴提交(拣选) + B: Mark commit as base commit for rebase A: 用已暂存的更改来修补提交 a: Set/Reset commit author t: 还原提交 diff --git a/docs/keybindings/Keybindings_zh-TW.md b/docs/keybindings/Keybindings_zh-TW.md index 960eee73a..4103a88ec 100644 --- a/docs/keybindings/Keybindings_zh-TW.md +++ b/docs/keybindings/Keybindings_zh-TW.md @@ -191,6 +191,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ <c-j>: 向下移動提交 <c-k>: 向上移動提交 v: 貼上提交 (揀選) + B: Mark commit as base commit for rebase A: 使用已預存的更改修正提交 a: 設置/重設提交作者 t: 還原提交 diff --git a/pkg/commands/git_commands/rebase.go b/pkg/commands/git_commands/rebase.go index 83c3fbe09..d491b6f5e 100644 --- a/pkg/commands/git_commands/rebase.go +++ b/pkg/commands/git_commands/rebase.go @@ -156,6 +156,15 @@ func (self *RebaseCommands) EditRebase(branchRef string) error { }).Run() } +func (self *RebaseCommands) EditRebaseFromBaseCommit(targetBranchName string, baseCommit string) error { + self.os.LogCommand(fmt.Sprintf("Beginning interactive rebase from '%s' onto '%s", baseCommit, targetBranchName), false) + return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{ + baseShaOrRoot: baseCommit, + onto: targetBranchName, + instruction: daemon.NewInsertBreakInstruction(), + }).Run() +} + func logTodoChanges(changes []daemon.ChangeTodoAction) string { changeTodoStr := strings.Join(lo.Map(changes, func(c daemon.ChangeTodoAction, _ int) string { return fmt.Sprintf("%s:%s", c.Sha, c.NewAction) @@ -165,6 +174,7 @@ func logTodoChanges(changes []daemon.ChangeTodoAction) string { type PrepareInteractiveRebaseCommandOpts struct { baseShaOrRoot string + onto string instruction daemon.Instruction overrideEditor bool keepCommitsThatBecomeEmpty bool @@ -183,6 +193,7 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteract ArgIf(opts.keepCommitsThatBecomeEmpty && !self.version.IsOlderThan(2, 26, 0), "--empty=keep"). Arg("--no-autosquash"). ArgIf(!self.version.IsOlderThan(2, 22, 0), "--rebase-merges"). + ArgIf(opts.onto != "", "--onto", opts.onto). Arg(opts.baseShaOrRoot). ToArgv() @@ -306,6 +317,13 @@ func (self *RebaseCommands) RebaseBranch(branchName string) error { return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{baseShaOrRoot: branchName}).Run() } +func (self *RebaseCommands) RebaseBranchFromBaseCommit(targetBranchName string, baseCommit string) error { + return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{ + baseShaOrRoot: baseCommit, + onto: targetBranchName, + }).Run() +} + func (self *RebaseCommands) GenericMergeOrRebaseActionCmdObj(commandType string, command string) oscommands.ICmdObj { cmdArgs := NewGitCmd(commandType).Arg("--" + command).ToArgv() diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index a5121f0c5..0426086fc 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -67,6 +67,8 @@ type ThemeConfig struct { SelectedRangeBgColor []string `yaml:"selectedRangeBgColor"` CherryPickedCommitBgColor []string `yaml:"cherryPickedCommitBgColor"` CherryPickedCommitFgColor []string `yaml:"cherryPickedCommitFgColor"` + MarkedBaseCommitBgColor []string `yaml:"markedBaseCommitBgColor"` + MarkedBaseCommitFgColor []string `yaml:"markedBaseCommitFgColor"` UnstagedChangesColor []string `yaml:"unstagedChangesColor"` DefaultFgColor []string `yaml:"defaultFgColor"` } @@ -267,6 +269,7 @@ type KeybindingCommitsConfig struct { CherryPickCopy string `yaml:"cherryPickCopy"` CherryPickCopyRange string `yaml:"cherryPickCopyRange"` PasteCommits string `yaml:"pasteCommits"` + MarkCommitAsBaseForRebase string `yaml:"markCommitAsBaseForRebase"` CreateTag string `yaml:"tagCommit"` CheckoutCommit string `yaml:"checkoutCommit"` ResetCherryPick string `yaml:"resetCherryPick"` @@ -432,6 +435,8 @@ func GetDefaultConfig() *UserConfig { SelectedRangeBgColor: []string{"blue"}, CherryPickedCommitBgColor: []string{"cyan"}, CherryPickedCommitFgColor: []string{"blue"}, + MarkedBaseCommitBgColor: []string{"yellow"}, + MarkedBaseCommitFgColor: []string{"blue"}, UnstagedChangesColor: []string{"red"}, DefaultFgColor: []string{"default"}, }, @@ -613,6 +618,7 @@ func GetDefaultConfig() *UserConfig { CherryPickCopy: "c", CherryPickCopyRange: "C", PasteCommits: "v", + MarkCommitAsBaseForRebase: "B", CreateTag: "T", CheckoutCommit: "", ResetCherryPick: "", diff --git a/pkg/gui/context/local_commits_context.go b/pkg/gui/context/local_commits_context.go index 417a4f30a..f8f7848f2 100644 --- a/pkg/gui/context/local_commits_context.go +++ b/pkg/gui/context/local_commits_context.go @@ -49,6 +49,7 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext { c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL, c.Modes().CherryPicking.SelectedShaSet(), c.Modes().Diffing.Ref, + c.Modes().MarkedBaseCommit.GetSha(), c.UserConfig.Gui.TimeFormat, c.UserConfig.Gui.ShortTimeFormat, time.Now(), diff --git a/pkg/gui/context/sub_commits_context.go b/pkg/gui/context/sub_commits_context.go index ba2f5e3f6..2643d294b 100644 --- a/pkg/gui/context/sub_commits_context.go +++ b/pkg/gui/context/sub_commits_context.go @@ -65,6 +65,7 @@ func NewSubCommitsContext( c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL, c.Modes().CherryPicking.SelectedShaSet(), c.Modes().Diffing.Ref, + "", c.UserConfig.Gui.TimeFormat, c.UserConfig.Gui.ShortTimeFormat, time.Now(), diff --git a/pkg/gui/controllers/helpers/merge_and_rebase_helper.go b/pkg/gui/controllers/helpers/merge_and_rebase_helper.go index e9140e102..03c3abb4b 100644 --- a/pkg/gui/controllers/helpers/merge_and_rebase_helper.go +++ b/pkg/gui/controllers/helpers/merge_and_rebase_helper.go @@ -224,8 +224,18 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error { Key: 's', OnPress: func() error { self.c.LogAction(self.c.Tr.Actions.RebaseBranch) - err := self.c.Git().Rebase.RebaseBranch(ref) - return self.CheckMergeOrRebase(err) + baseCommit := self.c.Modes().MarkedBaseCommit.GetSha() + var err error + if baseCommit != "" { + err = self.c.Git().Rebase.RebaseBranchFromBaseCommit(ref, baseCommit) + } else { + err = self.c.Git().Rebase.RebaseBranch(ref) + } + err = self.CheckMergeOrRebase(err) + if err == nil { + self.c.Modes().MarkedBaseCommit.Reset() + } + return err }, }, { @@ -234,17 +244,26 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error { Tooltip: self.c.Tr.InteractiveRebaseTooltip, OnPress: func() error { self.c.LogAction(self.c.Tr.Actions.RebaseBranch) - err := self.c.Git().Rebase.EditRebase(ref) + baseCommit := self.c.Modes().MarkedBaseCommit.GetSha() + var err error + if baseCommit != "" { + err = self.c.Git().Rebase.EditRebaseFromBaseCommit(ref, baseCommit) + } else { + err = self.c.Git().Rebase.EditRebase(ref) + } if err = self.CheckMergeOrRebase(err); err != nil { return err } + self.c.Modes().MarkedBaseCommit.Reset() return self.c.PushContext(self.c.Contexts().LocalCommits) }, }, } title := utils.ResolvePlaceholderString( - self.c.Tr.RebasingTitle, + lo.Ternary(self.c.Modes().MarkedBaseCommit.GetSha() != "", + self.c.Tr.RebasingFromBaseCommitTitle, + self.c.Tr.RebasingTitle), map[string]string{ "checkedOutBranch": checkedOutBranch, "ref": ref, @@ -283,3 +302,8 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e }, }) } + +func (self *MergeAndRebaseHelper) ResetMarkedBaseCommit() error { + self.c.Modes().MarkedBaseCommit.Reset() + return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits) +} diff --git a/pkg/gui/controllers/helpers/mode_helper.go b/pkg/gui/controllers/helpers/mode_helper.go index f5f3c738c..b3d9549a4 100644 --- a/pkg/gui/controllers/helpers/mode_helper.go +++ b/pkg/gui/controllers/helpers/mode_helper.go @@ -82,6 +82,16 @@ func (self *ModeHelper) Statuses() []ModeStatus { }, Reset: self.ExitFilterMode, }, + { + IsActive: self.c.Modes().MarkedBaseCommit.Active, + Description: func() string { + return self.withResetButton( + self.c.Tr.MarkedBaseCommitStatus, + style.FgCyan, + ) + }, + Reset: self.mergeAndRebaseHelper.ResetMarkedBaseCommit, + }, { IsActive: self.c.Modes().CherryPicking.Active, Description: func() string { diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index a8df8811c..49f664a29 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -103,6 +103,12 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ Handler: self.paste, Description: self.c.Tr.PasteCommits, }, + { + Key: opts.GetKey(opts.Config.Commits.MarkCommitAsBaseForRebase), + Handler: self.checkSelected(self.markAsBaseCommit), + Description: self.c.Tr.MarkAsBaseCommit, + Tooltip: self.c.Tr.MarkAsBaseCommitTooltip, + }, // overriding these navigation keybindings because we might need to load // more commits on demand { @@ -840,6 +846,16 @@ func (self *LocalCommitsController) paste() error { return self.c.Helpers().CherryPick.Paste() } +func (self *LocalCommitsController) markAsBaseCommit(commit *models.Commit) error { + if commit.Sha == self.c.Modes().MarkedBaseCommit.GetSha() { + // Reset when invoking it again on the marked commit + self.c.Modes().MarkedBaseCommit.SetSha("") + } else { + self.c.Modes().MarkedBaseCommit.SetSha(commit.Sha) + } + return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits) +} + func (self *LocalCommitsController) isHeadCommit() bool { return models.IsHeadCommit(self.c.Model().Commits, self.context().GetSelectedLineIdx()) } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 8db98238c..4da5d5e96 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -24,6 +24,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui/modes/cherrypicking" "github.com/jesseduffield/lazygit/pkg/gui/modes/diffing" "github.com/jesseduffield/lazygit/pkg/gui/modes/filtering" + "github.com/jesseduffield/lazygit/pkg/gui/modes/marked_base_commit" "github.com/jesseduffield/lazygit/pkg/gui/popup" "github.com/jesseduffield/lazygit/pkg/gui/presentation" "github.com/jesseduffield/lazygit/pkg/gui/presentation/authors" @@ -362,9 +363,10 @@ func (gui *Gui) resetState(startArgs appTypes.StartArgs) types.Context { Authors: map[string]*models.Author{}, }, Modes: &types.Modes{ - Filtering: filtering.New(startArgs.FilterPath), - CherryPicking: cherrypicking.New(), - Diffing: diffing.New(), + Filtering: filtering.New(startArgs.FilterPath), + CherryPicking: cherrypicking.New(), + Diffing: diffing.New(), + MarkedBaseCommit: marked_base_commit.New(), }, ScreenMode: initialScreenMode, // TODO: only use contexts from context manager diff --git a/pkg/gui/modes/marked_base_commit/marked_base_commit.go b/pkg/gui/modes/marked_base_commit/marked_base_commit.go new file mode 100644 index 000000000..516dd0f2f --- /dev/null +++ b/pkg/gui/modes/marked_base_commit/marked_base_commit.go @@ -0,0 +1,25 @@ +package marked_base_commit + +type MarkedBaseCommit struct { + sha string // the sha of the commit used as a rebase base commit; empty string when unset +} + +func New() MarkedBaseCommit { + return MarkedBaseCommit{} +} + +func (m *MarkedBaseCommit) Active() bool { + return m.sha != "" +} + +func (m *MarkedBaseCommit) Reset() { + m.sha = "" +} + +func (m *MarkedBaseCommit) SetSha(sha string) { + m.sha = sha +} + +func (m *MarkedBaseCommit) GetSha() string { + return m.sha +} diff --git a/pkg/gui/presentation/commits.go b/pkg/gui/presentation/commits.go index c00564e9d..f5915a7ee 100644 --- a/pkg/gui/presentation/commits.go +++ b/pkg/gui/presentation/commits.go @@ -45,6 +45,7 @@ func GetCommitListDisplayStrings( fullDescription bool, cherryPickedCommitShaSet *set.Set[string], diffName string, + markedBaseCommit string, timeFormat string, shortTimeFormat string, now time.Time, @@ -128,6 +129,7 @@ func GetCommitListDisplayStrings( lines := make([][]string, 0, len(filteredCommits)) var bisectStatus BisectStatus + willBeRebased := markedBaseCommit == "" for i, commit := range filteredCommits { unfilteredIdx := i + startIdx bisectStatus = getBisectStatus(unfilteredIdx, commit.Sha, bisectInfo, bisectBounds) @@ -136,11 +138,17 @@ func GetCommitListDisplayStrings( isYouAreHereCommit = true showYouAreHereLabel = false } + isMarkedBaseCommit := commit.Sha != "" && commit.Sha == markedBaseCommit + if isMarkedBaseCommit { + willBeRebased = true + } lines = append(lines, displayCommit( common, commit, branchHeadsToVisualize, cherryPickedCommitShaSet, + isMarkedBaseCommit, + willBeRebased, diffName, timeFormat, shortTimeFormat, @@ -290,6 +298,8 @@ func displayCommit( commit *models.Commit, branchHeadsToVisualize *set.Set[string], cherryPickedCommitShaSet *set.Set[string], + isMarkedBaseCommit bool, + willBeRebased bool, diffName string, timeFormat string, shortTimeFormat string, @@ -335,6 +345,12 @@ func displayCommit( color := lo.Ternary(commit.Action == models.ActionConflict, style.FgRed, style.FgYellow) youAreHere := color.Sprintf("<-- %s ---", common.Tr.YouAreHere) name = fmt.Sprintf("%s %s", youAreHere, name) + } else if isMarkedBaseCommit { + rebaseFromHere := style.FgYellow.Sprint(common.Tr.MarkedCommitMarker) + name = fmt.Sprintf("%s %s", rebaseFromHere, name) + } else if !willBeRebased { + willBeRebased := style.FgYellow.Sprint("✓") + name = fmt.Sprintf("%s %s", willBeRebased, name) } authorFunc := authors.ShortAuthor diff --git a/pkg/gui/presentation/commits_test.go b/pkg/gui/presentation/commits_test.go index 4bf4c09af..65122961e 100644 --- a/pkg/gui/presentation/commits_test.go +++ b/pkg/gui/presentation/commits_test.go @@ -33,6 +33,7 @@ func TestGetCommitListDisplayStrings(t *testing.T) { hasUpdateRefConfig bool fullDescription bool cherryPickedCommitShaSet *set.Set[string] + markedBaseCommit string diffName string timeFormat string shortTimeFormat string @@ -408,6 +409,7 @@ func TestGetCommitListDisplayStrings(t *testing.T) { s.fullDescription, s.cherryPickedCommitShaSet, s.diffName, + s.markedBaseCommit, s.timeFormat, s.shortTimeFormat, s.now, diff --git a/pkg/gui/types/modes.go b/pkg/gui/types/modes.go index ba135de63..a11ed0081 100644 --- a/pkg/gui/types/modes.go +++ b/pkg/gui/types/modes.go @@ -4,10 +4,12 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui/modes/cherrypicking" "github.com/jesseduffield/lazygit/pkg/gui/modes/diffing" "github.com/jesseduffield/lazygit/pkg/gui/modes/filtering" + "github.com/jesseduffield/lazygit/pkg/gui/modes/marked_base_commit" ) type Modes struct { - Filtering filtering.Filtering - CherryPicking *cherrypicking.CherryPicking - Diffing diffing.Diffing + Filtering filtering.Filtering + CherryPicking *cherrypicking.CherryPicking + Diffing diffing.Diffing + MarkedBaseCommit marked_base_commit.MarkedBaseCommit } diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index ecfa6a2ae..5aa6bd3c9 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -209,6 +209,7 @@ type TranslationSet struct { ConflictsResolved string Continue string RebasingTitle string + RebasingFromBaseCommitTitle string SimpleRebase string InteractiveRebase string InteractiveRebaseTooltip string @@ -578,6 +579,10 @@ type TranslationSet struct { Name string Branch string Path string + MarkedBaseCommitStatus string + MarkAsBaseCommit string + MarkAsBaseCommitTooltip string + MarkedCommitMarker string Actions Actions Bisect Bisect } @@ -947,6 +952,7 @@ func EnglishTranslationSet() TranslationSet { Continue: "Continue", Keybindings: "Keybindings", RebasingTitle: "Rebase '{{.checkedOutBranch}}' onto '{{.ref}}'", + RebasingFromBaseCommitTitle: "Rebase '{{.checkedOutBranch}}' from marked base onto '{{.ref}}'", SimpleRebase: "Simple rebase", InteractiveRebase: "Interactive rebase", InteractiveRebaseTooltip: "Begin an interactive rebase with a break at the start, so you can update the TODO commits before continuing", @@ -1315,6 +1321,10 @@ func EnglishTranslationSet() TranslationSet { Name: "Name", Branch: "Branch", Path: "Path", + MarkedBaseCommitStatus: "Marked a base commit for rebase", + MarkAsBaseCommit: "Mark commit as base commit for rebase", + MarkAsBaseCommitTooltip: "Select a base commit for the next rebase; this will effectively perform a 'git rebase --onto'.", + MarkedCommitMarker: "↑↑↑ Will rebase from here ↑↑↑", Actions: Actions{ // TODO: combine this with the original keybinding descriptions (those are all in lowercase atm) CheckoutCommit: "Checkout commit", diff --git a/pkg/integration/tests/branch/rebase_from_marked_base.go b/pkg/integration/tests/branch/rebase_from_marked_base.go new file mode 100644 index 000000000..fb6ede722 --- /dev/null +++ b/pkg/integration/tests/branch/rebase_from_marked_base.go @@ -0,0 +1,78 @@ +package branch + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var RebaseFromMarkedBase = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Rebase onto another branch from a marked base commit", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell. + NewBranch("base-branch"). + EmptyCommit("one"). + EmptyCommit("two"). + EmptyCommit("three"). + NewBranch("active-branch"). + EmptyCommit("active one"). + EmptyCommit("active two"). + EmptyCommit("active three"). + Checkout("base-branch"). + NewBranch("target-branch"). + EmptyCommit("target one"). + EmptyCommit("target two"). + Checkout("active-branch") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("active three"), + Contains("active two"), + Contains("active one"), + Contains("three"), + Contains("two"), + Contains("one"), + ). + NavigateToLine(Contains("active one")). + Press(keys.Commits.MarkCommitAsBaseForRebase). + Lines( + Contains("active three"), + Contains("active two"), + Contains("↑↑↑ Will rebase from here ↑↑↑ active one"), + Contains("three"), + Contains("two"), + Contains("one"), + ) + + t.Views().Information().Content(Contains("Marked a base commit for rebase")) + + t.Views().Branches(). + Focus(). + Lines( + Contains("active-branch"), + Contains("target-branch"), + Contains("base-branch"), + ). + SelectNextItem(). + Press(keys.Branches.RebaseBranch) + + t.ExpectPopup().Menu(). + Title(Equals("Rebase 'active-branch' from marked base onto 'target-branch'")). + Select(Contains("Simple rebase")). + Confirm() + + t.Views().Commits().Lines( + Contains("active three"), + Contains("active two"), + Contains("target two"), + Contains("target one"), + Contains("three"), + Contains("two"), + Contains("one"), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 9dcb57dee..dfcd6c0ea 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -44,6 +44,7 @@ var tests = []*components.IntegrationTest{ branch.RebaseAndDrop, branch.RebaseCancelOnConflict, branch.RebaseDoesNotAutosquash, + branch.RebaseFromMarkedBase, branch.Reset, branch.ResetUpstream, branch.SetUpstream, diff --git a/pkg/theme/theme.go b/pkg/theme/theme.go index 0a1624029..20832f6d3 100644 --- a/pkg/theme/theme.go +++ b/pkg/theme/theme.go @@ -36,6 +36,9 @@ var ( // CherryPickedCommitColor is the text style when cherry picking a commit CherryPickedCommitTextStyle = style.New() + // MarkedBaseCommitTextStyle is the text style of the marked rebase base commit + MarkedBaseCommitTextStyle = style.New() + OptionsFgColor = style.New() DiffTerminalColor = style.FgMagenta @@ -55,6 +58,10 @@ func UpdateTheme(themeConfig config.ThemeConfig) { cherryPickedCommitFgTextStyle := GetTextStyle(themeConfig.CherryPickedCommitFgColor, false) CherryPickedCommitTextStyle = cherryPickedCommitBgTextStyle.MergeStyle(cherryPickedCommitFgTextStyle) + markedBaseCommitBgTextStyle := GetTextStyle(themeConfig.MarkedBaseCommitBgColor, true) + markedBaseCommitFgTextStyle := GetTextStyle(themeConfig.MarkedBaseCommitFgColor, false) + MarkedBaseCommitTextStyle = markedBaseCommitBgTextStyle.MergeStyle(markedBaseCommitFgTextStyle) + unstagedChangesTextStyle := GetTextStyle(themeConfig.UnstagedChangesColor, false) UnstagedChangesColor = unstagedChangesTextStyle