perf: bound working tree diff reads (#4171)

Unstaged source-control diffs now check the working-tree file size before readFile and use the existing 10 MB diff budget to avoid pulling huge generated files into the main process.
This commit is contained in:
Neil
2026-05-31 06:48:17 -07:00
committed by GitHub
parent a5d7896f1b
commit 2e286eef8d
2 changed files with 33 additions and 1 deletions
+23
View File
@@ -8,6 +8,7 @@ const {
lstatMock,
realpathMock,
readFileMock,
statMock,
rmMock,
existsSyncMock
} = vi.hoisted(() => ({
@@ -16,6 +17,7 @@ const {
lstatMock: vi.fn(),
realpathMock: vi.fn(),
readFileMock: vi.fn(),
statMock: vi.fn(),
rmMock: vi.fn(),
existsSyncMock: vi.fn()
}))
@@ -33,6 +35,7 @@ vi.mock('fs/promises', () => ({
lstat: lstatMock,
realpath: realpathMock,
readFile: readFileMock,
stat: statMock,
rm: rmMock
}))
@@ -252,7 +255,12 @@ describe('getDiff', () => {
gitExecFileAsyncBufferMock.mockReset()
lstatMock.mockReset()
readFileMock.mockReset()
statMock.mockReset()
existsSyncMock.mockReset()
statMock.mockResolvedValue({
isFile: () => true,
size: 12
})
})
it('uses the index as the left side for unstaged diffs when present', async () => {
@@ -318,6 +326,21 @@ describe('getDiff', () => {
expect(result.modifiedIsBinary).toBe(false)
})
it('does not read oversized working-tree files into memory', async () => {
gitExecFileAsyncBufferMock.mockResolvedValueOnce({ stdout: Buffer.from('index-content\n') })
statMock.mockResolvedValueOnce({
isFile: () => true,
size: 10 * 1024 * 1024 + 1
})
const result = await getDiff('/repo', 'dist/large.log', false)
expect(readFileMock).not.toHaveBeenCalled()
expect(result.kind).toBe('binary')
expect(result.modifiedIsBinary).toBe(true)
expect(result.modifiedContent).toBe('')
})
it('includes preview metadata for pdf diffs', async () => {
const pdfBuffer = Buffer.from([0x25, 0x50, 0x44, 0x46, 0x00])
gitExecFileAsyncBufferMock.mockResolvedValueOnce({ stdout: pdfBuffer })
+10 -1
View File
@@ -1,6 +1,6 @@
/* eslint-disable max-lines */
import { existsSync } from 'fs'
import { readFile } from 'fs/promises'
import { readFile, stat } from 'fs/promises'
import * as path from 'path'
import type {
GitBranchChangeEntry,
@@ -950,6 +950,15 @@ async function readGitBlobAtOidPath(
async function readWorkingTreeFile(filePath: string): Promise<GitBlobReadResult> {
try {
const fileStat = await stat(filePath)
if (!fileStat.isFile()) {
return { content: '', isBinary: false, exists: false }
}
if (fileStat.size > MAX_GIT_SHOW_BYTES) {
// Why: git blob reads are capped through maxBuffer; mirror that bound for
// unstaged working-tree content before readFile can pull in huge assets.
return { content: '', isBinary: true, exists: true }
}
const buffer = await readFile(filePath)
return bufferToBlob(buffer, filePath)
} catch {