summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Haller <stefan@haller-berlin.de>2024-01-29 10:05:40 +0100
committerGitHub <noreply@github.com>2024-01-29 10:05:40 +0100
commit9d840088ace91bce355d0542a8a189f9e77e07ec (patch)
tree012317915bba9ee915508e5727ef25e2fbd62060
parent24e79a057c6ef39d5153a65cacedcdb419d9c7fd (diff)
parentb133318b40f9863e9952dddd8acfeffa6dc95b71 (diff)
Add command to squash all fixups in the current branch (#3274)
Add command to squash all fixups in the current branch. To do that, change the "Apply fixup commits" command to show a menu with the two choices "in current branch" and "above the selected commit"; we make "in current branch" the default, as it's the more useful one most of the time, even though it is a breaking change for those who are used to "shift-S enter" meaning "squash above selected". Fixes #3263.
-rw-r--r--docs/keybindings/Keybindings_en.md2
-rw-r--r--docs/keybindings/Keybindings_ja.md2
-rw-r--r--pkg/gui/controllers/local_commits_controller.go79
-rw-r--r--pkg/i18n/chinese.go1
-rw-r--r--pkg/i18n/dutch.go1
-rw-r--r--pkg/i18n/english.go14
-rw-r--r--pkg/i18n/japanese.go1
-rw-r--r--pkg/i18n/korean.go1
-rw-r--r--pkg/i18n/polish.go1
-rw-r--r--pkg/i18n/russian.go1
-rw-r--r--pkg/i18n/traditional_chinese.go1
-rw-r--r--pkg/integration/tests/interactive_rebase/squash_fixups_above_first_commit.go4
-rw-r--r--pkg/integration/tests/interactive_rebase/squash_fixups_in_current_branch.go55
-rw-r--r--pkg/integration/tests/test_list.go1
14 files changed, 133 insertions, 31 deletions
diff --git a/docs/keybindings/Keybindings_en.md b/docs/keybindings/Keybindings_en.md
index 693c56359..550b3640f 100644
--- a/docs/keybindings/Keybindings_en.md
+++ b/docs/keybindings/Keybindings_en.md
@@ -89,7 +89,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | Mark the selected commit to be picked (when mid-rebase). This means that the commit will be retained upon continuing the rebase. |
| `` F `` | Create fixup commit | Create 'fixup!' commit for the selected commit. Later on, you can press `S` on this same commit to apply all above fixup commits. |
-| `` S `` | Apply fixup commits | Squash all 'fixup!' commits above selected commit (autosquash). |
+| `` S `` | Apply fixup commits | Squash all 'fixup!' commits, either above the selected commit, or all in current branch (autosquash). |
| `` <c-j> `` | Move commit down one | |
| `` <c-k> `` | Move commit up one | |
| `` V `` | Paste (cherry-pick) | |
diff --git a/docs/keybindings/Keybindings_ja.md b/docs/keybindings/Keybindings_ja.md
index beb9ee3f4..b19f42fe7 100644
--- a/docs/keybindings/Keybindings_ja.md
+++ b/docs/keybindings/Keybindings_ja.md
@@ -106,7 +106,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
If you would instead like to start an interactive rebase from the selected commit, press `e`. |
| `` p `` | Pick | Mark the selected commit to be picked (when mid-rebase). This means that the commit will be retained upon continuing the rebase. |
| `` F `` | Create fixup commit | このコミットに対するfixupコミットを作成 |
-| `` S `` | Apply fixup commits | Squash all 'fixup!' commits above selected commit (autosquash). |
+| `` S `` | Apply fixup commits | Squash all 'fixup!' commits, either above the selected commit, or all in current branch (autosquash). |
| `` <c-j> `` | コミットを1つ下に移動 | |
| `` <c-k> `` | コミットを1つ上に移動 | |
| `` V `` | コミットを貼り付け (cherry-pick) | |
diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go
index 3dc1bae7f..3fadbe9e9 100644
--- a/pkg/gui/controllers/local_commits_controller.go
+++ b/pkg/gui/controllers/local_commits_controller.go
@@ -167,13 +167,13 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
},
{
Key: opts.GetKey(opts.Config.Commits.SquashAboveCommits),
- Handler: self.withItem(self.squashAllAboveFixupCommits),
+ Handler: self.squashFixupCommits,
GetDisabledReason: self.require(
self.notMidRebase(self.c.Tr.AlreadyRebasing),
- self.singleItemSelected(),
),
Description: self.c.Tr.SquashAboveCommits,
Tooltip: self.c.Tr.SquashAboveCommitsTooltip,
+ OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Commits.MoveDownCommit),
@@ -816,25 +816,62 @@ func (self *LocalCommitsController) createFixupCommit(commit *models.Commit) err
})
}
-func (self *LocalCommitsController) squashAllAboveFixupCommits(commit *models.Commit) error {
- prompt := utils.ResolvePlaceholderString(
- self.c.Tr.SureSquashAboveCommits,
- map[string]string{"commit": commit.Sha},
- )
-
- return self.c.Confirm(types.ConfirmOpts{
- Title: self.c.Tr.SquashAboveCommits,
- Prompt: prompt,
- HandleConfirm: func() error {
- return self.c.WithWaitingStatus(self.c.Tr.SquashingStatus, func(gocui.Task) error {
- self.c.LogAction(self.c.Tr.Actions.SquashAllAboveFixupCommits)
- err := self.c.Git().Rebase.SquashAllAboveFixupCommits(commit)
- return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
- })
+func (self *LocalCommitsController) squashFixupCommits() error {
+ return self.c.Menu(types.CreateMenuOptions{
+ Title: self.c.Tr.SquashAboveCommits,
+ Items: []*types.MenuItem{
+ {
+ Label: self.c.Tr.SquashCommitsInCurrentBranch,
+ OnPress: self.squashAllFixupsInCurrentBranch,
+ DisabledReason: self.canFindCommitForSquashFixupsInCurrentBranch(),
+ Key: 'b',
+ Tooltip: self.c.Tr.SquashCommitsInCurrentBranchTooltip,
+ },
+ {
+ Label: self.c.Tr.SquashCommitsAboveSelectedCommit,
+ OnPress: self.withItem(self.squashAllFixupsAboveSelectedCommit),
+ DisabledReason: self.singleItemSelected()(),
+ Key: 'a',
+ Tooltip: self.c.Tr.SquashCommitsAboveSelectedTooltip,
+ },
},
})
}
+func (self *LocalCommitsController) squashAllFixupsAboveSelectedCommit(commit *models.Commit) error {
+ return self.c.WithWaitingStatus(self.c.Tr.SquashingStatus, func(gocui.Task) error {
+ self.c.LogAction(self.c.Tr.Actions.SquashAllAboveFixupCommits)
+ err := self.c.Git().Rebase.SquashAllAboveFixupCommits(commit)
+ return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
+ })
+}
+
+func (self *LocalCommitsController) squashAllFixupsInCurrentBranch() error {
+ commit, err := self.findCommitForSquashFixupsInCurrentBranch()
+ if err != nil {
+ return self.c.Error(err)
+ }
+
+ return self.c.WithWaitingStatus(self.c.Tr.SquashingStatus, func(gocui.Task) error {
+ self.c.LogAction(self.c.Tr.Actions.SquashAllAboveFixupCommits)
+ err := self.c.Git().Rebase.SquashAllAboveFixupCommits(commit)
+ return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
+ })
+}
+
+func (self *LocalCommitsController) findCommitForSquashFixupsInCurrentBranch() (*models.Commit, error) {
+ commits := self.c.Model().Commits
+ _, index, ok := lo.FindIndexOf(commits, func(c *models.Commit) bool {
+ return c.IsMerge() || c.Status == models.StatusMerged
+ })
+
+ if !ok || index == 0 {
+ return nil, errors.New(self.c.Tr.CannotSquashCommitsInCurrentBranch)
+ }
+
+ return commits[index-1], nil
+}
+
func (self *LocalCommitsController) createTag(commit *models.Commit) error {
return self.c.Helpers().Tags.OpenCreateTagPrompt(commit.Sha, func() {})
}
@@ -1019,6 +1056,14 @@ func (self *LocalCommitsController) canFindCommitForQuickStart() *types.Disabled
return nil
}
+func (self *LocalCommitsController) canFindCommitForSquashFixupsInCurrentBranch() *types.DisabledReason {
+ if _, err := self.findCommitForSquashFixupsInCurrentBranch(); err != nil {
+ return &types.DisabledReason{Text: err.Error()}
+ }
+
+ return nil
+}
+
func (self *LocalCommitsController) canSquashOrFixup(_selectedCommits []*models.Commit, startIdx int, endIdx int) *types.DisabledReason {
if endIdx >= len(self.c.Model().Commits)-1 {
return &types.DisabledReason{Text: self.c.Tr.CannotSquashOrFixupFirstCommit}
diff --git a/pkg/i18n/chinese.go b/pkg/i18n/chinese.go
index 6f4440633..90d53995d 100644
--- a/pkg/i18n/chinese.go
+++ b/pkg/i18n/chinese.go
@@ -254,7 +254,6 @@ func chineseTranslationSet() TranslationSet {
ViewResetOptions: `查看重置选项`,
CreateFixupCommit: `为此提交创建修正`,
SquashAboveCommitsTooltip: `压缩在所选提交之上的所有“fixup!”提交(自动压缩)`,
- SureSquashAboveCommits: `您确定要压缩在 {{.commit}} 之上的所有“fixup!”提交吗?`,
CreateFixupCommitTooltip: `创建修正提交`,
SureCreateFixupCommit: `您确定要对 {{.commit}} 创建修正提交吗?`,
ExecuteCustomCommand: "执行自定义命令",
diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go
index 5f7aa2978..7e8a89707 100644
--- a/pkg/i18n/dutch.go
+++ b/pkg/i18n/dutch.go
@@ -217,7 +217,6 @@ func dutchTranslationSet() TranslationSet {
HardReset: "Harde reset",
CreateFixupCommit: `Creëer fixup commit voor deze commit`,
SquashAboveCommitsTooltip: `Squash bovenstaande commits`,
- SureSquashAboveCommits: `Weet je zeker dat je alles wil squash/fixup! voor de bovenstaand commits {{.commit}}?`,
CreateFixupCommitTooltip: `Creëer fixup commit`,
SureCreateFixupCommit: `Weet je zeker dat je een fixup wil maken! commit voor commit {{.commit}}?`,
ExecuteCustomCommand: "Voer aangepaste commando uit",
diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go
index 7d4c60c26..5ff58d085 100644
--- a/pkg/i18n/english.go
+++ b/pkg/i18n/english.go
@@ -386,8 +386,12 @@ type TranslationSet struct {
CreateFixupCommitDescription string
CreateFixupCommitTooltip string
SquashAboveCommitsTooltip string
+ SquashCommitsAboveSelectedTooltip string
+ SquashCommitsInCurrentBranchTooltip string
SquashAboveCommits string
- SureSquashAboveCommits string
+ SquashCommitsInCurrentBranch string
+ SquashCommitsAboveSelectedCommit string
+ CannotSquashCommitsInCurrentBranch string
SureCreateFixupCommit string
ExecuteCustomCommand string
ExecuteCustomCommandTooltip string
@@ -1322,8 +1326,12 @@ func EnglishTranslationSet() TranslationSet {
CreateFixupCommitDescription: `Create fixup commit`,
CreateFixupCommitTooltip: "Create 'fixup!' commit for the selected commit. Later on, you can press `{{.squashAbove}}` on this same commit to apply all above fixup commits.",
SquashAboveCommits: "Apply fixup commits",
- SquashAboveCommitsTooltip: `Squash all 'fixup!' commits above selected commit (autosquash).`,
- SureSquashAboveCommits: `Are you sure you want to squash all fixup! commits above {{.commit}}?`,
+ SquashAboveCommitsTooltip: `Squash all 'fixup!' commits, either above the selected commit, or all in current branch (autosquash).`,
+ SquashCommitsAboveSelectedTooltip: `Squash all 'fixup!' commits above the selected commit (autosquash).`,
+ SquashCommitsInCurrentBranchTooltip: `Squash all 'fixup!' commits in the current branch (autosquash).`,
+ SquashCommitsInCurrentBranch: "In current branch",
+ SquashCommitsAboveSelectedCommit: "Above the selected commit",
+ CannotSquashCommitsInCurrentBranch: "Cannot squash commits in current branch: the HEAD commit is a merge commit or is present on the main branch.",
CreateFixupCommit: `Create fixup commit`,
SureCreateFixupCommit: `Are you sure you want to create a fixup! commit for commit {{.commit}}?`,
ExecuteCustomCommand: "Execute custom command",
diff --git a/pkg/i18n/japanese.go b/pkg/i18n/japanese.go
index 19f1a3ca9..f0947f650 100644
--- a/pkg/i18n/japanese.go
+++ b/pkg/i18n/japanese.go
@@ -263,7 +263,6 @@ func japaneseTranslationSet() TranslationSet {
CreateFixupCommitTooltip: `このコミットに対するfixupコミットを作成`,
// LcSquashAboveCommits: `squash all 'fixup!' commits above selected commit (autosquash)`,
// SquashAboveCommits: `Squash all 'fixup!' commits above selected commit (autosquash)`,
- SureSquashAboveCommits: `{{.commit}}に対するすべての fixup! コミットをsquashします。よろしいですか?`,
CreateFixupCommit: `Fixupコミットを作成`,
SureCreateFixupCommit: `{{.commit}} に対する fixup! コミットを作成します。よろしいですか?`,
ExecuteCustomCommand: "カスタムコマンドを実行",
diff --git a/pkg/i18n/korean.go b/pkg/i18n/korean.go
index 26bb0542e..fdfb96c0a 100644
--- a/pkg/i18n/korean.go
+++ b/pkg/i18n/korean.go
@@ -258,7 +258,6 @@ func koreanTranslationSet() TranslationSet {
ViewResetOptions: `View reset options`,
CreateFixupCommitTooltip: `Create fixup commit for this commit`,
SquashAboveCommitsTooltip: `Squash all 'fixup!' commits above selected commit (autosquash)`,
- SureSquashAboveCommits: `Are you sure you want to squash all fixup! commits above {{.commit}}?`,
CreateFixupCommit: `Create fixup commit`,
SureCreateFixupCommit: `Are you sure you want to create a fixup! commit for commit {{.commit}}?`,
ExecuteCustomCommand: "Execute custom command",
diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go
index 2366ff174..fdde08a9c 100644
--- a/pkg/i18n/polish.go
+++ b/pkg/i18n/polish.go
@@ -180,7 +180,6 @@ func polishTranslationSet() TranslationSet {
ViewResetOptions: "Wyświetl opcje resetu",
CreateFixupCommitTooltip: "Utwórz commit naprawczy dla tego commita",
SquashAboveCommitsTooltip: `Spłaszcz wszystkie commity naprawcze powyżej zaznaczonych commitów (autosquash)`,
- SureSquashAboveCommits: `Na pewno chcesz spłaszczyć wszystkie commity naprawcze powyżej {{.commit}}?`,
CreateFixupCommit: `Utwóż commit naprawczy`,
SureCreateFixupCommit: `Na pewno utworzyć commit naprawczy dla commita {{.commit}}?`,
ExecuteCustomCommand: "Wykonaj własną komendę",
diff --git a/pkg/i18n/russian.go b/pkg/i18n/russian.go
index b1ac91dbf..af3fdd6bd 100644
--- a/pkg/i18n/russian.go
+++ b/pkg/i18n/russian.go
@@ -314,7 +314,6 @@ func RussianTranslationSet() TranslationSet {
ViewResetOptions: `Просмотреть параметры сброса`,
CreateFixupCommitTooltip: `Создать fixup коммит для этого коммита`,
SquashAboveCommitsTooltip: `Объединить все 'fixup!' коммиты выше в выбранный коммит (автосохранение)`,
- SureSquashAboveCommits: `Вы уверены, что хотите объединить все fixup! коммиты выше {{.commit}}?`,
CreateFixupCommit: `Создать fixup коммит`,
SureCreateFixupCommit: `Вы уверены, что хотите создать fixup! коммит для коммита {{.commit}}?`,
ExecuteCustomCommand: "Выполнить пользовательскую команду",
diff --git a/pkg/i18n/traditional_chinese.go b/pkg/i18n/traditional_chinese.go
index 76be7b6ef..714c259af 100644
--- a/pkg/i18n/traditional_chinese.go
+++ b/pkg/i18n/traditional_chinese.go
@@ -341,7 +341,6 @@ func traditionalChineseTranslationSet() TranslationSet {
ViewResetOptions: "檢視重設選項",
CreateFixupCommitTooltip: "為此提交建立修復提交",
SquashAboveCommitsTooltip: "壓縮上方所有的“fixup!”提交 (自動壓縮)",
- SureSquashAboveCommits: "你確定要壓縮{{.commit}}上方所有的fixup!提交嗎?",
CreateFixupCommit: "建立修復提交",
SureCreateFixupCommit: "你確定要為提交{{.commit}}建立fixup!提交嗎?",
ExecuteCustomCommand: "執行自訂命令",
diff --git a/pkg/integration/tests/interactive_rebase/squash_fixups_above_first_commit.go b/pkg/integration/tests/interactive_rebase/squash_fixups_above_first_commit.go
index 94324db77..9445dcf58 100644
--- a/pkg/integration/tests/interactive_rebase/squash_fixups_above_first_commit.go
+++ b/pkg/integration/tests/interactive_rebase/squash_fixups_above_first_commit.go
@@ -33,9 +33,9 @@ var SquashFixupsAboveFirstCommit = NewIntegrationTest(NewIntegrationTestArgs{
NavigateToLine(Contains("commit 01").DoesNotContain("fixup!")).
Press(keys.Commits.SquashAboveCommits).
Tap(func() {
- t.ExpectPopup().Confirmation().
+ t.ExpectPopup().Menu().
Title(Equals("Apply fixup commits")).
- Content(Contains("Are you sure you want to squash all fixup! commits above")).
+ Select(Contains("Above the selected commit")).
Confirm()
}).
Lines(
diff --git a/pkg/integration/tests/interactive_rebase/squash_fixups_in_current_branch.go b/pkg/integration/tests/interactive_rebase/squash_fixups_in_current_branch.go
new file mode 100644
index 000000000..636810533
--- /dev/null
+++ b/pkg/integration/tests/interactive_rebase/squash_fixups_in_current_branch.go
@@ -0,0 +1,55 @@
+package interactive_rebase
+
+import (
+ "github.com/jesseduffield/lazygit/pkg/config"
+ . "github.com/jesseduffield/lazygit/pkg/integration/components"
+)
+
+var SquashFixupsInCurrentBranch = NewIntegrationTest(NewIntegrationTestArgs{
+ Description: "Squashes all fixups in the current branch.",
+ ExtraCmdArgs: []string{},
+ Skip: false,
+ SetupConfig: func(config *config.AppConfig) {},
+ SetupRepo: func(shell *Shell) {
+ shell.
+ CreateFileAndAdd("file1", "file1").
+ Commit("master commit").
+ NewBranch("branch").
+ // Test the pathological case that the first commit of a branch is a
+ // fixup for the last master commit below it. We _don't_ want this to
+ // be squashed.
+ UpdateFileAndAdd("file1", "changed file1").
+ Commit("fixup! master commit").
+ CreateNCommits(2).
+ CreateFileAndAdd("fixup-file", "fixup content").
+ Commit("fixup! commit 01")
+ },
+ Run: func(t *TestDriver, keys config.KeybindingConfig) {
+ t.Views().Commits().
+ Focus().
+ Lines(
+ Contains("fixup! commit 01"),
+ Contains("commit 02"),
+ Contains("commit 01"),
+ Contains("fixup! master commit"),
+ Contains("master commit"),
+ ).
+ Press(keys.Commits.SquashAboveCommits).
+ Tap(func() {
+ t.ExpectPopup().Menu().
+ Title(Equals("Apply fixup commits")).
+ Select(Contains("In current branch")).
+ Confirm()
+ }).
+ Lines(
+ Contains("commit 02"),
+ Contains("commit 01"),
+ Contains("fixup! master commit"),
+ Contains("master commit"),
+ ).
+ NavigateToLine(Contains("commit 01"))
+
+ t.Views().Main().
+ Content(Contains("fixup content"))
+ },
+})
diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go
index 9c1f8b5fe..cfadde3b1 100644
--- a/pkg/integration/tests/test_list.go
+++ b/pkg/integration/tests/test_list.go
@@ -183,6 +183,7 @@ var tests = []*components.IntegrationTest{
interactive_rebase.SquashDownFirstCommit,
interactive_rebase.SquashDownSecondCommit,
interactive_rebase.SquashFixupsAboveFirstCommit,
+ interactive_rebase.SquashFixupsInCurrentBranch,
interactive_rebase.SwapInRebaseWithConflict,
interactive_rebase.SwapInRebaseWithConflictAndEdit,
interactive_rebase.SwapWithConflict,