Avoid auto-forwarding main branches checked out in other worktrees#5621
Merged
stefanhaller merged 6 commits intoMay 21, 2026
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 checkoutin 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
WORKTREESin 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 extragit worktree listplus per-worktree rev-parse on every fetch tick.A few small things picked up along the way landed as separate commits first:
loadWorktreeswhengit worktree listfails — the fallback was being overwritten by the nil return value on the next line.PULL_REQUESTSto the manual fetch refresh scope to match the background fetch; looks like an oversight from when PR support was added.BranchesHelper.PostFetchRefreshso the two fetch paths can't drift again.Fixes #5020.