From cbc3da507bafe7f2bf2ad4e8cc2ee5ab89eab5a5 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Fri, 14 Aug 2026 21:03:30 +0200 Subject: [PATCH] Disable staging all files when there are none to stage Besides the misleading error about submodules, the command crashes when it runs before the first files refresh has come in: the file tree doesn't exist yet at that point, and staging all of a tree that isn't there dereferences a nil root node. That is easy to hit in a big repo, where `git status` takes a moment while the panel sits there empty. Co-Authored-By: Claude Opus 5 (1M context) --- pkg/gui/controllers/files_controller.go | 20 +++++++++++++++---- .../file/stage_all_without_changed_files.go | 3 --- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/pkg/gui/controllers/files_controller.go b/pkg/gui/controllers/files_controller.go index 41f9197fe..be3348897 100644 --- a/pkg/gui/controllers/files_controller.go +++ b/pkg/gui/controllers/files_controller.go @@ -130,10 +130,11 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types OpensMenu: true, }, { - Keys: opts.GetKeys(opts.Config.Files.ToggleStagedAll), - Handler: self.toggleStagedAll, - Description: self.c.Tr.ToggleStagedAll, - Tooltip: self.c.Tr.ToggleStagedAllTooltip, + Keys: opts.GetKeys(opts.Config.Files.ToggleStagedAll), + Handler: self.toggleStagedAll, + GetDisabledReason: self.require(self.anyFilesDisplayed), + Description: self.c.Tr.ToggleStagedAll, + Tooltip: self.c.Tr.ToggleStagedAllTooltip, }, { Keys: opts.GetKeys(opts.Config.Universal.GoInto), @@ -916,6 +917,17 @@ func (self *FilesController) openSubmoduleConflictMenu(file *models.File) error }) } +// The stage-all command acts on the file tree as it is displayed, so there has +// to be something in it. This is also the case before the first files refresh +// has come in, when there is no tree at all yet. +func (self *FilesController) anyFilesDisplayed() *types.DisabledReason { + if self.context().FileTreeViewModel.Len() == 0 { + return &types.DisabledReason{Text: self.c.Tr.NoChangedFiles} + } + + return nil +} + func (self *FilesController) toggleStagedAll() error { if err := self.toggleStagedAllWithLock(); err != nil { return err diff --git a/pkg/integration/tests/file/stage_all_without_changed_files.go b/pkg/integration/tests/file/stage_all_without_changed_files.go index db9eecce3..bae54dffe 100644 --- a/pkg/integration/tests/file/stage_all_without_changed_files.go +++ b/pkg/integration/tests/file/stage_all_without_changed_files.go @@ -19,10 +19,7 @@ var StageAllWithoutChangedFiles = NewIntegrationTest(NewIntegrationTestArgs{ IsEmpty(). Press(keys.Files.ToggleStagedAll). Tap(func() { - /* EXPECTED: t.ExpectToast(Contains("No changed files")) - ACTUAL: */ - t.ExpectPopup().Alert().Title(Equals("Error")).Content(Contains("Nothing to stage")).Confirm() }) }, })