Repository navigation
Fix sidebar branch refresh after terminal checkout - #2307
austinywang wants to merge 3 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdded periodic (2s) Git-branch polling that captures branch + dirty state as a new private value type and applies branch snapshots to panel/workspace state with in-flight deduplication and triggers metadata refreshes when branch labels change. Changes
Sequence DiagramsequenceDiagram
participant Timer as Timer (2s)
participant TM as TabManager
participant Probe as Git Probe (async)
participant Main as Main Actor
participant Store as Workspace State
Timer->>TM: tick
TM->>TM: select candidate panels\n(filter active/eligible & in-flight dedupe)
TM->>Probe: schedule probe (repo dir → git branch/status)
Probe->>Probe: run git commands
Probe->>Main: return WorkspaceGitBranchSnapshot
Main->>TM: applyTrackedWorkspaceGitBranchSnapshot(snapshot)
TM->>TM: validate workspace/panel & dir match
TM->>Store: update/clear panelGitBranches
alt branch label changed
TM->>TM: scheduleWorkspaceGitMetadataRefreshIfPossible(reason: "branchPollChanged")
TM->>Store: trigger PR/metadata refresh
else no change
TM->>Store: no metadata refresh
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
cmuxTests/TabManagerUnitTests.swift (1)
585-595: Fold workspace-level assertions into the wait predicate to reduce test flake risk.
waitForConditionon Line 586 currently gates only panel-level state, while Line 592-595 immediately assert workspace-level snapshot state. If those propagate one runloop later, this can intermittently fail.Proposed stabilization diff
- XCTAssertTrue( - waitForCondition { - workspace.panelGitBranches[panelId]?.branch == "feature/sidebar-branch-update" - && workspace.panelGitBranches[panelId]?.isDirty == false - && workspace.panelPullRequests[panelId] == nil - } - ) - XCTAssertEqual(workspace.gitBranch?.branch, "feature/sidebar-branch-update") - XCTAssertEqual(workspace.gitBranch?.isDirty, false) - XCTAssertNil(workspace.pullRequest) + XCTAssertTrue( + waitForCondition { + workspace.panelGitBranches[panelId]?.branch == "feature/sidebar-branch-update" + && workspace.panelGitBranches[panelId]?.isDirty == false + && workspace.panelPullRequests[panelId] == nil + && workspace.gitBranch?.branch == "feature/sidebar-branch-update" + && workspace.gitBranch?.isDirty == false + && workspace.pullRequest == nil + } + )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmuxTests/TabManagerUnitTests.swift` around lines 585 - 595, The test currently waits only for panel-level state via waitForCondition { workspace.panelGitBranches[panelId]? ... } but then immediately asserts workspace-level snapshot (workspace.gitBranch and workspace.pullRequest), which can race; update the waitForCondition predicate (the closure passed to waitForCondition) to also assert workspace.gitBranch?.branch == "feature/sidebar-branch-update", workspace.gitBranch?.isDirty == false, and workspace.pullRequest == nil so all relevant assertions are synchronized, then remove the subsequent standalone XCTAssertEqual/XCTAssertNil lines; reference waitForCondition, workspace.panelGitBranches, workspace.gitBranch, workspace.pullRequest, and panelId when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Sources/TabManager.swift`:
- Around line 1738-1751: The git invocations in workspaceGitBranchSnapshot (and
the similar method at lines ~1753-1758) currently call runGitCommand without a
timeout which can leave probes stuck; update these call sites to use a
timeout-aware variant of runGitCommand (or wrap the git Process call with a
timeout) so the branch and status commands return nil/error on timeout, then
handle that result by returning a safe WorkspaceGitBranchSnapshot (branch: nil,
isDirty: false) and ensuring any probe bookkeeping
(workspaceGitBranchProbeInFlightKeys) is cleared when a timeout occurs;
specifically modify workspaceGitBranchSnapshot and the sibling function to call
the timeout-enabled runGitCommand, handle timed-out/nil outputs gracefully, and
make sure in-flight probe keys are removed on timeout.
---
Nitpick comments:
In `@cmuxTests/TabManagerUnitTests.swift`:
- Around line 585-595: The test currently waits only for panel-level state via
waitForCondition { workspace.panelGitBranches[panelId]? ... } but then
immediately asserts workspace-level snapshot (workspace.gitBranch and
workspace.pullRequest), which can race; update the waitForCondition predicate
(the closure passed to waitForCondition) to also assert
workspace.gitBranch?.branch == "feature/sidebar-branch-update",
workspace.gitBranch?.isDirty == false, and workspace.pullRequest == nil so all
relevant assertions are synchronized, then remove the subsequent standalone
XCTAssertEqual/XCTAssertNil lines; reference waitForCondition,
workspace.panelGitBranches, workspace.gitBranch, workspace.pullRequest, and
panelId when making the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7685a005-2841-42a3-a78e-ecee4c209b15
📒 Files selected for processing (2)
Sources/TabManager.swiftcmuxTests/TabManagerUnitTests.swift
| private nonisolated static func workspaceGitBranchSnapshot( | ||
| for directory: String | ||
| ) -> WorkspaceGitBranchSnapshot { | ||
| let branch = normalizedBranchName( | ||
| runGitCommand(directory: directory, arguments: ["branch", "--show-current"]) | ||
| ) | ||
| guard let branch else { | ||
| return WorkspaceGitBranchSnapshot(branch: nil, isDirty: false) | ||
| } | ||
|
|
||
| let statusOutput = runGitCommand(directory: directory, arguments: ["status", "--porcelain", "-uno"]) | ||
| let isDirty = !(statusOutput?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true) | ||
| let pullRequest = workspacePullRequestSnapshot(directory: directory, branch: branch) | ||
| return InitialWorkspaceGitMetadataSnapshot(branch: branch, isDirty: isDirty, pullRequest: pullRequest) | ||
| return WorkspaceGitBranchSnapshot(branch: branch, isDirty: isDirty) | ||
| } |
There was a problem hiding this comment.
Add timeouts to periodic git branch probes to prevent permanent in-flight stalls.
These commands run in the 2s polling path, but they currently have no timeout. If git hangs, the probe key remains in workspaceGitBranchProbeInFlightKeys and that panel can stop refreshing branch state indefinitely.
🛠️ Proposed fix
@@
- private nonisolated static let workspacePullRequestProbeTimeout: TimeInterval = 5.0
+ private nonisolated static let workspacePullRequestProbeTimeout: TimeInterval = 5.0
+ private nonisolated static let workspaceGitBranchProbeTimeout: TimeInterval = 3.0
@@
private nonisolated static func workspaceGitBranchSnapshot(
for directory: String
) -> WorkspaceGitBranchSnapshot {
let branch = normalizedBranchName(
- runGitCommand(directory: directory, arguments: ["branch", "--show-current"])
+ runGitCommand(
+ directory: directory,
+ arguments: ["branch", "--show-current"],
+ timeout: workspaceGitBranchProbeTimeout
+ )
)
guard let branch else {
return WorkspaceGitBranchSnapshot(branch: nil, isDirty: false)
}
- let statusOutput = runGitCommand(directory: directory, arguments: ["status", "--porcelain", "-uno"])
+ let statusOutput = runGitCommand(
+ directory: directory,
+ arguments: ["status", "--porcelain", "-uno"],
+ timeout: workspaceGitBranchProbeTimeout
+ )
let isDirty = !(statusOutput?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true)
return WorkspaceGitBranchSnapshot(branch: branch, isDirty: isDirty)
}
- private nonisolated static func runGitCommand(directory: String, arguments: [String]) -> String? {
+ private nonisolated static func runGitCommand(
+ directory: String,
+ arguments: [String],
+ timeout: TimeInterval? = nil
+ ) -> String? {
runCommand(
directory: directory,
executable: "git",
- arguments: arguments
+ arguments: arguments,
+ timeout: timeout
)
}Also applies to: 1753-1758
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Sources/TabManager.swift` around lines 1738 - 1751, The git invocations in
workspaceGitBranchSnapshot (and the similar method at lines ~1753-1758)
currently call runGitCommand without a timeout which can leave probes stuck;
update these call sites to use a timeout-aware variant of runGitCommand (or wrap
the git Process call with a timeout) so the branch and status commands return
nil/error on timeout, then handle that result by returning a safe
WorkspaceGitBranchSnapshot (branch: nil, isDirty: false) and ensuring any probe
bookkeeping (workspaceGitBranchProbeInFlightKeys) is cleared when a timeout
occurs; specifically modify workspaceGitBranchSnapshot and the sibling function
to call the timeout-enabled runGitCommand, handle timed-out/nil outputs
gracefully, and make sure in-flight probe keys are removed on timeout.
Greptile SummaryThis PR fixes #666 by adding a lightweight 2-second git branch polling loop ( Key changes:
Issues found:
Confidence Score: 4/5Safe to merge with one minor logic fix; the detached HEAD edge case leaves a stale PR visible for up to 30 s but does not corrupt data. The implementation is well-structured and correctly guards against concurrent probes. The only real logic gap (stale PR in detached HEAD) is a narrow edge case and not a regression from pre-PR behaviour for most users. Both findings are P2; the detached-HEAD fix is a one-line change worth doing before merge. Sources/TabManager.swift — specifically the applyTrackedWorkspaceGitBranchSnapshot metadata-refresh condition at line 1688. Important Files Changed
Sequence DiagramsequenceDiagram
participant Timer2s as Branch Poll (2 s)
participant TimerMeta as Metadata Poll (30 s)
participant Queue as initialWorkspaceGitProbeQueue
participant Main as MainActor (TabManager)
participant GitHub as GitHub API
Timer2s->>Main: refreshTrackedWorkspaceGitBranches()
Main->>Queue: workspaceGitBranchSnapshot(dir)
Queue-->>Main: WorkspaceGitBranchSnapshot
Main->>Main: applyTrackedWorkspaceGitBranchSnapshot()
Note over Main: updatePanelGitBranch / clearPanelGitBranch
alt branch changed (new branch != nil)
Main->>Queue: scheduleWorkspaceGitMetadataRefreshIfPossible()
Queue->>GitHub: gh pr view
GitHub-->>Queue: PR result
Queue-->>Main: applyWorkspaceGitMetadataSnapshot()
Note over Main: update/clear panelPullRequests
end
TimerMeta->>Main: refreshTrackedWorkspaceGitMetadata()
Main->>Queue: initialWorkspaceGitMetadataSnapshot(dir)
Queue->>GitHub: gh pr view
GitHub-->>Queue: PR result
Queue-->>Main: applyWorkspaceGitMetadataSnapshot()
Reviews (1): Last reviewed commit: "fix: refresh sidebar branch after checko..." | Re-trigger Greptile |
| if previousState?.branch != nextState?.branch, | ||
| nextState?.branch != nil { | ||
| scheduleWorkspaceGitMetadataRefreshIfPossible( | ||
| workspaceId: probeKey.workspaceId, | ||
| panelId: probeKey.panelId, | ||
| reason: "branchPollChanged" | ||
| ) | ||
| } |
There was a problem hiding this comment.
Stale PR persists when branch transitions to detached HEAD
The nextState?.branch != nil guard prevents triggering a metadata refresh when the branch goes from a named branch to nil (i.e. the user enters detached HEAD via git checkout <sha>). In that case clearPanelGitBranch removes the branch label, but panelPullRequests[panelId] keeps the old PR. It will remain visible in the sidebar until the next 30-second periodic metadata poll — up to 30 seconds of incorrect sidebar state.
The condition should be loosened to fire on any branch change, not just changes that land on a valid branch:
| if previousState?.branch != nextState?.branch, | |
| nextState?.branch != nil { | |
| scheduleWorkspaceGitMetadataRefreshIfPossible( | |
| workspaceId: probeKey.workspaceId, | |
| panelId: probeKey.panelId, | |
| reason: "branchPollChanged" | |
| ) | |
| } | |
| if previousState?.branch != nextState?.branch { | |
| scheduleWorkspaceGitMetadataRefreshIfPossible( | |
| workspaceId: probeKey.workspaceId, | |
| panelId: probeKey.panelId, | |
| reason: "branchPollChanged" | |
| ) | |
| } |
initialWorkspaceGitMetadataSnapshot already handles the branch == nil case by returning pullRequest: .notFound, which will clear the stale PR entry.
| func testTrackedWorkspaceGitBranchRefreshUpdatesSidebarBranchAfterCheckout() throws { | ||
| let fileManager = FileManager.default | ||
| let repoURL = fileManager.temporaryDirectory.appendingPathComponent( | ||
| "cmux-git-branch-refresh-\(UUID().uuidString)" | ||
| ) | ||
| try fileManager.createDirectory(at: repoURL, withIntermediateDirectories: true) | ||
| defer { try? fileManager.removeItem(at: repoURL) } | ||
|
|
||
| try runGit(["init", "-b", "main"], in: repoURL) | ||
| try runGit(["config", "user.name", "cmux tests"], in: repoURL) | ||
| try runGit(["config", "user.email", "cmux@example.invalid"], in: repoURL) | ||
| try "seed\n".write( | ||
| to: repoURL.appendingPathComponent("README.md"), | ||
| atomically: true, | ||
| encoding: .utf8 | ||
| ) | ||
| try runGit(["add", "README.md"], in: repoURL) | ||
| try runGit(["commit", "-m", "Initial commit"], in: repoURL) | ||
|
|
||
| let manager = TabManager() | ||
| guard let workspace = manager.selectedWorkspace, | ||
| let panelId = workspace.focusedPanelId else { | ||
| XCTFail("Expected selected workspace with focused panel") | ||
| return | ||
| } | ||
|
|
||
| workspace.updatePanelDirectory(panelId: panelId, directory: repoURL.path) | ||
| workspace.updatePanelGitBranch(panelId: panelId, branch: "main", isDirty: false) | ||
| workspace.updatePanelPullRequest( | ||
| panelId: panelId, | ||
| number: 666, | ||
| label: "PR", | ||
| url: try XCTUnwrap(URL(string: "https://github.com/manaflow-ai/cmux/pull/666")), | ||
| status: .open, | ||
| branch: "main" | ||
| ) | ||
|
|
||
| try runGit(["checkout", "-b", "feature/sidebar-branch-update"], in: repoURL) | ||
|
|
||
| manager.refreshTrackedWorkspaceGitMetadataForTesting() | ||
|
|
||
| XCTAssertTrue( | ||
| waitForCondition { | ||
| workspace.panelGitBranches[panelId]?.branch == "feature/sidebar-branch-update" | ||
| && workspace.panelGitBranches[panelId]?.isDirty == false | ||
| && workspace.panelPullRequests[panelId] == nil | ||
| } | ||
| ) | ||
| XCTAssertEqual(workspace.gitBranch?.branch, "feature/sidebar-branch-update") | ||
| XCTAssertEqual(workspace.gitBranch?.isDirty, false) | ||
| XCTAssertNil(workspace.pullRequest) | ||
| } | ||
| } | ||
|
|
||
|
|
There was a problem hiding this comment.
Regression test and fix should be in separate commits
Per the project's regression test commit policy (CLAUDE.md), a regression test for a bug fix must be introduced in a separate commit before the fix so CI can confirm the test fails on the unfixed code and passes after the fix:
- Commit 1: Add
testTrackedWorkspaceGitBranchRefreshUpdatesSidebarBranchAfterCheckoutwith no production changes — CI should go red. - Commit 2: Add the
startWorkspaceGitBranchPollTimer/refreshTrackedWorkspaceGitBranchesfix — CI should go green.
As written, both the test and the fix land together, so the PR's commit history can't prove the test actually catches the regression.
Context Used: CLAUDE.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
Sources/TabManager.swift (1)
1741-1761:⚠️ Potential issue | 🟠 MajorStill missing timeouts on the branch poll git commands.
These calls back
workspaceGitBranchProbeInFlightKeys. If eithergitprocess hangs, the completion path never runs and that panel stops polling permanently.🛠️ Minimal fix
+ private nonisolated static let workspaceGitBranchProbeTimeout: TimeInterval = 3.0 @@ let branch = normalizedBranchName( - runGitCommand(directory: directory, arguments: ["branch", "--show-current"]) + runGitCommand( + directory: directory, + arguments: ["branch", "--show-current"], + timeout: workspaceGitBranchProbeTimeout + ) ) @@ - let statusOutput = runGitCommand(directory: directory, arguments: ["status", "--porcelain", "-uno"]) + let statusOutput = runGitCommand( + directory: directory, + arguments: ["status", "--porcelain", "-uno"], + timeout: workspaceGitBranchProbeTimeout + ) @@ - private nonisolated static func runGitCommand(directory: String, arguments: [String]) -> String? { + private nonisolated static func runGitCommand( + directory: String, + arguments: [String], + timeout: TimeInterval? = nil + ) -> String? { runCommand( directory: directory, executable: "git", - arguments: arguments + arguments: arguments, + timeout: timeout ) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/TabManager.swift` around lines 1741 - 1761, The git invocations in workspaceGitBranchSnapshot (via runGitCommand) can hang and block the probe flow tied to workspaceGitBranchProbeInFlightKeys; update the implementation so runGitCommand (or the underlying runCommand) enforces a reasonable timeout and returns nil/error on timeout instead of blocking indefinitely; ensure workspaceGitBranchSnapshot treats timeout as a failed/empty result (branch nil or isDirty false) so the completion path always runs and the poll can continue. Identify and modify the runGitCommand function (or runCommand it calls) to accept/apply a timeout and to abort the Process if the timeout elapses, then propagate a nil/string result back to workspaceGitBranchSnapshot.
🧹 Nitpick comments (1)
Sources/TabManager.swift (1)
1114-1123: Separate branch polling from the PR probe queue.
initialWorkspaceGitProbeQueueis serial, so one slowghmetadata probe on another panel/workspace can block these 2s branch snapshots behind it. That weakens the “fast branch / slow metadata” split this PR is introducing; a dedicated queue for branch probes would keep sidebar branch updates responsive while the existing per-key guards still prevent same-panel overlap.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/TabManager.swift` around lines 1114 - 1123, The branch snapshot work should run on a separate serial queue instead of initialWorkspaceGitProbeQueue; add a dedicated DispatchQueue property (e.g., workspaceGitBranchProbeQueue) for branch probes and use that queue when scheduling the call that computes Self.workspaceGitBranchSnapshot(for: normalizedDirectory) and then dispatches the Task { `@MainActor` ... self?.applyTrackedWorkspaceGitBranchSnapshot(...) }; keep the existing weak self, probeKey/key and expectedDirectory/normalizedDirectory usage and retain the per-key guards so same-panel overlap is still prevented while avoiding blocking branch probes behind slow gh metadata probes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Sources/TabManager.swift`:
- Around line 1691-1698: The current guard only schedules a metadata refresh
when the branch changed and the new branch is non-nil, so if the branch becomes
nil (detached HEAD) we skip refresh; update the condition in the block that
calls scheduleWorkspaceGitMetadataRefreshIfPossible (the one using
previousState, nextState and probeKey) to trigger whenever previousState?.branch
!= nextState?.branch (remove the nextState?.branch != nil check) so the refresh
runs both when a branch changes and when it becomes nil; keep the same
parameters (workspaceId: probeKey.workspaceId, panelId: probeKey.panelId,
reason: "branchPollChanged").
---
Duplicate comments:
In `@Sources/TabManager.swift`:
- Around line 1741-1761: The git invocations in workspaceGitBranchSnapshot (via
runGitCommand) can hang and block the probe flow tied to
workspaceGitBranchProbeInFlightKeys; update the implementation so runGitCommand
(or the underlying runCommand) enforces a reasonable timeout and returns
nil/error on timeout instead of blocking indefinitely; ensure
workspaceGitBranchSnapshot treats timeout as a failed/empty result (branch nil
or isDirty false) so the completion path always runs and the poll can continue.
Identify and modify the runGitCommand function (or runCommand it calls) to
accept/apply a timeout and to abort the Process if the timeout elapses, then
propagate a nil/string result back to workspaceGitBranchSnapshot.
---
Nitpick comments:
In `@Sources/TabManager.swift`:
- Around line 1114-1123: The branch snapshot work should run on a separate
serial queue instead of initialWorkspaceGitProbeQueue; add a dedicated
DispatchQueue property (e.g., workspaceGitBranchProbeQueue) for branch probes
and use that queue when scheduling the call that computes
Self.workspaceGitBranchSnapshot(for: normalizedDirectory) and then dispatches
the Task { `@MainActor` ... self?.applyTrackedWorkspaceGitBranchSnapshot(...) };
keep the existing weak self, probeKey/key and
expectedDirectory/normalizedDirectory usage and retain the per-key guards so
same-panel overlap is still prevented while avoiding blocking branch probes
behind slow gh metadata probes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
| if previousState?.branch != nextState?.branch, | ||
| nextState?.branch != nil { | ||
| scheduleWorkspaceGitMetadataRefreshIfPossible( | ||
| workspaceId: probeKey.workspaceId, | ||
| panelId: probeKey.panelId, | ||
| reason: "branchPollChanged" | ||
| ) | ||
| } |
There was a problem hiding this comment.
Refresh PR state when the polled branch becomes nil.
If a checkout lands in detached HEAD, this guard skips the follow-up metadata refresh, so the sidebar can show no branch while still keeping the previous PR badge until the 30s metadata poll runs.
🛠️ Minimal fix
- if previousState?.branch != nextState?.branch,
- nextState?.branch != nil {
+ if previousState?.branch != nextState?.branch {
scheduleWorkspaceGitMetadataRefreshIfPossible(
workspaceId: probeKey.workspaceId,
panelId: probeKey.panelId,
reason: "branchPollChanged"
)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if previousState?.branch != nextState?.branch, | |
| nextState?.branch != nil { | |
| scheduleWorkspaceGitMetadataRefreshIfPossible( | |
| workspaceId: probeKey.workspaceId, | |
| panelId: probeKey.panelId, | |
| reason: "branchPollChanged" | |
| ) | |
| } | |
| if previousState?.branch != nextState?.branch { | |
| scheduleWorkspaceGitMetadataRefreshIfPossible( | |
| workspaceId: probeKey.workspaceId, | |
| panelId: probeKey.panelId, | |
| reason: "branchPollChanged" | |
| ) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Sources/TabManager.swift` around lines 1691 - 1698, The current guard only
schedules a metadata refresh when the branch changed and the new branch is
non-nil, so if the branch becomes nil (detached HEAD) we skip refresh; update
the condition in the block that calls
scheduleWorkspaceGitMetadataRefreshIfPossible (the one using previousState,
nextState and probeKey) to trigger whenever previousState?.branch !=
nextState?.branch (remove the nextState?.branch != nil check) so the refresh
runs both when a branch changes and when it becomes nil; keep the same
parameters (workspaceId: probeKey.workspaceId, panelId: probeKey.panelId,
reason: "branchPollChanged").
Fixes #666.
Summary
Verification
./scripts/reload.sh --tag sidebar-branch-update --launch./scripts/reload.sh --tag sidebar-branch-updateSummary by cubic
Fixes stale sidebar branch after a terminal checkout by polling the repo HEAD every ~2s. Sidebar branch updates quickly, and PR metadata refreshes only when the branch actually changes.
Written for commit 977f7fd. Summary will update on new commits.
Summary by CodeRabbit