Skip to content

Refresh git state after branch switch so SSH projects update the branch name - #61701

Open
kkoshin wants to merge 2 commits into
zed-industries:mainfrom
kkoshin:fix/ssh-branch-switch-refresh
Open

Refresh git state after branch switch so SSH projects update the branch name#61701
kkoshin wants to merge 2 commits into
zed-industries:mainfrom
kkoshin:fix/ssh-branch-switch-refresh

Conversation

@kkoshin

@kkoshin kkoshin commented Jul 27, 2026

Copy link
Copy Markdown

Summary

In a remote (SSH) project, clicking a branch in the branch picker switches the branch successfully (verified on the host with git branch --show-current / cat .git/HEAD), but the branch name shown in Zed's UI never updates. Local projects are unaffected.

This is the same family of bugs as #55133 / #13176. Two recent PRs (#59876, #61541) hardened the worktree file watcher so .git metadata changes reliably trigger a git-state reload. After those landed, however, a UI-initiated branch switch in an SSH project still leaves the branch indicator / branch picker stale for me — because change_branch itself still relies on the remote host's file watcher catching the .git/HEAD write, and that round-trip is not reliable on the SSH host in question. This change makes the UI-initiated switch not depend on the watcher, mirroring how reset and fetch already work.

Root cause

Repository::change_branch ran backend.change_branch() (local) or sent a GitChangeBranch RPC (remote) and returned, relying entirely on the worktree file watcher noticing the .git/HEAD change to re-scan and propagate the new branch. On the SSH host that round-trip does not reliably fire, so the server never sends a downstream UpdateRepository with the new branch_summary, the client's apply_remote_update never emits HeadChanged, and the UI stays stale until something else touches .git.

This is inconsistent with the sibling mutating operations, both of which explicitly refresh git state after the mutation rather than trusting the watcher:

  • Repository::reset calls schedule_scan(updates_tx, cx) after the job.
  • Repository::fetch calls refresh_branch_list(...) after a successful fetch.

change_branch did neither.

Fix

Mirror reset: make Repository::change_branch call schedule_scan(updates_tx, cx) after the change job.

  • Local repo: schedule_scan(None)compute_snapshot reads the new branch, emits HeadChanged locally.
  • SSH server: schedule_scan(Some(tx))compute_snapshot reads the new branch, sends DownstreamUpdate::UpdateRepository over the downstream channel → the client's apply_remote_update sees the new branch_summary and emits HeadChanged.
  • SSH client: its own git store is GitStoreState::Remote, so no local scan runs; the server does it.

schedule_scan is keyed by GitJobKey::ReloadGitState, so any scan additionally triggered by the watcher is deduped — this change is purely additive and never causes a double scan.

Because schedule_scan needs cx: &mut Context<Self> (matching reset's signature), change_branch now takes cx. Updated the branch picker, the server-side handle_change_branch, the agent worktree archive caller, and the collab/remote_server integration tests. Added assertions in test_remote_git_branches that the downstream client repository observes the new branch after a remote switch.

Because compute_snapshot recomputes the full snapshot — branch, HEAD commit, file statuses, and diff stats — the git panel's file list and per-file diff stats refresh together with the branch name, all via the same reliable path.

Caveats

Related

Release Notes:

  • Fixed the branch name in the UI not updating after switching branches in a remote (SSH) project, even though the switch succeeded on the host.

…ch name

Repository::change_branch relied entirely on the worktree file watcher
picking up the .git/HEAD change to re-scan and propagate the new branch.
That watcher round-trip is unreliable on remote (SSH) hosts: the switch
runs on the server, but the downstream UpdateRepository carrying the new
branch_summary never reaches the client, so the UI keeps showing the old
branch even though the checkout succeeded (verified in the terminal).
Local mode worked only because the local watcher reliably observes
.git/HEAD.

Mirror reset and fetch, which explicitly refresh after mutating git
state instead of trusting the watcher: call schedule_scan(updates_tx, cx)
after the change job. On a local repo this re-reads the branch and emits
HeadChanged; on the SSH server it sends DownstreamUpdate::UpdateRepository
to the client, whose apply_remote_update then emits HeadChanged.
schedule_scan is keyed by ReloadGitState, so any watcher-triggered scan is
deduped.

This required threading cx into change_branch; updated the branch picker,
the server-side handle_change_branch, the agent worktree archive caller,
and the collab/remote_server integration tests. Added assertions in
test_remote_git_branches that the downstream client repository observes
the new branch after a remote switch.
@cla-bot cla-bot Bot added the cla-signed The user has signed the Contributor License Agreement label Jul 27, 2026
@zed-community-bot zed-community-bot Bot added the first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions label Jul 27, 2026
@kkoshin

kkoshin commented Jul 27, 2026

Copy link
Copy Markdown
Author

@cla-bot check

@cla-bot

cla-bot Bot commented Jul 27, 2026

Copy link
Copy Markdown

The cla-bot has been summoned, and re-checked this pull request!

@MrSubidubi MrSubidubi added area:integrations/git Git integration feedback platform:remote Remote development, SSH and zed-remote-server labels Jul 27, 2026
@kkoshin

kkoshin commented Aug 7, 2026

Copy link
Copy Markdown
Author

@MrSubidubi
The 5 workflow runs for this PR are sitting in action_required, waiting for a maintainer to approve them (first-time contributor). Could one of you approve the runs so CI can execute? Happy to fix any failures that come up.

Thanks!

@cole-miller

cole-miller commented Aug 16, 2026

Copy link
Copy Markdown
Member

Thanks for opening a PR. I'd definitely love to understand why our watcher code (or UpdateRepository) is not doing its job here; could you try reproducing this while having some additional logging enabled in your settings.json, so we can see why the git state is not being refreshed in your case?

// in settings.json

"log": {
  "worktree": "trace"
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:integrations/git Git integration feedback cla-signed The user has signed the Contributor License Agreement first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions platform:remote Remote development, SSH and zed-remote-server

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Remote SSH project: branch indicator and branch picker stay stale after branch switch/create until restart

4 participants