Skip to content

Prevent staging from becoming slower over time#5712

Merged
stefanhaller merged 3 commits into
masterfrom
speed-up-git-status
Jun 19, 2026
Merged

Prevent staging from becoming slower over time#5712
stefanhaller merged 3 commits into
masterfrom
speed-up-git-status

Conversation

@stefanhaller

Copy link
Copy Markdown
Collaborator

In a large repo, when touching (editing) more and more files, staging hunks in lazygit could become slower and slower over time. Specifically, this happened when you edited a lot of files and then discarded their changes again. I have seen cases where staging a hunk began to take seconds; the fix then was to type git status on the command line once, this made it fast again.

The reason was that lazygit was trying too hard to be a good git citizen, and used the GIT_OPTIONAL_LOCKS=0 env var on every git command it made. The consequence was that it never updated the mod date cache in git's index file, which caused git to rehash every file whose mod date doesn't match what it recorded in the index, on every refresh. Typing git status updates that cache, which is why this was a workaround.

Fix this by using the GIT_OPTIONAL_LOCKS=0 flag only for refreshes that are running unattended in the background, i.e. the periodic autoRefresh and the newly external change detection. For those it is important because it avoids "cannot lock index" errors for commands that the user might issue at the same time. All other refreshes are user initiated and no longer use the flag, which is in line with what git status does, so this keeps performance from deteriorating over time.

stefanhaller and others added 3 commits June 19, 2026 18:14
This can be used to add a git argument that goes before the git
subcommand.
We set GIT_OPTIONAL_LOCKS=0 for every git command we run. That env var
only affects `git status`: it tells git not to take the optional lock it
would otherwise use to write the index back after refreshing the cached
stat information. The intent was to avoid contending for index.lock with
git commands the user runs in a terminal.

The downside is that our `git status` never persists the refreshed
stat-cache. So whenever the working tree's cached stat info goes stale
(e.g. editing files and discarding the changes, or a checkout), every
subsequent status re-hashes the affected files to confirm they're clean,
and stays slow until something else writes the index (such as the user
running `git status` in a terminal).

Fix this by only suppressing optional locks for refreshes that run
unattended in the background; foreground refreshes triggered by a user
action now run a plain `git status` that writes the refreshed index back,
just like the command line does. Background refreshes keep passing
--no-optional-locks so they still can't cause lock contention.

RefreshOptions gains a Background flag that the background routines set,
threaded down to the status command.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The wrapper existed to add a git-specific env var to every command. Now
that that's gone, its New/NewShell/Quote methods just delegated to the
inner builder. The only remaining git-specific behavior — the command
runner — is attached in the constructor via CloneWithNewRunner, which
already returns a complete builder, so we can return that directly and
drop the wrapper struct.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@stefanhaller stefanhaller enabled auto-merge June 19, 2026 16:27
@stefanhaller stefanhaller merged commit 0b78438 into master Jun 19, 2026
14 checks passed
@stefanhaller stefanhaller deleted the speed-up-git-status branch June 19, 2026 16:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant