Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pkg/commands/git_commands/rebase.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,11 @@ func (self *RebaseCommands) InteractiveRebase(commits []*models.Commit, startIdx

baseHashOrRoot := getBaseHashOrRoot(commits, baseIndex)

changes := lo.Map(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) daemon.ChangeTodoAction {
changes := lo.FilterMap(commits[startIdx:endIdx+1], func(commit *models.Commit, _ int) (daemon.ChangeTodoAction, bool) {
return daemon.ChangeTodoAction{
Hash: commit.Hash,
NewAction: action,
}
}, !commit.IsMerge()
})

self.os.LogCommand(logTodoChanges(changes), false)
Expand Down
66 changes: 48 additions & 18 deletions pkg/gui/controllers/local_commits_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/context/traits"
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
"github.com/jesseduffield/lazygit/pkg/gui/style"
Expand Down Expand Up @@ -115,7 +116,7 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
},
{
Key: opts.GetKey(editCommitKey),
Handler: self.withItems(self.edit),
Handler: self.withItemsRange(self.edit),
GetDisabledReason: self.require(
self.itemRangeSelected(self.midRebaseCommandEnabled),
),
Expand Down Expand Up @@ -510,11 +511,25 @@ func (self *LocalCommitsController) drop(selectedCommits []*models.Commit, start
return nil
}

func (self *LocalCommitsController) edit(selectedCommits []*models.Commit) error {
func (self *LocalCommitsController) edit(selectedCommits []*models.Commit, startIdx int, endIdx int) error {
if self.isRebasing() {
return self.updateTodos(todo.Edit, selectedCommits)
}

commits := self.c.Model().Commits
if !commits[endIdx].IsMerge() {
selectionRangeAndMode := self.getSelectionRangeAndMode()
err := self.c.Git().Rebase.InteractiveRebase(commits, startIdx, endIdx, todo.Edit)
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions(
err,
types.RefreshOptions{
Mode: types.BLOCK_UI, Then: func() error {
self.restoreSelectionRangeAndMode(selectionRangeAndMode)
return nil
},
})
}

return self.startInteractiveRebaseWithEdit(selectedCommits)
}

Expand All @@ -532,10 +547,7 @@ func (self *LocalCommitsController) startInteractiveRebaseWithEdit(
) error {
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.EditCommit)
selectedIdx, rangeStartIdx, rangeSelectMode := self.context().GetSelectionRangeAndMode()
commits := self.c.Model().Commits
selectedHash := commits[selectedIdx].Hash
rangeStartHash := commits[rangeStartIdx].Hash
selectionRangeAndMode := self.getSelectionRangeAndMode()
err := self.c.Git().Rebase.EditRebase(commitsToEdit[len(commitsToEdit)-1].Hash)
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions(
err,
Expand All @@ -554,23 +566,41 @@ func (self *LocalCommitsController) startInteractiveRebaseWithEdit(
}
}

// We need to select the same commit range again because after starting a rebase,
// new lines can be added for update-ref commands in the TODO file, due to
// stacked branches. So the selected commits may be in different positions in the list.
_, newSelectedIdx, ok1 := lo.FindIndexOf(self.c.Model().Commits, func(c *models.Commit) bool {
return c.Hash == selectedHash
})
_, newRangeStartIdx, ok2 := lo.FindIndexOf(self.c.Model().Commits, func(c *models.Commit) bool {
return c.Hash == rangeStartHash
})
if ok1 && ok2 {
self.context().SetSelectionRangeAndMode(newSelectedIdx, newRangeStartIdx, rangeSelectMode)
}
self.restoreSelectionRangeAndMode(selectionRangeAndMode)
return nil
}})
})
}

type SelectionRangeAndMode struct {
selectedHash string
rangeStartHash string
mode traits.RangeSelectMode
}

func (self *LocalCommitsController) getSelectionRangeAndMode() SelectionRangeAndMode {
selectedIdx, rangeStartIdx, rangeSelectMode := self.context().GetSelectionRangeAndMode()
commits := self.c.Model().Commits
selectedHash := commits[selectedIdx].Hash
rangeStartHash := commits[rangeStartIdx].Hash
return SelectionRangeAndMode{selectedHash, rangeStartHash, rangeSelectMode}
}

func (self *LocalCommitsController) restoreSelectionRangeAndMode(selectionRangeAndMode SelectionRangeAndMode) {
// We need to select the same commit range again because after starting a rebase,
// new lines can be added for update-ref commands in the TODO file, due to
// stacked branches. So the selected commits may be in different positions in the list.
_, newSelectedIdx, ok1 := lo.FindIndexOf(self.c.Model().Commits, func(c *models.Commit) bool {
return c.Hash == selectionRangeAndMode.selectedHash
})
_, newRangeStartIdx, ok2 := lo.FindIndexOf(self.c.Model().Commits, func(c *models.Commit) bool {
return c.Hash == selectionRangeAndMode.rangeStartHash
})
if ok1 && ok2 {
self.context().SetSelectionRangeAndMode(newSelectedIdx, newRangeStartIdx, selectionRangeAndMode.mode)
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if we can't find the original start/end, we should exit the range select mode (assuming it's actually possible to end up in that situation).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's possible to end up in that situation, I was just being extra defensive. But I wouldn't feel good about adding logic that I don't know how to write a test for.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries

}

func (self *LocalCommitsController) findCommitForQuickStartInteractiveRebase() (*models.Commit, error) {
commit, index, ok := lo.FindIndexOf(self.c.Model().Commits, func(c *models.Commit) bool {
return c.IsMerge() || c.Status == models.StatusMerged
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ var DropTodoCommitWithUpdateRef = NewIntegrationTest(NewIntegrationTestArgs{
).
NavigateToLine(Contains("commit 02")).
Press(keys.Universal.Edit).
Focus().
Lines(
Contains("pick").Contains("CI commit 07"),
Contains("pick").Contains("CI commit 06"),
Expand Down
55 changes: 55 additions & 0 deletions pkg/integration/tests/interactive_rebase/edit_and_auto_amend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package interactive_rebase

import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)

var EditAndAutoAmend = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Edit a commit, make a change and stage it, then continue the rebase to auto-amend the commit",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.
CreateNCommits(3)
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Commits().
Focus().
Lines(
Contains("commit 03"),
Contains("commit 02"),
Contains("commit 01"),
).
NavigateToLine(Contains("commit 02")).
Press(keys.Universal.Edit).
Lines(
Contains("commit 03"),
MatchesRegexp("YOU ARE HERE.*commit 02").IsSelected(),
Contains("commit 01"),
)

t.Shell().CreateFile("fixup-file", "fixup content")
t.Views().Files().
Focus().
Press(keys.Files.RefreshFiles).
Lines(
Contains("??").Contains("fixup-file").IsSelected(),
).
PressPrimaryAction()

t.Common().ContinueRebase()

t.Views().Commits().
Focus().
Lines(
Contains("commit 03"),
Contains("commit 02").IsSelected(),
Contains("commit 01"),
)

t.Views().Main().
Content(Contains("fixup content"))
},
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package interactive_rebase

import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)

var EditLastCommitOfStackedBranch = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Edit and amend the last commit of a branch in a stack of branches, and ensure that it doesn't break the stack",
ExtraCmdArgs: []string{},
Skip: false,
GitVersion: AtLeast("2.38.0"),
SetupConfig: func(config *config.AppConfig) {
config.GetUserConfig().Git.MainBranches = []string{"master"}
config.GetAppState().GitLogShowGraph = "never"
},
SetupRepo: func(shell *Shell) {
shell.
CreateNCommits(1).
NewBranch("branch1").
CreateNCommitsStartingAt(2, 2).
NewBranch("branch2").
CreateNCommitsStartingAt(2, 4)

shell.SetConfig("rebase.updateRefs", "true")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Commits().
Focus().
Lines(
Contains("CI commit 05").IsSelected(),
Contains("CI commit 04"),
Contains("CI * commit 03"),
Contains("CI commit 02"),
Contains("CI commit 01"),
).
NavigateToLine(Contains("commit 03")).
Press(keys.Universal.Edit).
Lines(
Contains("pick").Contains("CI commit 05"),
Contains("pick").Contains("CI commit 04"),
Contains("update-ref").Contains("branch1"),
Contains("<-- YOU ARE HERE --- * commit 03").IsSelected(),
Contains("CI commit 02"),
Contains("CI commit 01"),
)

t.Shell().CreateFile("fixup-file", "fixup content")
t.Views().Files().
Focus().
Press(keys.Files.RefreshFiles).
Lines(
Contains("??").Contains("fixup-file").IsSelected(),
).
PressPrimaryAction().
Press(keys.Files.AmendLastCommit)
t.ExpectPopup().Confirmation().
Title(Equals("Amend last commit")).
Content(Contains("Are you sure you want to amend last commit?")).
Confirm()

t.Common().ContinueRebase()

t.Views().Commits().
Focus().
Lines(
Contains("CI commit 05"),
Contains("CI commit 04"),
Contains("CI * commit 03"),
Contains("CI commit 02"),
Contains("CI commit 01"),
)
},
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package interactive_rebase

import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
"github.com/jesseduffield/lazygit/pkg/integration/tests/shared"
)

var EditRangeSelectDownToMergeOutsideRebase = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Select a range of commits (the last one being a merge commit) to edit outside of a rebase",
ExtraCmdArgs: []string{},
Skip: false,
GitVersion: AtLeast("2.22.0"), // first version that supports the --rebase-merges option
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shared.CreateMergeCommit(shell)
shell.CreateNCommits(2)
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Commits().
Focus().
TopLines(
Contains("CI ◯ commit 02").IsSelected(),
Contains("CI ◯ commit 01"),
Contains("Merge branch 'second-change-branch' into first-change-branch"),
).
Press(keys.Universal.RangeSelectDown).
Press(keys.Universal.RangeSelectDown).
Press(keys.Universal.Edit).
Lines(
Contains("edit CI commit 02").IsSelected(),
Contains("edit CI commit 01").IsSelected(),
Contains(" CI ⏣─╮ <-- YOU ARE HERE --- Merge branch 'second-change-branch' into first-change-branch").IsSelected(),
Contains(" CI │ ◯ * second-change-branch unrelated change"),
Contains(" CI │ ◯ second change"),
Contains(" CI ◯ │ first change"),
Contains(" CI ◯─╯ * original"),
Contains(" CI ◯ three"),
Contains(" CI ◯ two"),
Contains(" CI ◯ one"),
)
},
})
3 changes: 3 additions & 0 deletions pkg/integration/tests/test_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,11 @@ var tests = []*components.IntegrationTest{
interactive_rebase.DropCommitInCopiedBranchWithUpdateRef,
interactive_rebase.DropTodoCommitWithUpdateRef,
interactive_rebase.DropWithCustomCommentChar,
interactive_rebase.EditAndAutoAmend,
interactive_rebase.EditFirstCommit,
interactive_rebase.EditLastCommitOfStackedBranch,
interactive_rebase.EditNonTodoCommitDuringRebase,
interactive_rebase.EditRangeSelectDownToMergeOutsideRebase,
interactive_rebase.EditRangeSelectOutsideRebase,
interactive_rebase.EditTheConflCommit,
interactive_rebase.FixupFirstCommit,
Expand Down