Files
paseo/packages/server/src/utils/git-file-contents.ts
T
Mohamed Boudra 33a52d0ae7 Speed up cold diffs and wrapped text measurement (#4676)
* perf(app): optimize wrapped measurements and checkpoint native layout experiment

Compute full grapheme geometry only for final wrap fragments. Preserve shaped-text search behavior while bounding additive wrap probes. Keep the Android background layout experiment isolated from the normal diff renderer; native renderer integration and iOS bindings remain unfinished.

* perf(server): reduce cold diff generation work

Keep active diff reads owned across invalidation and wait for the initial watcher inventory before cold generation. Batch patch and blob reads while preserving per-file size isolation, bounded output, and daemon responsiveness. Avoid constructing fallback tokens when full-file highlighting already covers both sides.

* test(server): exercise oversized highlighted diff rejection

The single-line fixture now bypasses highlighting under the long-line guard, so it no longer reaches the structured payload limit. Use short dense lines to keep testing oversized rejection and continued socket usability.

* fix(server): queue fresh diffs for forced reads

A forced request following a repository mutation must not reuse an older active build. Invalidate that result and coalesce waiting callers onto one subsequent build. Give the cache a named object input and completed-value type.

* test(server): share deferred diff loader setup

* test(server): name completed diff cache setup

* refactor(server): use aliases for new diff imports

* fix(server): keep shipped diff imports resolvable by Node

The TypeScript build preserves path aliases without a runtime resolver. Restore relative imports for shipped code and declarations, and correct the server import guideline that prompted the regression.
2026-09-11 08:07:01 +02:00

49 lines
1.8 KiB
TypeScript

import { runGitCommandBytes } from "./run-git-command.js";
// Match the existing git-show ceiling. Oversized batches use bounded per-file reads.
const MAX_CONTENT_BYTES = 20 * 1024 * 1024;
/** Null means the batch cannot preserve per-file read semantics within its budget. */
export async function readGitFileContents(
cwd: string,
specs: string[],
): Promise<Map<string, string | null> | null> {
const unique = [...new Set(specs)];
if (unique.length === 0) return new Map();
// The line-based batch protocol cannot represent embedded newlines.
if (unique.some((spec) => spec.includes("\n"))) return null;
const result = await runGitCommandBytes(["cat-file", "--batch"], {
cwd,
input: unique.join("\n") + "\n",
envOverlay: { GIT_OPTIONAL_LOCKS: "0", LC_ALL: "C" },
maxOutputBytes: MAX_CONTENT_BYTES,
});
if (result.truncated) return null;
const contents = new Map<string, string | null>();
let offset = 0;
for (const spec of unique) {
const end = result.stdout.indexOf(10, offset);
if (end < 0) throw new Error("Incomplete git object header");
const header = result.stdout.subarray(offset, end).toString("utf8");
offset = end + 1;
if (header === `${spec} missing`) {
contents.set(spec, null);
continue;
}
const match = /^[a-f0-9]+ blob (\d+)$/.exec(header);
// Submodules and other non-blob objects retain git-show behavior.
if (!match) return null;
const size = Number(match[1]);
if (
!Number.isSafeInteger(size) ||
offset + size >= result.stdout.length ||
result.stdout[offset + size] !== 10
)
throw new Error("Invalid git object length");
contents.set(spec, result.stdout.subarray(offset, offset + size).toString("utf8"));
offset += size + 1;
}
if (offset !== result.stdout.length) throw new Error("Unexpected git object output");
return contents;
}