Skip to content

Avoid auto-forwarding main branches checked out in other worktrees#5621

Merged
stefanhaller merged 6 commits into
masterfrom
fix-autoforwarding-main-branches-wrt-worktrees
May 21, 2026
Merged

Avoid auto-forwarding main branches checked out in other worktrees#5621
stefanhaller merged 6 commits into
masterfrom
fix-autoforwarding-main-branches-wrt-worktrees

Conversation

@stefanhaller

Copy link
Copy Markdown
Collaborator

After fetching, we auto-forward main branches that have fallen behind their upstream, but skip any that are currently checked out in another worktree — otherwise we'd update the ref behind that worktree's back, leaving its working copy showing the inverse of what was just fetched.

The skip check had a hole though: when starting lazygit while no worktrees have a main branch checked out, leaving it running in the background, and then checking out a main branch in one of the other worktrees outside of lazygit (e.g. in a lazygit instance in another terminal, or using git checkout in the shell, or using some other git client or IDE), then lazygit wouldn't notice the change, and the next fetch would auto-forward main even though it is now checked out in a worktree.

The fix is to include WORKTREES in the post-fetch refresh scope when auto-forwarding is enabled. We gate on the config so users with auto-forward disabled don't pay for an extra git worktree list plus per-worktree rev-parse on every fetch tick.

A few small things picked up along the way landed as separate commits first:

  • Preserve the empty-slice fallback in loadWorktrees when git worktree list fails — the fallback was being overwritten by the nil return value on the next line.
  • Add PULL_REQUESTS to the manual fetch refresh scope to match the background fetch; looks like an oversight from when PR support was added.
  • Extract BranchesHelper.PostFetchRefresh so the two fetch paths can't drift again.

Fixes #5020.

stefanhaller and others added 6 commits May 21, 2026 13:57
If `git worktree list` fails, we want the Worktrees model to fall back
to an empty slice so callers iterating over it stay correct. The error
branch was setting it to `[]`, but the line below unconditionally
overwrote it with the nil `worktrees` value from the failed call.

Use an else branch so the empty-slice fallback actually sticks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The background fetch path already includes PULL_REQUESTS in its
post-fetch refresh scope, but the manual fetch from the files view
doesn't. As far as I can tell that's an oversight from when
PULL_REQUESTS was added — there's no reason the two paths should
differ. Align them so both refresh PRs after fetching.

This also sets up the next commit to extract a shared helper for
the post-fetch refresh.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The post-fetch logic was duplicated in `backgroundFetch` and the manual
fetch handler: refresh a fixed set of views, then auto-forward branches
if the fetch succeeded. The two had already drifted on the refresh
scope; folding them into a single helper makes the duplication go
away and prevents it from drifting again.

Pass the fetch error through so we preserve the previous behaviour of
refreshing unconditionally but only auto-forwarding on success.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When the linked worktree's branch is changed externally — by another
shell, by another tool, or by git running outside lazygit — lazygit's
worktrees model goes stale. The next post-fetch auto-forward then
doesn't realise the branch is now checked out elsewhere, and advances
its ref behind the worktree's back. The worktree's HEAD then resolves
to a commit its index/working tree haven't been updated to, and the
user sees that diff as the inverse of what was fetched — files
appearing as pending changes that they didn't make.

The test sets up a linked worktree initially on a side branch, then
externally checks out master in it before pressing fetch. Two
EXPECTED/ACTUAL pairs capture the symptoms: the branches view shows
master as `✓` rather than `↓1`, and switching to the linked worktree
shows master's would-be incoming file as a pending deletion against
HEAD.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AutoForwardBranches relies on the worktree model to skip any branch
that's currently checked out in another worktree (so we don't update
its ref behind the worktree's back). The post-fetch refresh wasn't
including the worktrees scope, so any external change to the worktree
list between lazygit's startup and the fetch — a `git worktree add`,
a `git checkout` in a linked worktree, a branch rename — left the
in-memory model stale and the skip check returned false negatives.

Add WORKTREES to the post-fetch refresh scope when auto-forwarding is
enabled. We gate on the config so users with auto-forward disabled
don't pay for an extra `git worktree list` plus per-worktree rev-parse
on every fetch tick.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@stefanhaller stefanhaller added the bug Something isn't working label May 21, 2026
@stefanhaller stefanhaller merged commit 608c90a into master May 21, 2026
14 checks passed
@stefanhaller stefanhaller deleted the fix-autoforwarding-main-branches-wrt-worktrees branch May 21, 2026 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Weird behavior since 0.56.0: Changes from origin/main appear as staged changed files in my working copy

1 participant