fix(wsl): Commits history wakes a stopped distro only on the user's own act (#35854)

* fix(wsl): Commits history wakes a stopped distro only on the user's own act

Showing Commits woke the distro on every mount, remount and re-show. Now
only the user expanding Commits in the same worktree, or the user's own
switch to the workspace (once per switch), may start it; the Refresh
button keeps its explicit wake (E9).

* test: a mount and a base-ref move read Commits history in the background

#35778's base-move refetch test expected every shown-history read to
wake a stopped WSL distro; only the user's own act does now.
This commit is contained in:
Neil
2026-10-29 02:47:20 -07:00
committed by GitHub
parent 03d1e8c289
commit cd821abb2c
3 changed files with 101 additions and 30 deletions
@@ -1,7 +1,11 @@
// @vitest-environment happy-dom
import { act, cleanup, renderHook } from '@testing-library/react'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import {
noteUserWorkspaceSwitch,
resetWorkspaceSwitchUserActsForTests
} from '@/lib/workspace-switch-user-act'
const getRuntimeGitHistoryMock = vi.hoisted(() => vi.fn())
vi.mock('@/runtime/runtime-git-status-client', () => ({
@@ -11,33 +15,83 @@ vi.mock('@/lib/connection-context', () => ({ getConnectionId: () => null }))
import { useSourceControlGitHistory } from './use-git-history'
type Props = Parameters<typeof useSourceControlGitHistory>[0]
const props = (overrides: Partial<Props> = {}): Props => ({
activeRepoTarget: { kind: 'local' },
activeWorktreeId: 'wt-1',
worktreePath: '\\\\wsl.localhost\\Ubuntu\\repo',
compareBaseRef: 'main',
isFolder: false,
isBranchVisible: true,
isGitHistoryExpanded: true,
isGitHistoryVisible: true,
worktreeMap: new Map([
['wt-1', {}],
['wt-2', {}]
]),
...overrides
})
const wakes = (): unknown[] =>
getRuntimeGitHistoryMock.mock.calls.map((call) => call[1].wakeWslDistro)
beforeEach(() => {
getRuntimeGitHistoryMock.mockReset().mockResolvedValue({ items: [] })
resetWorkspaceSwitchUserActsForTests()
})
afterEach(cleanup)
// E9: showing Commits (or its Refresh) is a user act; a refresh after a Git action is not.
// E9: only the user's own act may start a stopped WSL distro for a Commits read.
describe('Commits history and a stopped WSL distro', () => {
it('wakes for the read that shows Commits, not for a later ref-driven refresh', async () => {
getRuntimeGitHistoryMock.mockReset().mockResolvedValue({ items: [] })
const { result } = renderHook(() =>
useSourceControlGitHistory({
activeRepoTarget: { kind: 'local' },
activeWorktreeId: 'wt-1',
worktreePath: '\\\\wsl.localhost\\Ubuntu\\repo',
compareBaseRef: 'main',
isFolder: false,
isBranchVisible: true,
isGitHistoryExpanded: true,
isGitHistoryVisible: true,
worktreeMap: new Map([['wt-1', {}]])
})
)
it('stays background on a mount or remount with Commits already open', async () => {
const first = renderHook(() => useSourceControlGitHistory(props()))
await act(async () => {})
first.unmount()
renderHook(() => useSourceControlGitHistory(props()))
await act(async () => {})
expect(wakes()).toEqual([undefined, undefined])
})
it('wakes once for the user switching to the workspace, never again on a remount', async () => {
noteUserWorkspaceSwitch('wt-1')
const first = renderHook(() => useSourceControlGitHistory(props()))
await act(async () => {})
first.unmount()
renderHook(() => useSourceControlGitHistory(props()))
await act(async () => {})
expect(wakes()).toEqual([true, undefined])
})
it('wakes when the user expands Commits, not when Orca re-shows it or a refresh follows', async () => {
const { result, rerender } = renderHook((p: Props) => useSourceControlGitHistory(p), {
initialProps: props({ isGitHistoryExpanded: false })
})
await act(async () => {})
rerender(props())
await act(async () => {})
// Hidden and shown again by Orca (a sidebar re-show), with no switch.
rerender(props({ isGitHistoryVisible: false }))
rerender(props())
await act(async () => {})
await act(async () => result.current.refreshGitHistoryRef.current())
await act(async () => result.current.refreshGitHistory({ wakeWslDistro: true }))
expect(wakes()).toEqual([true, undefined, undefined])
})
expect(getRuntimeGitHistoryMock.mock.calls.map((call) => call[1].wakeWslDistro)).toEqual([
true,
undefined,
true
])
it("does not take another worktree's restored expanded view as a click", async () => {
const { rerender } = renderHook((p: Props) => useSourceControlGitHistory(p), {
initialProps: props({ isGitHistoryExpanded: false })
})
await act(async () => {})
rerender(props({ activeWorktreeId: 'wt-2' }))
await act(async () => {})
expect(wakes()).toEqual([undefined])
})
it("keeps the Refresh button's explicit wake", async () => {
const { result } = renderHook(() => useSourceControlGitHistory(props()))
await act(async () => {})
await act(async () => result.current.refreshGitHistory({ wakeWslDistro: true }))
expect(wakes()).toEqual([undefined, true])
})
})
@@ -4,12 +4,15 @@ import type { RuntimeClientTarget } from '../../../../../../shared/runtime-clien
import { getConnectionId } from '@/lib/connection-context'
import { getRuntimeGitHistory } from '@/runtime/runtime-git-status-client'
import type { GitHistoryPanelState } from './git-history-panel'
import { takeWorkspaceSwitchWake } from '@/lib/workspace-switch-user-act'
const EMPTY_GIT_HISTORY_STATE: GitHistoryPanelState = { status: 'idle' }
/** `wakeWslDistro`: showing Commits or its Refresh, a user act that may start a stopped WSL distro. */
/** `wakeWslDistro`: the user opening Commits or its Refresh, a user act that may start a stopped WSL distro. */
type HistoryRefresh = { wakeWslDistro?: boolean }
const SHOWN_HISTORY_LOAD: HistoryRefresh = { wakeWslDistro: true }
// The mounts and re-shows a user's own workspace switch causes land within this window.
const SWITCH_MOUNT_WINDOW_MS = 10_000
/** Cache/prune history per worktree and reject superseded requests; refreshGitHistoryRef lets effects refresh without callback resubscription. */
export function useSourceControlGitHistory({
@@ -150,13 +153,27 @@ export function useSourceControlGitHistory({
refreshGitHistoryRef.current = refreshGitHistory
}, [refreshGitHistory])
// Expanded at mount, or restored with another worktree's view, is not a click: only an expand
// within the same worktree counts as the user opening Commits.
const lastExpandedRef = useRef({ worktreeId: activeWorktreeId, expanded: isGitHistoryExpanded })
useEffect(() => {
const last = lastExpandedRef.current
const userExpanded =
isGitHistoryExpanded && !last.expanded && last.worktreeId === activeWorktreeId
lastExpandedRef.current = { worktreeId: activeWorktreeId, expanded: isGitHistoryExpanded }
// Defer Git subprocesses until Commits is expanded and visible, especially for large/remote repos.
if (!isBranchVisible || !isGitHistoryExpanded || !isGitHistoryVisible) {
return
}
// The user is looking at Commits; refreshes after Git actions stay background (E9).
void refreshGitHistoryRef.current(SHOWN_HISTORY_LOAD)
// Only the user's own act may start a stopped WSL distro: opening Commits, or switching to this
// workspace (once per switch). A remount, restore or Orca's own re-show stays background (E9).
const userAct =
userExpanded ||
(activeWorktreeId !== null &&
takeWorkspaceSwitchWake(activeWorktreeId, 'git-history', {
withinMs: SWITCH_MOUNT_WINDOW_MS
}))
void refreshGitHistoryRef.current(userAct ? SHOWN_HISTORY_LOAD : undefined)
}, [
// History uses compareBaseRef, which can move independently of effectiveBaseRef.
activeWorktreeId,
@@ -219,8 +219,8 @@ describe('useSourceControlGitHistory stale completion', () => {
await flush()
expect(mocks.getRuntimeGitHistory).toHaveBeenLastCalledWith(
expect.objectContaining({ worktreeId: 'A', worktreePath: '/a' }),
// Showing Commits is a user act, so it may start a stopped WSL distro.
{ limit: 50, baseRef: 'origin/main', wakeWslDistro: true }
// A mount with Commits already open is restored state, not a user act: no WSL wake (E9).
{ limit: 50, baseRef: 'origin/main' }
)
await act(async () => {
@@ -228,10 +228,10 @@ describe('useSourceControlGitHistory stale completion', () => {
})
await flush()
expect(mocks.getRuntimeGitHistory).toHaveBeenCalledTimes(2)
// A base-ref move refetches in the background; it never starts a stopped distro.
expect(mocks.getRuntimeGitHistory).toHaveBeenLastCalledWith(expect.anything(), {
limit: 50,
baseRef: 'origin/dev',
wakeWslDistro: true
baseRef: 'origin/dev'
})
})