Disable squash merging while there are staged changes

Git refuses to merge a branch while there are staged changes, unless
the merge is a fast-forward. A fast-forward keeps the staged changes as
they are. A squash merge that git can fast-forward adds the changes of
the branch to them, though, and "Squash merge and commit" then commits
them all.

Disable both squash merge items in the merge menu while there are
staged changes, so that squash merges behave like other merges. We will
rely on this when we let the user abort a squash merge. Aborting resets
the staged changes, so they must all come from the merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Stefan Haller
2026-11-08 15:36:41 +01:00
co-authored by Claude Opus 5.5
parent a7fcae2ca1
commit acb54f73d1
3 changed files with 18 additions and 24 deletions
@@ -611,15 +611,24 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e
}
}
// Git refuses to merge while there are staged changes, but not if it can
// fast-forward the squash merge. It would add the changes of the branch to
// the staged ones then, and we couldn't tell them apart anymore.
var squashMergeDisabledReason *types.DisabledReason
if AnyStagedFiles(self.c.Model().Files) {
squashMergeDisabledReason = &types.DisabledReason{Text: self.c.Tr.CantSquashMergeWithStagedChanges}
}
return self.c.Menu(types.CreateMenuOptions{
Title: self.c.Tr.Merge,
Items: []*types.MenuItem{
firstRegularMergeItem,
secondRegularMergeItem,
{
Label: self.c.Tr.SquashMergeUncommitted,
OnPress: self.SquashMergeUncommitted(refName),
Keys: menuKey('s'),
Label: self.c.Tr.SquashMergeUncommitted,
OnPress: self.SquashMergeUncommitted(refName),
DisabledReason: squashMergeDisabledReason,
Keys: menuKey('s'),
Tooltip: utils.ResolvePlaceholderString(
self.c.Tr.SquashMergeUncommittedTooltip,
map[string]string{
@@ -628,9 +637,10 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e
),
},
{
Label: self.c.Tr.SquashMergeCommitted,
OnPress: self.SquashMergeCommitted(refName, checkedOutBranchName),
Keys: menuKey('S'),
Label: self.c.Tr.SquashMergeCommitted,
OnPress: self.SquashMergeCommitted(refName, checkedOutBranchName),
DisabledReason: squashMergeDisabledReason,
Keys: menuKey('S'),
Tooltip: utils.ResolvePlaceholderString(
self.c.Tr.SquashMergeCommittedTooltip,
map[string]string{
+2
View File
@@ -304,6 +304,7 @@ type TranslationSet struct {
SquashMergeUncommittedTooltip string
SquashMergeCommitted string
SquashMergeCommittedTooltip string
CantSquashMergeWithStagedChanges string
ConfirmQuit string
SwitchRepo string
AllBranchesLogGraph string
@@ -1533,6 +1534,7 @@ func EnglishTranslationSet() *TranslationSet {
SquashMergeUncommittedTooltip: "Squash merge '{{.selectedBranch}}' into the working tree.",
SquashMergeCommitted: "Squash merge and commit",
SquashMergeCommittedTooltip: "Squash merge '{{.selectedBranch}}' into '{{.checkedOutBranch}}' as a single commit.",
CantSquashMergeWithStagedChanges: "Cannot squash merge while there are staged changes",
ConfirmQuit: `Are you sure you want to quit?`,
SwitchRepo: `Switch to a recent repo`,
AllBranchesLogGraph: `Show/cycle all branch logs`,
@@ -34,7 +34,6 @@ var SquashMergeWithStagedChanges = NewIntegrationTest(NewIntegrationTestArgs{
Select(Contains("Squash merge and commit")).
Confirm()
/* EXPECTED:
t.ExpectToast(Equals("Disabled: Cannot squash merge while there are staged changes"))
t.ExpectPopup().Menu().
@@ -43,22 +42,5 @@ var SquashMergeWithStagedChanges = NewIntegrationTest(NewIntegrationTestArgs{
Confirm()
t.ExpectToast(Equals("Disabled: Cannot squash merge while there are staged changes"))
ACTUAL: */
t.Views().Commits().
Focus().
Lines(
Contains("Squash merge feature-branch into original-branch"),
Contains("one"),
).
NavigateToLine(Contains("Squash merge feature-branch into original-branch")).
PressEnter()
t.Views().CommitFiles().
IsFocused().
Lines(
Equals("▼ /"),
Contains("feature-file"),
Contains("staged-file"),
)
},
})