From 25663ed00fbe7e93cffb3295d7f8a217d5242ef3 Mon Sep 17 00:00:00 2001 From: r4iju <5772718+r4iju@users.noreply.github.com> Date: Fri, 16 Oct 2026 12:46:02 +0900 Subject: [PATCH] fix(web): diffs that turn a file into a folder no longer crash the file tree A diff that deletes a file (often a symlink) and adds files under the same path as a folder handed Pierre's tree both a file and a directory at one path, and resetPaths threw "Path collides with an existing file". The tree now leaves out the replaced file; the folder's files carry the change, and the diff body still shows the deletion. --- .../components/diffs/diffFileTree.logic.test.ts | 15 +++++++++++++++ .../src/components/diffs/diffFileTree.logic.ts | 8 +++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/diffs/diffFileTree.logic.test.ts b/apps/web/src/components/diffs/diffFileTree.logic.test.ts index 170e84308069..f7d62350f575 100644 --- a/apps/web/src/components/diffs/diffFileTree.logic.test.ts +++ b/apps/web/src/components/diffs/diffFileTree.logic.test.ts @@ -51,6 +51,21 @@ describe("diffFileTreeEntries", () => { }); }); +describe("diffFileTreeEntries", () => { + it("drops a file whose path became a directory in the same diff", () => { + const entries = diffFileTreeEntries([ + file("deleted", "skills/react-composition"), + file("new", "skills/react-composition/SKILL.md"), + file("new", "skills/other.md"), + ]); + expect(entries).toEqual([ + { path: "skills/react-composition/SKILL.md", status: "added" }, + { path: "skills/other.md", status: "added" }, + ]); + expect(() => preloadFileTree({ paths: entries.map((entry) => entry.path) })).not.toThrow(); + }); +}); + describe("collectDirectoryPaths", () => { it("lists every ancestor once, parents first, with Pierre's trailing slash", () => { expect(collectDirectoryPaths(["apps/web/src/a.ts", "apps/web/b.ts", "README.md"])).toEqual([ diff --git a/apps/web/src/components/diffs/diffFileTree.logic.ts b/apps/web/src/components/diffs/diffFileTree.logic.ts index b727c3ee3ffb..b14fc434e92f 100644 --- a/apps/web/src/components/diffs/diffFileTree.logic.ts +++ b/apps/web/src/components/diffs/diffFileTree.logic.ts @@ -27,6 +27,9 @@ function toGitStatus(file: FileDiffMetadata): GitStatus { * Maps parsed diff files to tree entries, keeping the diff's own order. A path * appears once: a type change (regular file to symlink) is a deletion plus an * addition of the same path, and the tree shows the surviving file as modified. + * + * A file whose path is also a directory in the same diff (a file or symlink replaced by a + * folder) is left out: the tree cannot hold both, and the folder's files carry the change. */ export function diffFileTreeEntries( files: ReadonlyArray, @@ -38,7 +41,10 @@ export function diffFileTreeEntries( const previous = statusByPath.get(path); statusByPath.set(path, previous === undefined || previous === status ? status : "modified"); } - return [...statusByPath].map(([path, status]) => ({ path, status })); + const directories = new Set(collectDirectoryPaths([...statusByPath.keys()])); + return [...statusByPath] + .filter(([path]) => !directories.has(`${path}/`)) + .map(([path, status]) => ({ path, status })); } /**