From 41b94a3ea1306df8e28b8e09808e4e1d4a2bfd62 Mon Sep 17 00:00:00 2001 From: Anirudh Oppiliappan Date: Thu, 29 Oct 2026 17:08:12 +0200 Subject: [PATCH] web/repo: diff multi-commit ranges from the combined diff, not format-patch format-patch serves one patch per commit, oldest first, and the diff views key the parsed files by name, so a file touched by several commits kept only the newest commit's hunks. a pull's "All changes" (and any range of more than one commit, on the pull, compare and compose pages) showed an incomplete diff: on tangled.org/core, spindle/repos.go across the last four master commits showed 16 changed lines instead of 27. those ranges now skip the patch and take their files from the combined diff; a single commit still streams its patch. --- web/src/lib/api/patchStream.ts | 4 ++++ web/src/lib/components/repo/CompareView.svelte | 3 ++- web/src/lib/components/repo/pulls/PullCompose.svelte | 3 ++- .../[repo]/pulls/[rkey]/[version]/[[range]]/+page.ts | 11 ++++++++++- .../[rkey]/[version]/[[range]]/PullViewPage.svelte | 6 +++++- 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/web/src/lib/api/patchStream.ts b/web/src/lib/api/patchStream.ts index 1a99e2561..6687ade0e 100644 --- a/web/src/lib/api/patchStream.ts +++ b/web/src/lib/api/patchStream.ts @@ -5,6 +5,10 @@ import { streamPatchInWorker } from "#lib/components/repo/diffRpc.ts"; * A patch being parsed in the worker while it downloads. Starting one does not * wait for anything else on the page: the file list and the patch describe the * same commit, and fetching them one after the other costs the sum of both. + * + * Only ever for a single commit: format-patch serves one patch per commit and + * files are keyed by name, so over a longer range a file several commits touch + * keeps one commit's hunks. Longer ranges take the combined diff instead. */ export interface PatchStream { /** parsed files, keyed by current name and by previous name for renames */ diff --git a/web/src/lib/components/repo/CompareView.svelte b/web/src/lib/components/repo/CompareView.svelte index 5eb2264ae..2c3883342 100644 --- a/web/src/lib/components/repo/CompareView.svelte +++ b/web/src/lib/components/repo/CompareView.svelte @@ -99,8 +99,9 @@ return await loadDiff(diffDeps, diffSpec); }); + // one commit only, see PatchStream const patchUrl = $derived( - hasChanges && diffSpec.version.base + hasChanges && diffSpec.version.base && commitRange.end === commitRange.start ? formatPatchUrl(diffDeps.git.ctx.serviceUrl, { repo: repo.repoDid ?? "", from: diffSpec.version.base, diff --git a/web/src/lib/components/repo/pulls/PullCompose.svelte b/web/src/lib/components/repo/pulls/PullCompose.svelte index c5b6a766a..89005b6fc 100644 --- a/web/src/lib/components/repo/pulls/PullCompose.svelte +++ b/web/src/lib/components/repo/pulls/PullCompose.svelte @@ -226,8 +226,9 @@ return await loadDiff(diffDeps, diffSpec); }); + // one commit only, see PatchStream const patchUrl = $derived( - hasChanges && diffSpec.version.base + hasChanges && diffSpec.version.base && commitRange.end === commitRange.start ? formatPatchUrl(diffDeps.git.ctx.serviceUrl, { repo: view.sourceRepoDid, from: diffSpec.version.base, diff --git a/web/src/routes/[handle]/[repo]/pulls/[rkey]/[version]/[[range]]/+page.ts b/web/src/routes/[handle]/[repo]/pulls/[rkey]/[version]/[[range]]/+page.ts index 98a14513f..28666a0b7 100644 --- a/web/src/routes/[handle]/[repo]/pulls/[rkey]/[version]/[[range]]/+page.ts +++ b/web/src/routes/[handle]/[repo]/pulls/[rkey]/[version]/[[range]]/+page.ts @@ -52,11 +52,20 @@ export const load: PageLoad = async (event) => { // downloads alongside the file list instead of after it - the component // picks up whichever files have already parsed by the time it mounts. // an interdiff's left side is a tree the mirror rebases in memory, - // reachable by no ref, so only a plain version diff has a patch to stream + // reachable by no ref, so only a plain version diff has a patch to stream, + // and only for one commit (see PatchStream) + const rowOf = (hash: string, missing: number) => { + const row = commits.findIndex((commit) => commit.hash === hash); + return row === -1 ? missing : row; + }; + const oneCommit = + route.mode === "diff" && + rowOf(route.base, commits.length) - rowOf(route.head, 0) === 1; if ( browser && !versionPage.commitsFailed && route.mode === "diff" && + oneCommit && route.base && route.head && repo.repoDid diff --git a/web/src/routes/[handle]/[repo]/pulls/[rkey]/[version]/[[range]]/PullViewPage.svelte b/web/src/routes/[handle]/[repo]/pulls/[rkey]/[version]/[[range]]/PullViewPage.svelte index c35203c3c..16eea610d 100644 --- a/web/src/routes/[handle]/[repo]/pulls/[rkey]/[version]/[[range]]/PullViewPage.svelte +++ b/web/src/routes/[handle]/[repo]/pulls/[rkey]/[version]/[[range]]/PullViewPage.svelte @@ -389,8 +389,12 @@ ); // Interdiff left trees exist only in mirror memory; format-patch requires a ref. + // One commit only, see PatchStream. const patchUrl = $derived( - !data.commitsFailed && diffSpec.kind === "diff" && data.repo.repoDid + !data.commitsFailed && + diffSpec.kind === "diff" && + commitRange.end === commitRange.start && + data.repo.repoDid ? formatPatchUrl(diffDeps.git.ctx.serviceUrl, { repo: (diffSpec.version.repo ?? data.repo.repoDid) as Did, from: diffSpec.version.base, -- knotmirror 0.0.1