update
Some checks failed
CI / check (push) Has been cancelled

This commit is contained in:
2026-06-17 17:35:45 +02:00
parent 513af0e164
commit 6beef86506
2 changed files with 34 additions and 4 deletions

View File

@@ -5,6 +5,22 @@ import { spawn } from 'node:child_process'
const delay = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms))
/** Map `fn` over `items` with at most `limit` running at once, preserving order.
* Keeps the per-file git/disk reads overlapping without spawning hundreds of
* subprocesses at once (macOS has a low default open-file limit). */
async function mapLimit<T, R>(items: T[], limit: number, fn: (item: T, index: number) => Promise<R>): Promise<R[]> {
const out = new Array<R>(items.length)
let next = 0
const worker = async (): Promise<void> => {
while (next < items.length) {
const i = next++
out[i] = await fn(items[i], i)
}
}
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker))
return out
}
export type GitStatusLetter = 'A' | 'M' | 'D' | 'R' | 'U'
export interface GitChange {
@@ -193,15 +209,19 @@ async function doLoad(root: string): Promise<GitLoad | null> {
if (raw == null) return null
const { branch, files } = parseStatus(raw)
const changes: GitChange[] = []
for (const f of files) {
// Each changed file needs its HEAD blob (a `git show` spawn) + its disk text.
// Done serially this is O(files) subprocess spawns in a row — staging one file
// re-reads ALL of them, which is the dominant cost of a reload. Run them with
// bounded concurrency instead so the spawns overlap (cap keeps us well under
// macOS's low default FD limit). Order is preserved by index.
const changes = await mapLimit(files, 12, async (f) => {
const { letter, staged } = classify(f.index, f.working)
const isNew = f.index === '?' || f.index === 'A'
const isDeleted = letter === 'D'
const original = isNew ? '' : await headText(root, f.path)
const updated = isDeleted ? '' : await diskText(root, f.path)
changes.push({ path: f.path, status: letter, staged, original, updated })
}
return { path: f.path, status: letter, staged, original, updated } as GitChange
})
return { branch, changes }
}