From f8b7bab1abbd872b583553ab46d23ef009b105a9 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 5 Aug 2026 09:14:02 +0200 Subject: [PATCH] Decide the commit graph from the loaded list, not the filtering mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Whether a graph can be drawn was read from the filtering mode, while the graph itself is drawn over the commit list in the model. Those two only agree once the list has been reloaded for the new mode, and a filtering mode change reloads the list in the background, so in between we can be asked to draw a graph over a list the graph makes no sense for. That is not just cosmetic. Commits in a filtered list are almost never each other's parents, so no pipe ever terminates: the pipe set grows by one per row and every continuing pipe rescans it, which is cubic in the length of the list. Escaping out of filtering mode with a filtered list of 13000 commits — as you get once the 300 commit limit has been lifted, which happens for good as soon as the selection passes COMMIT_THRESHOLD — wedges the UI thread for around twenty minutes. Record whether the list was loaded with a filter, right where the list itself is stored, and decide from that. The graph now also stays up while the pre-change list is still on display, rather than vanishing a moment before the list it belongs to. --- pkg/gui/context/local_commits_context.go | 8 +++++++- pkg/gui/controllers/helpers/refresh_helper.go | 1 + pkg/gui/types/common.go | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/gui/context/local_commits_context.go b/pkg/gui/context/local_commits_context.go index 7f34ea7dc..4a99259fd 100644 --- a/pkg/gui/context/local_commits_context.go +++ b/pkg/gui/context/local_commits_context.go @@ -324,7 +324,13 @@ func (self *LocalCommitsViewModel) GetCommits() []*models.Commit { } func shouldShowGraph(c *ContextCommon) bool { - if c.Modes().Filtering.Active() { + // Whether we can draw a graph is a property of the commit list we have + // loaded, not of the filtering mode: turning filtering on or off only + // reaches the screen when the reloaded list does, and until then the graph + // has to keep matching the list that is still on display. Drawing one for a + // filtered list is also ruinously slow, because none of the commits in it + // are connected to each other, so no pipe ever terminates. + if c.Model().CommitsWereFilteredAtLastRefresh { return false } diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go index 3db9ab9dc..d0327216c 100644 --- a/pkg/gui/controllers/helpers/refresh_helper.go +++ b/pkg/gui/controllers/helpers/refresh_helper.go @@ -817,6 +817,7 @@ func (self *RefreshHelper) refreshCommitsWithLimit(captured capturedCommitState, self.c.Model().BisectInfo = bisectInfo self.c.Model().Commits = commits + self.c.Model().CommitsWereFilteredAtLastRefresh = captured.filterPath != "" || captured.filterAuthor != "" self.RefreshAuthors(commits) self.c.Model().WorkingTreeStateAtLastCommitRefresh = workingTreeState if checkedOutRef != nil { diff --git a/pkg/gui/types/common.go b/pkg/gui/types/common.go index 7091d46f7..2b5fb0ac7 100644 --- a/pkg/gui/types/common.go +++ b/pkg/gui/types/common.go @@ -349,6 +349,7 @@ type Model struct { BisectInfo *git_commands.BisectInfo WorkingTreeStateAtLastCommitRefresh models.WorkingTreeState + CommitsWereFilteredAtLastRefresh bool RemoteBranches []*models.RemoteBranch Tags []*models.Tag