From 7a322aac507bec1afd3144bfcb5c897c4eed6f74 Mon Sep 17 00:00:00 2001 From: "paseo-bot[bot]" <266920839+paseo-bot[bot]@users.noreply.github.com> Date: Tue, 10 Nov 2026 18:08:48 +0000 Subject: [PATCH] test(diff): diagnose changing Git interpretation on Windows --- packages/server/src/server/diff/git.test.ts | 10 +++--- packages/server/src/server/diff/inputs.ts | 36 ++++++++++++++++++--- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/packages/server/src/server/diff/git.test.ts b/packages/server/src/server/diff/git.test.ts index 5c77f80e45..6c2f89aa4d 100644 --- a/packages/server/src/server/diff/git.test.ts +++ b/packages/server/src/server/diff/git.test.ts @@ -666,10 +666,12 @@ test("replacing a submodule with a regular file preserves its replacement body", } }); -test.each([ - { ignoreWhitespace: false, after: "export const value = 2;\n", files: ["a.ts"] }, - { ignoreWhitespace: true, after: "export const value = 1;\n", files: [] }, -])( +test.each( + Array.from({ length: process.platform === "win32" ? 30 : 1 }, () => [ + { ignoreWhitespace: false, after: "export const value = 2;\n", files: ["a.ts"] }, + { ignoreWhitespace: true, after: "export const value = 1;\n", files: [] }, + ]).flat(), +)( "interpretation checks ignore content edits but detect new binary attributes, whitespace=$ignoreWhitespace", async ({ ignoreWhitespace, after, files }) => { const cwd = await mkdtemp(join(tmpdir(), "diff-interpretation-check-")); diff --git a/packages/server/src/server/diff/inputs.ts b/packages/server/src/server/diff/inputs.ts index 84a5a7f446..9963451bd7 100644 --- a/packages/server/src/server/diff/inputs.ts +++ b/packages/server/src/server/diff/inputs.ts @@ -3,6 +3,9 @@ import { runGitCommand, type GitCommandOptions } from "../../utils/run-git-comma interface Interpretation { config: string; + configSorted: string; + configBytes: number; + configRecords: number; attributes: Map; fingerprint: string; } @@ -40,14 +43,14 @@ export async function readGitInputs( ); return { identities, - hasInterpretationChanged: interpretationCheck(paths, options.cwd, interpretation.fingerprint), + hasInterpretationChanged: interpretationCheck(paths, options.cwd, interpretation), }; } function interpretationCheck( paths: string[], cwd: string, - baseline: string, + baseline: Interpretation, ): () => Promise { // Retain only the input description, never a comparison's patch/source pages. return async () => { @@ -55,7 +58,21 @@ function interpretationCheck( cwd, envOverlay: { GIT_OPTIONAL_LOCKS: "0" }, }); - return current.fingerprint !== baseline; + const changed = current.fingerprint !== baseline.fingerprint; + if (changed) { + // Temporary CI diagnosis: no paths, configuration keys, or values are logged. + console.warn("Git interpretation changed", { + configChanged: current.config !== baseline.config, + configOrderOnly: current.configSorted === baseline.configSorted, + configBytes: [baseline.configBytes, current.configBytes], + configRecords: [baseline.configRecords, current.configRecords], + attributePaths: [baseline.attributes.size, current.attributes.size], + attributeValues: [baseline, current].map((value) => + [...value.attributes.values()].reduce((sum, attributes) => sum + attributes.length, 0), + ), + }); + } + return changed; }; } @@ -63,7 +80,15 @@ async function readInterpretation( paths: string[], options: GitCommandOptions, ): Promise { - if (!paths.length) return { config: "", attributes: new Map(), fingerprint: "" }; + if (!paths.length) + return { + config: "", + configSorted: "", + configBytes: 0, + configRecords: 0, + attributes: new Map(), + fingerprint: "", + }; const [config, result] = await Promise.all([ runGitCommand(["config", "--null", "--list"], options), runGitCommand(["check-attr", "--all", "-z", "--stdin"], { @@ -86,6 +111,9 @@ async function readInterpretation( const configIdentity = createHash("sha256").update(config.stdout).digest("hex"); return { config: configIdentity, + configSorted: fingerprint(config.stdout.split("\0").sort()), + configBytes: Buffer.byteLength(config.stdout), + configRecords: config.stdout.split("\0").length - 1, attributes, fingerprint: fingerprint([ configIdentity,