mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-05 07:54:38 +00:00
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.
This commit is contained in:
@@ -2,39 +2,8 @@ import { execFileSync } from "node:child_process";
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync, realpathSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { beforeEach, afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const spawnCounters = vi.hoisted(() => ({
|
||||
trackedTextDiffCalls: 0,
|
||||
}));
|
||||
|
||||
vi.mock("child_process", async () => {
|
||||
const actual = await vi.importActual<typeof import("child_process")>("child_process");
|
||||
return {
|
||||
...actual,
|
||||
spawn: (...args: Parameters<typeof actual.spawn>) => {
|
||||
const [command, commandArgs] = args;
|
||||
if (command === "git" && Array.isArray(commandArgs)) {
|
||||
const normalizedArgs = commandArgs.map((arg) => String(arg));
|
||||
// `runGitCommand` always prepends its two config overrides; skip them
|
||||
// to find the actual git subcommand.
|
||||
const subcommandIndex =
|
||||
normalizedArgs[0] === "-c" && normalizedArgs[1] === "core.quotepath=false" ? 4 : 0;
|
||||
const isTrackedTextDiff =
|
||||
normalizedArgs[subcommandIndex] === "diff" &&
|
||||
normalizedArgs.includes("HEAD") &&
|
||||
!normalizedArgs.includes("--numstat") &&
|
||||
!normalizedArgs.includes("--no-index") &&
|
||||
!normalizedArgs.includes("--shortstat") &&
|
||||
!normalizedArgs.includes("--name-status");
|
||||
if (isTrackedTextDiff) {
|
||||
spawnCounters.trackedTextDiffCalls += 1;
|
||||
}
|
||||
}
|
||||
return actual.spawn(...args);
|
||||
},
|
||||
};
|
||||
});
|
||||
import { beforeEach, afterEach, describe, expect, it } from "vitest";
|
||||
import { startGitCommandMetrics, stopGitCommandMetrics } from "@server/utils/run-git-command.js";
|
||||
|
||||
import { getCheckoutDiff } from "./checkout-git.js";
|
||||
|
||||
@@ -70,14 +39,14 @@ describe("checkout git diff batching", () => {
|
||||
const setup = initRepoWithTrackedChanges(20);
|
||||
tempDir = setup.tempDir;
|
||||
repoDir = setup.repoDir;
|
||||
spawnCounters.trackedTextDiffCalls = 0;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(tempDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it("uses per-file tracked git diff commands for tracked file diffs", async () => {
|
||||
it("collects ordinary tracked patches with one bounded Git command", async () => {
|
||||
startGitCommandMetrics();
|
||||
const result = await getCheckoutDiff(repoDir, {
|
||||
mode: "uncommitted",
|
||||
includeStructured: false,
|
||||
@@ -85,6 +54,44 @@ describe("checkout git diff batching", () => {
|
||||
|
||||
expect(result.diff).toContain("file-0.txt");
|
||||
expect(result.diff).toContain("file-19.txt");
|
||||
expect(spawnCounters.trackedTextDiffCalls).toBe(20);
|
||||
const metrics = stopGitCommandMetrics();
|
||||
expect(
|
||||
metrics.commands.filter(({ args }) => args[0] === "diff" && args.includes("--")),
|
||||
).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("batches committed file contents and preserves the highlighted diff", async () => {
|
||||
execFileSync("git", ["checkout", "-b", "feature"], { cwd: repoDir });
|
||||
execFileSync("git", ["add", "."], { cwd: repoDir });
|
||||
execFileSync("git", ["-c", "commit.gpgsign=false", "commit", "-m", "change"], { cwd: repoDir });
|
||||
startGitCommandMetrics();
|
||||
const result = await getCheckoutDiff(repoDir, {
|
||||
mode: "base",
|
||||
baseRef: "main",
|
||||
includeStructured: true,
|
||||
});
|
||||
const metrics = stopGitCommandMetrics();
|
||||
expect(result.structured).toHaveLength(20);
|
||||
expect(result.structured?.every((file) => file.additions === 1 && file.deletions === 1)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(metrics.commands.filter(({ args }) => args[0] === "show")).toHaveLength(0);
|
||||
expect(metrics.commands.filter(({ args }) => args[0] === "cat-file")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("keeps small neighbors when a patch exceeds the entire batch budget", async () => {
|
||||
writeFileSync(join(repoDir, "file-0.txt"), "x".repeat(9 * 1024 * 1024) + "\n");
|
||||
const result = await getCheckoutDiff(repoDir, {
|
||||
mode: "uncommitted",
|
||||
includeStructured: true,
|
||||
});
|
||||
expect(result.structured).toHaveLength(20);
|
||||
expect(result.structured?.find((file) => file.path === "file-0.txt")).toMatchObject({
|
||||
status: "too_large",
|
||||
hunks: [],
|
||||
});
|
||||
expect(result.structured?.filter((file) => file.status === "ok")).toHaveLength(19);
|
||||
expect(result.diff).toContain("+after-19");
|
||||
expect(result.diff).not.toContain("x".repeat(1_000));
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user