diff --git a/src/relay/git-handler-status-ops.test.ts b/src/relay/git-handler-status-ops.test.ts new file mode 100644 index 00000000000..c966ce1a97e --- /dev/null +++ b/src/relay/git-handler-status-ops.test.ts @@ -0,0 +1,56 @@ +import { mkdtempSync } from 'fs' +import * as fs from 'fs/promises' +import { tmpdir } from 'os' +import * as path from 'path' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import type { GitExec } from './git-handler-ops' +import { getStatusOp } from './git-handler-status-ops' + +const LARGE_STATUS_ENTRY_COUNT = 150_000 + +function buildLargeStatusOutput(count: number): string { + const lines: string[] = [] + for (let index = 0; index < count; index += 1) { + lines.push(`1 A. N... 100644 100644 100644 000000 111111 generated-${index}.txt`) + } + return lines.join('\n') +} + +describe('getStatusOp', () => { + let tmpDir: string + + beforeEach(() => { + tmpDir = mkdtempSync(path.join(tmpdir(), 'relay-git-status-')) + }) + + afterEach(async () => { + await fs.rm(tmpDir, { recursive: true, force: true }) + }) + + it('returns large parsed status entry lists', async () => { + const statusOutput = buildLargeStatusOutput(LARGE_STATUS_ENTRY_COUNT) + const git = vi.fn(async (args) => { + if (args.includes('status')) { + return { stdout: statusOutput, stderr: '' } + } + if (args.includes('diff')) { + return { stdout: '', stderr: '' } + } + throw new Error(`Unexpected git command: ${args.join(' ')}`) + }) + + const result = await getStatusOp(git, { worktreePath: tmpDir }) + + expect(result.entries).toHaveLength(LARGE_STATUS_ENTRY_COUNT) + expect(result.entries[0]).toEqual({ + path: 'generated-0.txt', + status: 'added', + area: 'staged' + }) + expect(result.entries.at(-1)).toEqual({ + path: `generated-${LARGE_STATUS_ENTRY_COUNT - 1}.txt`, + status: 'added', + area: 'staged' + }) + }) +}) diff --git a/src/relay/git-handler-status-ops.ts b/src/relay/git-handler-status-ops.ts index 02cfc3f5195..3c4b9c4ee8f 100644 --- a/src/relay/git-handler-status-ops.ts +++ b/src/relay/git-handler-status-ops.ts @@ -99,7 +99,11 @@ export async function getStatusOp( disableOptionalLocks: true }) const parsed = parseStatusOutput(stdout) - entries.push(...parsed.entries) + // Why: huge worktrees can produce enough status rows to exceed JavaScript's + // spread-argument limit during routine source-control polling. + for (const entry of parsed.entries) { + entries.push(entry) + } head = parsed.head branch = parsed.branch upstreamStatus = parsed.upstreamStatus