Commit Graph
8121 Commits
Author SHA1 Message Date
Stefan HallerandGitHub 1868167a7a Auto-select the conflicted commit when stopping in rebase (#5937)
When a rebase (or multi-commit cherry-pick or revert) stops with a
conflict, it is often useful to look at the diff of the "<-- CONFLICT"
commit to double-check that the conflict resolution matches the diff of
the original commit. To make that easier, select that commit
automatically.
2026-08-15 15:29:31 +02:00
Stefan Haller 1c17ee1a92 Auto-select the conflicted commit when stopping in rebase
When a rebase (or multi-commit cherry-pick or revert) stops with a
conflict, it is often useful to look at the diff of the "<-- CONFLICT"
commit to double-check that the conflict resolution matches the diff of
the original commit. To make that easier, select that commit
automatically.
2026-08-15 15:24:36 +02:00
Stefan Haller 6417da5319 Add selection assertions to tests involving conflicts
This doesn't change anything, we just pin down the selection behavior
around conflicts; we are going to change that behavior, and these tests
will make it obvious how when they change in the next commit.
2026-08-15 15:24:15 +02:00
Stefan HallerandGitHub aa38daff98 Keep the last conflict file selected after resolution (#5936)
When there is a single conflicting file left to be resolved, lazygit
dismisses the conflicted-files-only filter when the file no longer has
conflict markers. However, the selection moved to the top, which is
annoying because very often it is useful to look at that file's
resulting diff once more to confirm that conflicts were resolved
correctly, and finding it again can be cumbersome when there are many
changed files. So keep it selected.

Of course, this only helps for the last (or only) conflicted files; when
there are multiple, a resolved file disappears from the panel until all
are resolved, which makes it hard to double-check the resulting diffs.
Doing it afterwards is not easy because you'd have to remember which
files were conflicting. This needs a different solution, but for the
special case of only a single conflicting file this is already a big
improvement.
2026-08-15 15:24:01 +02:00
Stefan Haller 39513d244d Keep the last conflict file selected after resolution
When there is a single conflicting file left to be resolved, lazygit
dismisses the conflicted-files-only filter when the file no longer has
conflict markers. However, the selection moved to the top, which is
annoying because very often it is useful to look at that file's
resulting diff once more to confirm that conflicts were resolved
correctly, and finding it again can be cumbersome when there are many
changed files. So keep it selected.

Of course, this only helps for the last (or only) conflicted files; when
there are multiple, a resolved file disappears from the panel until all
are resolved, which makes it hard to double-check the resulting diffs.
Doing it afterwards is not easy because you'd have to remember which
files were conflicting. This needs a different solution, but for the
special case of only a single conflicting file this is already a big
improvement.
2026-08-15 15:03:29 +02:00
Stefan Haller 4e2a1cd5b0 Add a function SetStatusFilterPreservingSelection 2026-08-15 14:58:27 +02:00
Stefan Haller b2b9519bcc Extract a private helper function preserveSelection
The operation around which the selection should be preserved is passed
in so that it can be reused for different purposes.
2026-08-15 14:58:10 +02:00
Stefan HallerandGitHub a1f1a4ee8c Draw the UI in a more inactive look when the window is not focused (#5935)
When using lazygit in a multi-tab terminal it is useful to see if the
lazygit tab is currently active; ghostty does a very good job at dimming
down the inactive tabs, but VS Code's builtin terminal does not, so
indicate this on our side by removing the green highlight from panel
frames and tab titles, and showing the selection as inactive like we do
for a side panel when the main view is focused.
2026-08-15 12:39:51 +02:00
Stefan Haller a5a2bd0699 Draw the UI in a more inactive look when the window is not focused
When using lazygit in a multi-tab terminal it is useful to see if the
lazygit tab is currently active; ghostty does a very good job at dimming
down the inactive tabs, but VS Code's builtin terminal does not, so
indicate this on our side by removing the green highlight from panel
frames and tab titles, and showing the selection as inactive like we do
for a side panel when the main view is focused.
2026-08-15 12:17:40 +02:00
Stefan HallerandGitHub ae1007612b Improve startup time (#5934)
This PR has two separate improvements for the startup time, in
particular for the time until the Files panel shows the modified files:

- avoid walking the `.git/workspaces` dir recursively, looking for the
gitdir files of linked worktrees. This code was not used to populate the
worktrees panel, but only for the decision in the Files panel whether to
show an entry with the worktree icon; it was doing unnecessary work,
because walking the `.git/workspaces` dir recursively is pointless (git
stores the worktree gitdir files only at the top level, so a flat read
of that directory would have been enough), and can take significant time
in large repos, especially when they have many submodules. Instead of
fixing that code, remove it entirely and rearrange the refresh code so
that we can use the regular worktree model for this Files panel
decision.
- at startup we were doing two full refreshes at the same time: the
regular one that we always do after loading a repo for the first time,
and then also a focus-in refresh. I didn't realize that a terminal will
send us a focus-in event right at the moment we request these events.
Nothing bad happens from doing those two refreshes at the same time, but
it slows things down a bit when we have two "git status" calls running
concurrently.

Both of these together reduce the time it takes for the Files panel to
show its files at startup (in my regular work repo with three worktrees
and ~30 submodules) from 860ms to 440ms, so almost a factor of two. If
you want to measure this in your own repo, here's a small throwaway
patch that you can use for that:

```diff
diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go
index 40ce86eae..fbb482f39 100644
--- a/pkg/gui/controllers/helpers/refresh_helper.go
+++ b/pkg/gui/controllers/helpers/refresh_helper.go
@@ -26,6 +26,11 @@ import (
 	"github.com/sasha-s/go-deadlock"
 )
 
+var (
+	applicationStartTime     = time.Now()
+	applicationStartTimeOnce sync.Once
+)
+
 type RefreshHelper struct {
 	c                    *HelperCommon
 	refsHelper           *RefsHelper
@@ -1213,6 +1218,10 @@ func (self *RefreshHelper) refreshFilesAndSubmodules(captured capturedFilesState
 	self.refreshView(self.c.Contexts().Submodules, env)
 	self.refreshView(self.c.Contexts().Files, env)
 
+	applicationStartTimeOnce.Do(func() {
+		self.c.Log.Infof("Time until first files refresh: %s", time.Since(applicationStartTime))
+	})
+
 	return nil
 }
 
```

To use it, run `./lazygit -l | grep "first files refresh"` in one
terminal, and `lazygit -d` in the one that you want to test. I'm curious
about your before/after measurements, feel free to post them below in
the comments.
2026-08-15 12:02:33 +02:00
Stefan HallerandClaude Opus 5 312a5f2cc1 Only react to focus reports that change whether we're focused
A terminal that supports focus reporting answers with the state it is
already in when we turn reporting on, so at startup we were told that we
had gained focus that we never lost, and refreshed everything a second
time on top of the refresh that loading the repo had just started. The
two ran at once, each with its own `git status`, which made both of them
slower than the one refresh needed to be.

Keep track of what the reports say, then, and pass on only the ones that
change it. Assuming that we start out focused costs us nothing when we
don't: that same first report says so, so a lazygit started in a window
that isn't in front knows it from the start.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 11:26:58 +02:00
Stefan HallerandClaude Opus 5 80ad2db71a Recognize worktrees among the files from the worktrees model
Finding out which of the files are worktrees of ours had its own answer
to where this repo's worktrees are, walking the directory that git keeps
them in. The worktrees panel asks git itself, and that is the better
answer: it is the one git gives for the same question elsewhere in the
app, and it doesn't need to know where git records what.

The model that panel fills is all the files need, so mark them from it.
That takes the work out of the file loader, whose other two callers were
paying for it without wanting it, and it costs no git call at all: both
models are written on the UI thread, so whichever of the two refreshes
lands second marks the files against the other's fresh data.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 11:26:58 +02:00
Stefan Haller a1f5db6bce Add test for how we show a worktree that is inside the main work tree
We show this with a worktree icon (which is only shown when nerd fonts
are used, so turn these on), but also we strip the trailing `/` that
"git status" reports, so that it shows as a file rather than a directory
with a bogus file in it.

The reason for adding the test is that we are going to touch the logic
that determines whether an item in the Files panel is a linked worktree,
and this guards against regressing.
2026-08-15 11:26:58 +02:00
Stefan HallerandClaude Opus 5 62caf427ac Refresh the worktrees in their own scope again
The worktrees were loaded and written by the branches refresh whenever
both were in scope, because the branches view shows worktrees against
branches: refreshing them separately rendered that view twice, once
with worktrees that were still stale.

Ordering the two is enough for that, and it leaves each scope owning
its own model again. The worktrees refresh now runs first and queues
its model write before it reports being done, so a branches refresh
that waits for it queues its own write behind that one, and renders
once with both. The worktrees scope only renders the branches view
itself when nobody else is going to.

As a side effect the two loads now run concurrently, where the branches
refresh used to load the worktrees after its own branches.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 11:18:23 +02:00
Stefan HallerandClaude Opus 5 cfe7961d54 Give commits and branches their own scope checks
Everything in performRefresh is meant to read as "if this scope was
asked for, refresh it", with the scopes that always change together
expanded into each other up front so that each check can name a single
one. The commits and the branches were the exception: one condition
asking for either of them refreshed both, so what that block does only
followed from reading it together with the expansion at the top of the
function. The rebase commits hung off the same condition as an else,
even though it is the commits refresh they are an alternative to.

Expand those two into each other like the other pairs, and give each of
them a check of its own. They now capture their inputs separately,
which is what every other scope has always done.

The reflog stays with the branches rather than getting a check of its
own, because sorting the branches by recency needs it loaded first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 11:18:23 +02:00
Stefan HallerandGitHub 1f1fd99357 Two small improvements to the debug log (#5933)
- log time stamps with nano-second granularity (on macOS we effectively
get only microseconds, but that's still more than we need); `lazygit -l`
prints them with millisecond resolution, which can be useful for
investigating performance
- add a blank line at startup to make it much easier to see where a new
run starts
2026-08-15 09:53:13 +02:00
Stefan Haller 0140e19eb2 Separate the runs in the debug log with a blank line
This makes it much easier to find where a new run starts; previously you
had to search for `git --version` for that.
2026-08-15 09:48:11 +02:00
Stefan Haller d298a7ce4b Use nano-second time stamp granularity in the debug log
It's overkill for most purposes, but I'd say it doesn't hurt to have the
extra resolution available in the raw JSON data for the few cases where
it's useful. Have "lazygit -l" print them with milli-second resolution;
that seems to be a good middle ground.
2026-08-15 09:47:32 +02:00
Stefan HallerandGitHub 1248ac4457 Disable staging all files when there are none to stage (#5932)
Pressing `a` in the files panel to stage all files would show a
confusing error popup about something with submodules, which made no
sense at all. And worse, when pressing `a` very quickly after startup
(before the initial refresh had a chance to populate the files panel) it
would crash with a nil pointer panic.

Fix both by showing an error toast that there are no files to stage.

Fixes #5929.
2026-08-15 09:47:16 +02:00
Stefan HallerandClaude Opus 5 cbc3da507b 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) <noreply@anthropic.com>
2026-08-14 22:52:45 +02:00
Stefan HallerandClaude Opus 5 0c04ee5c61 Add a test for staging all files when there are none
The stage-all command acts on the whole file tree, and nothing stops it
from doing that when the tree is empty. It ends up in the branch that
explains why a submodule couldn't be staged, which has nothing to do
with what the user did.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 22:52:45 +02:00
Stefan HallerandGitHub c477a2959b Avoid gofumpt'ing other worktrees (#5931)
This is usually not a problem (except for the unnecessarily wasted time)
because all worktrees should normally be correctly formatted; but it is
a problem when we bump the formatter version, and some worktrees are
already on the new version and others still on the old.
2026-08-14 22:51:59 +02:00
Stefan Haller 1a3cf798f9 Avoid gofumpt'ing other worktrees
This is usually not a problem (except for the unnecessarily wasted time)
because all worktrees should normally be correctly formatted; but it is
a problem when we bump the formatter version, and some worktrees are
already on the new version and others still on the old.
2026-08-14 22:43:37 +02:00
Stefan HallerandGitHub af04698d9a Scroll the selection into view by default again (#5928)
In v0.58 (see #5134) we changed the refresh behavior to no longer scroll
a selection into view by default if it had been scrolled out of view
using the mouse wheel; the primary reason for that was to avoid a
background fetch or files refresh to yank the selection back into view
while you are looking at something else, which was pretty annoying.
However, that meant we had to fix lots of cases where the selection
didn't become visible after a normal, foreground user action, and add
code to manually scroll it into view again in those cases. This is
error-prone and easy to forget for new features, and to this day we were
still missing some.

So turn it around: by default, the selection is scrolled into view
again, and the few cases where we don't want it (background routines and
the focus-in refresh) opt out.

This does mean that some user actions now scroll into view that didn't
before, and there might be cases where this is unwanted (I can't think
of one, but who knows). If we come across one we can easily fix it by
opting out; this is probably still much better than not scrolling into
view where it's wanted.
2026-08-14 08:31:46 +02:00
Stefan HallerandClaude Opus 5 5c4c7139d7 Remove the scroll calls that are now redundant
Every one of these did by hand what focusing the list now does on its
own: five hand-added scroll requests, and four origin resets that paired
a "select the first item" with a "and show the top of the list".

The scroll that the commits refresh performed when it found the selected
commit at a new index goes too. It is now unconditional for a foreground
refresh, and deliberately absent for a background one: when an agent
commits in another window, we would rather see the new commits arrive
than have the view yank itself back to the commit we had selected.

The one origin reset that stays is the one in ReApplyFilter, which runs
as part of a refresh and so can't rely on the refresh scrolling.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 08:04:13 +02:00
Stefan HallerandClaude Opus 5 aebf495dce Scroll the selection into view by default
Ever since scrolling the selection into view became opt-in, we have been
fixing the same class of regression by hand, five times so far: a
controller moves the selection somewhere new, doesn't say that it wants
the view to follow, and the selection ends up off screen. The decision
needs facts from two places — whether the selection went somewhere new is
known to the list, whether the scroll position is the caller's to manage
is known to the caller — and asking every caller for both is what keeps
going wrong. The callers that get it wrong are usually not even the ones
that moved the selection: they are pass-throughs like postRefreshUpdate,
which can't know what a refresh did to the selection.

So default to scrolling, and let the two callers that maintain the scroll
position themselves say so.

The one case where scrolling is always wrong is a refresh that no user
action is behind: a background poll, or a reload of state on window
focus, after a subprocess, or after a repo switch. Those must leave the
viewport wherever the user last scrolled it to — that is what made the
scrolling opt-in in the first place. Both are already marked in
RefreshOptions, so the refresh can decide it once, centrally, instead of
each caller judging it.

A user action that ends in a foreground refresh does now yank the view
back to the selection if the user had scrolled away from it. That's a
behaviour change, and there may be actions where it turns out to be
unwelcome; those we can fix individually, and it beats the ones that
don't scroll today.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 08:04:13 +02:00
Stefan HallerandClaude Opus 5 29d05e23f9 Add tests for the scroll-into-view regressions we fixed by hand
Since scrolling the selection into view became opt-in, five places have
had to be fixed by hand after the fact, none of them with a test. Cover
them now: making the scrolling automatic has to keep all five working,
and once it does, the hand-added scroll calls can go.

Two of them assert that the selection is visible rather than on an exact
scroll position, because the panel they look at changes height along the
way (filtering mode switches to half screen), or because what matters is
only that the commit we jumped to can be seen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 08:04:13 +02:00
Stefan HallerandClaude Opus 5 a85d6e0349 Add a test for dragging a range selection past the bottom of a panel
This is the other place that manages its own scroll position: while a
drag extends the selection to a line below the viewport, the view stays
put, and the drag autoscroller scrolls it one line at a time for as long
as the pointer stays there. Making the scroll automatic would centre the
selection instead, i.e. jump the view rather than scroll it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 08:04:13 +02:00
Stefan HallerandClaude Opus 5 e9faf0325d Add a test that a background refresh keeps the scroll position
The one behaviour that made scrolling the selection into view opt-in in
the first place — a background refresh must not yank the view back to a
selection the user scrolled away from — has never been covered by a test.
It's about to become the one case that the automatic scrolling has to
suppress, so cover it first.

Getting there needs two things from the test harness: mouse wheel events,
which are the only way to scroll a list panel without moving the
selection, and a way to trigger a background refresh. The periodic
routine that issues it is turned off in tests, and turning it on would
mean waiting for its timer and hoping it fires while we're looking, so
drive the refresh directly instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 08:04:13 +02:00
Stefan HallerandClaude Opus 5 a881fb7ee0 Add a test for paging up and down in a list
We are about to make list panels scroll their selection into view
automatically. Page up and down are one of the few places that manage
the scroll position themselves, keeping the selection at the edge of the
viewport rather than in its middle, and nothing covers that today.

Asserting on it needs an exact scroll position assertion; only
OriginYAtLeast existed so far.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 08:04:13 +02:00
Stefan HallerandGitHub 4e4cd93993 Fix example for args field in rawGit diff renderer docs (#5927) 2026-08-14 07:45:17 +02:00
Stefan Haller ac5e0b4ae9 Fix example for args field in rawGit diff renderer docs 2026-08-14 07:42:25 +02:00
Stefan HallerandGitHub d25315b55a Bump gofumpt to version 0.11.0 (#5925) 2026-08-13 20:51:19 +02:00
Stefan Haller 25c6aeadf0 go get -tool mvdan.cc/gofumpt@v0.11.0 2026-08-13 20:40:11 +02:00
Stefan Haller b1e9ac3969 Remove a few unnecessary parentheses
The new version of gofumpt that we are going to update to in a moment
would complain about these.
2026-08-13 20:40:11 +02:00
Stefan HallerandGitHub fbe2379fa5 Bump JamesIves/github-sponsors-readme-action from 1.6.0 to 1.6.1 (#5917)
Bumps
[JamesIves/github-sponsors-readme-action](https://github.com/jamesives/github-sponsors-readme-action)
from 1.6.0 to 1.6.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/jamesives/github-sponsors-readme-action/releases">JamesIves/github-sponsors-readme-action's
releases</a>.</em></p>
<blockquote>
<h2>v1.6.1</h2>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>Dependencies 🤖</h3>
<ul>
<li>chore(deps): bump the misc group across 1 directory with 3 updates
by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/JamesIves/github-sponsors-readme-action/pull/1034">JamesIves/github-sponsors-readme-action#1034</a></li>
<li>chore(deps-dev): bump <code>@​types/node</code> from 25.2.0 to
25.2.1 in the misc group by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/JamesIves/github-sponsors-readme-action/pull/1036">JamesIves/github-sponsors-readme-action#1036</a></li>
<li>chore(deps): bump actions/setup-node from 6.2.0 to 6.3.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/JamesIves/github-sponsors-readme-action/pull/1041">JamesIves/github-sponsors-readme-action#1041</a></li>
<li>chore(deps): bump actions/upload-artifact from 6.0.0 to 7.0.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/JamesIves/github-sponsors-readme-action/pull/1040">JamesIves/github-sponsors-readme-action#1040</a></li>
<li>chore(deps): bump actions/upload-artifact from 7.0.0 to 7.0.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/JamesIves/github-sponsors-readme-action/pull/1052">JamesIves/github-sponsors-readme-action#1052</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/JamesIves/github-sponsors-readme-action/compare/v1...v1.6.1">https://github.com/JamesIves/github-sponsors-readme-action/compare/v1...v1.6.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/JamesIves/github-sponsors-readme-action/commit/02650b8cd445fc16dfef73195f9c406dce041623"><code>02650b8</code></a>
Merge remote-tracking branch 'origin/dev' into releases/v1</li>
<li><a
href="https://github.com/JamesIves/github-sponsors-readme-action/commit/e958d3e9b7e1e704656add532e547cc86060d553"><code>e958d3e</code></a>
Deploy Production Code for Commit
d0e97b6c881bf5598173fe04a41bffac988b7667 🚀</li>
<li><a
href="https://github.com/JamesIves/github-sponsors-readme-action/commit/c0481077d61a48f92a52ef4a7bcae9f2885fb6c8"><code>c048107</code></a>
Merge remote-tracking branch 'origin/dev' into releases/v1</li>
<li><a
href="https://github.com/JamesIves/github-sponsors-readme-action/commit/8fd9552b1cef39636a3f6d11d8c674154655cfdd"><code>8fd9552</code></a>
fix: use a dedicated RELEASE_PAT for release creation</li>
<li><a
href="https://github.com/JamesIves/github-sponsors-readme-action/commit/d0e97b6c881bf5598173fe04a41bffac988b7667"><code>d0e97b6</code></a>
Merge branch 'dev' of <a
href="https://github.com/JamesIves/github-sponsors-readme-act">https://github.com/JamesIves/github-sponsors-readme-act</a>...</li>
<li><a
href="https://github.com/JamesIves/github-sponsors-readme-action/commit/6eb9fb19bdb29912a94bb93e6e691cc6ad4557c6"><code>6eb9fb1</code></a>
ci: run sponsors README update twice a week instead of daily</li>
<li><a
href="https://github.com/JamesIves/github-sponsors-readme-action/commit/152fad67d2a569947ecc0378b76064af9300e494"><code>152fad6</code></a>
ci: run integration tests weekly instead of daily</li>
<li><a
href="https://github.com/JamesIves/github-sponsors-readme-action/commit/7962d89b080bcdc4f980f5bba33bff803eba46b6"><code>7962d89</code></a>
chore(deps): bump actions/upload-artifact from 7.0.0 to 7.0.1 (<a
href="https://redirect.github.com/jamesives/github-sponsors-readme-action/issues/1052">#1052</a>)</li>
<li><a
href="https://github.com/JamesIves/github-sponsors-readme-action/commit/7b03cded5bf3927fbd58c83ad2cc4f662b23d657"><code>7b03cde</code></a>
fix: remove unsupported semver cooldown keys from the github-actions
ecosystem</li>
<li><a
href="https://github.com/JamesIves/github-sponsors-readme-action/commit/592de7d30b66a1ca22bcd92a1217aacabd2fb496"><code>592de7d</code></a>
security: add explicit permissions blocks to workflows</li>
<li>Additional commits viewable in <a
href="https://github.com/jamesives/github-sponsors-readme-action/compare/2fd9142e765f755780202122261dc85e78459405...02650b8cd445fc16dfef73195f9c406dce041623">compare
view</a></li>
</ul>
</details>
<br />
v0.64.1
2026-08-12 19:45:58 +02:00
dependabot[bot]andGitHub f77c1d37f1 Bump JamesIves/github-sponsors-readme-action from 1.6.0 to 1.6.1
Bumps [JamesIves/github-sponsors-readme-action](https://github.com/jamesives/github-sponsors-readme-action) from 1.6.0 to 1.6.1.
- [Release notes](https://github.com/jamesives/github-sponsors-readme-action/releases)
- [Commits](https://github.com/jamesives/github-sponsors-readme-action/compare/2fd9142e765f755780202122261dc85e78459405...02650b8cd445fc16dfef73195f9c406dce041623)

---
updated-dependencies:
- dependency-name: JamesIves/github-sponsors-readme-action
  dependency-version: 1.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-12 17:43:40 +00:00
Stefan HallerandGitHub 27fcf4b729 Bump github.com/lucasb-eyer/go-colorful from 1.4.0 to 1.4.1 (#5916)
Bumps
[github.com/lucasb-eyer/go-colorful](https://github.com/lucasb-eyer/go-colorful)
from 1.4.0 to 1.4.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/lucasb-eyer/go-colorful/releases">github.com/lucasb-eyer/go-colorful's
releases</a>.</em></p>
<blockquote>
<h2>v1.4.1</h2>
<h2>What's Changed</h2>
<ul>
<li>fix: correct D50ToD65 to the CSS Color 4 matrix that inverts
D65ToD50 by <a
href="https://github.com/gaoflow"><code>@​gaoflow</code></a> in <a
href="https://redirect.github.com/lucasb-eyer/go-colorful/pull/85">lucasb-eyer/go-colorful#85</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/gaoflow"><code>@​gaoflow</code></a> made
their first contribution in <a
href="https://redirect.github.com/lucasb-eyer/go-colorful/pull/85">lucasb-eyer/go-colorful#85</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/lucasb-eyer/go-colorful/compare/v1.4.0...v1.4.1">https://github.com/lucasb-eyer/go-colorful/compare/v1.4.0...v1.4.1</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/lucasb-eyer/go-colorful/blob/master/CHANGELOG.md">github.com/lucasb-eyer/go-colorful's
changelog</a>.</em></p>
<blockquote>
<h2>[1.4.1] - 2026-08-02</h2>
<h3>Fixed</h3>
<ul>
<li>Corrected <code>D50ToD65</code> to use the CSS Color 4 matrix
inverse of <code>D65ToD50</code> (<a
href="https://redirect.github.com/lucasb-eyer/go-colorful/issues/85">#85</a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/lucasb-eyer/go-colorful/commit/315b48282c63bac7b48ba128d0c87b7f827b2285"><code>315b482</code></a>
ready for v1.4.1</li>
<li><a
href="https://github.com/lucasb-eyer/go-colorful/commit/e9123175c008141525fe0eee08a328987b66f650"><code>e912317</code></a>
fix: correct D50ToD65 to the CSS Color 4 matrix that inverts
D65ToD50</li>
<li>See full diff in <a
href="https://github.com/lucasb-eyer/go-colorful/compare/v1.4.0...v1.4.1">compare
view</a></li>
</ul>
</details>
<br />
2026-08-12 19:41:33 +02:00
dependabot[bot]andGitHub 93b8f343bb Bump github.com/lucasb-eyer/go-colorful from 1.4.0 to 1.4.1
Bumps [github.com/lucasb-eyer/go-colorful](https://github.com/lucasb-eyer/go-colorful) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/lucasb-eyer/go-colorful/releases)
- [Changelog](https://github.com/lucasb-eyer/go-colorful/blob/master/CHANGELOG.md)
- [Commits](https://github.com/lucasb-eyer/go-colorful/compare/v1.4.0...v1.4.1)

---
updated-dependencies:
- dependency-name: github.com/lucasb-eyer/go-colorful
  dependency-version: 1.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-12 17:38:40 +00:00
Stefan HallerandGitHub c18a0c6680 Fix pull requests silently disappearing until lazygit is restarted (#5921)
A lazygit instance that has been open for a while sometimes stops
showing pull requests, and keeps not showing them until you quit and
restart it. The cause is that we resolve the GitHub token once per
process and then keep using that stale answer.

We get the token from go-gh, which reads gh's `hosts.yml` on the first
call and caches it for the lifetime of the process. gh rewrites that
file whenever the active account changes, and keeps the active account's
token either in the file or in the system keyring, depending on the
account. Once the file changes under us, our snapshot no longer
describes reality: either we keep sending a token for an account that is
no longer active, or — if the snapshot was taken while a keyring-backed
account was active — we find no token at all, drop the remote, and show
no pull requests. Nothing is reported to the user, so it looks like PR
fetching is simply broken.

Switching accounts with `gh auth switch` is the easiest way to trigger
this, but it isn't limited to multi-account setups: a single account
hits the same thing whenever its token is rotated, or moved between the
keyring and `hosts.yml` by a fresh `gh auth login`.

So ask gh itself for the token on every refresh, with `gh auth token
--hostname <host>`, which resolves it afresh from whichever of the
environment, the keyring, or the config file currently holds it. Not
passing `--secure-storage` (which go-gh does internally) also means it
stops mattering which of the two the active account uses. go-gh's lookup
stays as a fallback for setups without the gh binary, where it still
picks up `GH_TOKEN` and friends; when gh is present it checks those
variables itself.

To reproduce on master, with two gh accounts, one with its token in the
keyring and one in `hosts.yml`: start lazygit while the keyring-backed
account is active, then run `gh auth switch` to the other one. The pull
request information disappears on the next refresh and doesn't come back
until lazygit is restarted. Running `gh auth token --secure-storage
--hostname github.com` by hand at that point prints `no oauth token
found for github.com`.
2026-08-12 19:30:37 +02:00
Stefan HallerandClaude Opus 5 c8bc1928f2 Get the GitHub token from gh instead of resolving it in-process
go-gh reads gh's config file once per process and answers from that
snapshot for the rest of the process's life. gh rewrites the file
whenever the active account changes, and stores the active account's
token either in it or in the system keyring, depending on the account.
A lazygit that has been running for a while therefore consults a
snapshot that no longer describes reality: it either keeps using a
token for an account that is no longer active, or, when the snapshot
was taken while a keyring-backed account was active, finds no token at
all and silently stops showing pull requests until it is restarted.

Asking gh resolves the token afresh on every refresh, from whichever of
the environment, the keyring or the config file currently holds it.
go-gh's lookup stays behind as a fallback for setups without the gh
binary, where it still picks up GH_TOKEN and friends.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 19:13:06 +02:00
Stefan HallerandGitHub 4b22b844e7 Fix hang on quit when confirmOnQuit is true (#5919)
When confirmOnQuit is true, quitting would sometimes hang for three
seconds and then print "cannot kill child process". Concretely, this
happened whenever the Files panel was focused but there were no changed
files (the main view shows "No changed files").

This is a regression in 0.64.0, it worked before.

Fixes #5918.
2026-08-12 19:12:16 +02:00
Stefan HallerandClaude Opus 5 ec577f1afa Give up waiting for the UI thread once the main loop has exited
Quitting with confirmOnQuit set hung for three seconds and printed
"cannot kill child process", but only with a clean working tree. Closing
the confirmation pops the context before running its handler, so the
files panel is re-focused and re-renders the main view, and only then
does the handler return ErrQuit. With no changed files that render is a
string task, whose whole body is one hop to the UI thread — a hop that
is never served, because the handler's ErrQuit has meanwhile brought the
main loop down. The task can't finish, so the ViewBufferManager.Close
that follows waits for it until it times out. (With changed files it's a
command task instead, and every blocking point in one of those selects
on the stop channel, so Close gets through.)

A wait for the UI thread now ends when the loop does. That also covers
the command task's own hops, which are stopped only in between them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 13:03:26 +02:00
Stefan HallerandClaude Opus 5 70427c8ff5 Add a test for waiting on the UI thread after the loop has exited
Nothing dequeues user events once MainLoop has returned, so a worker
blocked in OnUIThreadAndWait is blocked for good. The assertion records
that; the next commit makes the wait give up instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 13:03:26 +02:00
Stefan HallerandClaude Opus 5 2f06724b80 Make RefreshHelper pay attention to the error returned from OnUIThreadAndWait
Right now the function always returns nil, but this will change later in
this branch, so handle errors properly. Without that, the first capture
that assigns env.git would not run, leave env.git nil, and subsequent
code would crash.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 13:03:26 +02:00
Stefan HallerandClaude Opus 5 f9b790a1f9 Let OnUIThreadAndWait's error be about the wait, not about f
Every caller passes an f that unconditionally returns nil, so f's error
return has never carried anything: the value is dead weight, and it
occupies the one channel the wait itself needs to report that it couldn't
run f at all. Drop it, so that the error the wait returns can only ever
mean that.

Work that can fail hands its error back through a captured variable, the
way the background fetch already hands back four values, which keeps the
two outcomes distinguishable at a call site that has both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 11:04:53 +02:00
Stefan HallerandGitHub ddceff6962 Bump mheap/github-action-required-labels from 5.5.2 to 5.6.0 (#5769)
Bumps
[mheap/github-action-required-labels](https://github.com/mheap/github-action-required-labels)
from 5.5.2 to 5.6.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/mheap/github-action-required-labels/releases">mheap/github-action-required-labels's
releases</a>.</em></p>
<blockquote>
<h2>v5.6.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Bump GitHub client to v7 by <a
href="https://github.com/VincentLanglet"><code>@​VincentLanglet</code></a>
in <a
href="https://redirect.github.com/mheap/github-action-required-labels/pull/96">mheap/github-action-required-labels#96</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/VincentLanglet"><code>@​VincentLanglet</code></a>
made their first contribution in <a
href="https://redirect.github.com/mheap/github-action-required-labels/pull/96">mheap/github-action-required-labels#96</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/mheap/github-action-required-labels/compare/v5.5.2...v5.6.0">https://github.com/mheap/github-action-required-labels/compare/v5.5.2...v5.6.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/mheap/github-action-required-labels/commit/23e10fde7e062233401931a0eece796cd9bf3177"><code>23e10fd</code></a>
Automatic compilation</li>
<li><a
href="https://github.com/mheap/github-action-required-labels/commit/6e4081ebcbed471862a3ddcbc8b5f6ce99b4263a"><code>6e4081e</code></a>
Replace nock with undici in tests + bump GH client to v7 (<a
href="https://redirect.github.com/mheap/github-action-required-labels/issues/96">#96</a>)</li>
<li>See full diff in <a
href="https://github.com/mheap/github-action-required-labels/compare/0ac283b4e65c1fb28ce6079dea5546ceca98ccbe...23e10fde7e062233401931a0eece796cd9bf3177">compare
view</a></li>
</ul>
</details>
<br />
2026-08-08 17:59:43 +02:00
dependabot[bot]andGitHub f416a4ba6a Bump mheap/github-action-required-labels from 5.5.2 to 5.6.0
Bumps [mheap/github-action-required-labels](https://github.com/mheap/github-action-required-labels) from 5.5.2 to 5.6.0.
- [Release notes](https://github.com/mheap/github-action-required-labels/releases)
- [Commits](https://github.com/mheap/github-action-required-labels/compare/0ac283b4e65c1fb28ce6079dea5546ceca98ccbe...23e10fde7e062233401931a0eece796cd9bf3177)

---
updated-dependencies:
- dependency-name: mheap/github-action-required-labels
  dependency-version: 5.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-08 15:57:21 +00:00
Stefan HallerandGitHub 2399cac0db Bump github.com/kyokomi/emoji/v2 from 2.2.13 to 2.2.14 (#5813)
Bumps [github.com/kyokomi/emoji/v2](https://github.com/kyokomi/emoji)
from 2.2.13 to 2.2.14.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/kyokomi/emoji/releases">github.com/kyokomi/emoji/v2's
releases</a>.</em></p>
<blockquote>
<h2>v2.2.14</h2>
<h2>What's Changed</h2>
<h3>Emoji data</h3>
<ul>
<li>Regenerated <code>emoji_codemap.go</code> with Unicode Emoji 16.0 /
17.0 additions — 18 new emoji such as <code>🫆</code>,
<code>:orca:</code>, <code>:treasure_chest:</code>,
<code>:ballet_dancer:</code> (no removals)</li>
</ul>
<h3>Behavior change</h3>
<ul>
<li><code>NormalizeShortCode</code> now consistently returns the
lowercase alias when same-length aliases differ only in case. Affects 26
entries (e.g. <code>:Aries:</code> → <code></code>,
<code>:OK_hand:</code> → <code>👌</code>, <code>:ZZZ:</code> →
<code>💤</code>, <code>:T-Rex:</code> → <code>🦖</code>)</li>
</ul>
<h3>Maintenance</h3>
<ul>
<li>go.mod: <code>go 1.14</code> → <code>go 1.21</code> (cmd module:
<code>go 1.12</code> → <code>go 1.25</code>, goquery v1.5.1 →
v1.12.0)</li>
<li>CI: pin Go via <code>go-version-file</code>, golangci-lint-action v6
→ v8, verify cmd module</li>
<li>Added dependabot for github-actions and gomod</li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/kyokomi/emoji/compare/v2.2.13...v2.2.14">https://github.com/kyokomi/emoji/compare/v2.2.13...v2.2.14</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/kyokomi/emoji/commit/a659fe56a640109bc44bfecdd0a9641642971f27"><code>a659fe5</code></a>
Maintenance: update emoji data, modernize Go/CI, add dependabot (<a
href="https://redirect.github.com/kyokomi/emoji/issues/65">#65</a>)</li>
<li><a
href="https://github.com/kyokomi/emoji/commit/eb108489069ff2953e6bb4fe471784e664563644"><code>eb10848</code></a>
Bump GitHub workflow actions (<a
href="https://redirect.github.com/kyokomi/emoji/issues/64">#64</a>)</li>
<li>See full diff in <a
href="https://github.com/kyokomi/emoji/compare/v2.2.13...v2.2.14">compare
view</a></li>
</ul>
</details>
<br />
2026-08-08 17:55:18 +02:00
dependabot[bot]andGitHub d6cf948dca Bump github.com/kyokomi/emoji/v2 from 2.2.13 to 2.2.14
Bumps [github.com/kyokomi/emoji/v2](https://github.com/kyokomi/emoji) from 2.2.13 to 2.2.14.
- [Release notes](https://github.com/kyokomi/emoji/releases)
- [Commits](https://github.com/kyokomi/emoji/compare/v2.2.13...v2.2.14)

---
updated-dependencies:
- dependency-name: github.com/kyokomi/emoji/v2
  dependency-version: 2.2.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-08 15:52:34 +00:00