Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.

Commit b5907d7

Browse files
authored
refactor(code-review): share tree traversal order, drop re-export indirection
Cleanup pass on the diff-review-matches-file-tree change: - Extract orderedTreeDirs/orderedTreeFiles into the shared changeTree helper and have both flattenNode (review ordering) and ChangesTreeView (the rendered tree) consume them. The two surfaces now share one traversal, so they can't drift on sort order again — the original bug. - Drop the orderPathsLikeTree/sortReviewItemsByTreeOrder re-export aliases from reviewItemBuilders; ReviewPage imports the core helpers directly. Removes a layer of indirection with no consumer relying on the renamed names. - Drop the unused TreeNode/buildChangesTree/compactTree back-compat aliases from ChangesTreeView; it uses the core names directly. Net -73 lines; behavior unchanged. Generated-By: PostHog Code Task-Id: 4eb23888-6afa-4fb9-9b7a-22d6a48e1721
1 parent 725e73d commit b5907d7

5 files changed

Lines changed: 30 additions & 103 deletions

File tree

‎packages/core/src/git-interaction/changeTree.ts‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,25 @@ export function compactChangeTree(node: ChangeTreeNode): ChangeTreeNode {
5555

5656
const compareLocale = (a: string, b: string) => a.localeCompare(b);
5757

58-
function flattenNode(node: ChangeTreeNode, out: ChangedFile[]) {
59-
const sortedDirs = [...node.children.values()].sort((a, b) =>
58+
export function orderedTreeDirs(node: ChangeTreeNode): ChangeTreeNode[] {
59+
return [...node.children.values()].sort((a, b) =>
6060
compareLocale(a.name, b.name),
6161
);
62-
const sortedFiles = [...node.files].sort((a, b) => {
62+
}
63+
64+
export function orderedTreeFiles(node: ChangeTreeNode): ChangedFile[] {
65+
return [...node.files].sort((a, b) => {
6366
const aName = a.path.split("/").pop() ?? "";
6467
const bName = b.path.split("/").pop() ?? "";
6568
return compareLocale(aName, bName);
6669
});
70+
}
6771

68-
for (const child of sortedDirs) {
72+
function flattenNode(node: ChangeTreeNode, out: ChangedFile[]) {
73+
for (const child of orderedTreeDirs(node)) {
6974
flattenNode(child, out);
7075
}
71-
for (const file of sortedFiles) {
76+
for (const file of orderedTreeFiles(node)) {
7277
out.push(file);
7378
}
7479
}

‎packages/ui/src/features/code-review/components/ReviewPage.tsx‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import type { parsePatchFiles } from "@pierre/diffs";
22
import type { ResolvedDiffSource } from "@posthog/core/code-review/resolveDiffSource";
33
import type { PrCommentThread } from "@posthog/core/code-review/types";
4+
import {
5+
orderPathsLikeChangeTree,
6+
sortByChangeTreeOrder,
7+
} from "@posthog/core/git-interaction/changeTree";
48
import { useHostTRPC } from "@posthog/host-router/react";
59
import type { ChangedFile, Task } from "@posthog/shared/domain-types";
610
import { Flex, Text } from "@radix-ui/themes";
@@ -33,9 +37,7 @@ import {
3337
buildRemoteReviewItems,
3438
buildUntrackedReviewItems,
3539
changedFileSignature,
36-
orderPathsLikeTree,
3740
patchFileSignature,
38-
sortReviewItemsByTreeOrder,
3941
} from "./reviewItemBuilders";
4042

4143
const EMPTY_CHANGED_FILES: ChangedFile[] = [];
@@ -348,7 +350,7 @@ function LocalReviewContent({
348350

349351
const stagedItems = useMemo(
350352
() =>
351-
sortReviewItemsByTreeOrder(
353+
sortByChangeTreeOrder(
352354
buildPatchReviewItems({
353355
files: stagedParsedFiles,
354356
staged: true,
@@ -363,7 +365,7 @@ function LocalReviewContent({
363365
prUrl,
364366
commentThreads,
365367
}),
366-
orderPathsLikeTree(
368+
orderPathsLikeChangeTree(
367369
stagedParsedFiles.map((f) => f.name ?? f.prevName ?? ""),
368370
),
369371
),
@@ -384,7 +386,7 @@ function LocalReviewContent({
384386

385387
const changesItems = useMemo(
386388
() =>
387-
sortReviewItemsByTreeOrder(
389+
sortByChangeTreeOrder(
388390
[
389391
...buildPatchReviewItems({
390392
files: unstagedParsedFiles,
@@ -411,7 +413,7 @@ function LocalReviewContent({
411413
onStageFile,
412414
}),
413415
],
414-
orderPathsLikeTree([
416+
orderPathsLikeChangeTree([
415417
...unstagedParsedFiles.map((f) => f.name ?? f.prevName ?? ""),
416418
...untrackedFiles.map((f) => f.path),
417419
]),
@@ -552,7 +554,7 @@ function RemoteReviewPage({
552554

553555
const items = useMemo(
554556
() =>
555-
sortReviewItemsByTreeOrder(
557+
sortByChangeTreeOrder(
556558
buildRemoteReviewItems({
557559
files,
558560
taskId,
@@ -562,7 +564,7 @@ function RemoteReviewPage({
562564
toggleFile: reviewState.toggleFile,
563565
commentThreads,
564566
}),
565-
orderPathsLikeTree(files.map((f) => f.path)),
567+
orderPathsLikeChangeTree(files.map((f) => f.path)),
566568
),
567569
[
568570
commentThreads,

‎packages/ui/src/features/code-review/components/reviewItemBuilders.test.ts‎

Lines changed: 1 addition & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
import type { ChangedFile } from "@posthog/shared/domain-types";
22
import { describe, expect, it } from "vitest";
3-
import type { ReviewListItem } from "../commentFileFilter";
4-
import {
5-
changedFileSignature,
6-
orderPathsLikeTree,
7-
patchFileSignature,
8-
sortReviewItemsByTreeOrder,
9-
} from "./reviewItemBuilders";
3+
import { changedFileSignature, patchFileSignature } from "./reviewItemBuilders";
104

115
const changedFile = (over: Partial<ChangedFile>): ChangedFile => ({
126
path: "a.ts",
@@ -81,58 +75,3 @@ describe("patchFileSignature", () => {
8175
expect(a).not.toBe(b);
8276
});
8377
});
84-
85-
describe("orderPathsLikeTree", () => {
86-
it("orders paths like the file tree, not git byte order", () => {
87-
expect(
88-
orderPathsLikeTree([
89-
"Beta.txt",
90-
"ZETA_CAPS.txt",
91-
"alpha.txt",
92-
"src/Apple.ts",
93-
"src/beta.ts",
94-
"zeta.txt",
95-
]),
96-
).toEqual([
97-
"src/Apple.ts",
98-
"src/beta.ts",
99-
"alpha.txt",
100-
"Beta.txt",
101-
"ZETA_CAPS.txt",
102-
"zeta.txt",
103-
]);
104-
});
105-
});
106-
107-
describe("sortReviewItemsByTreeOrder", () => {
108-
const item = (key: string, filePaths: string[]): ReviewListItem => ({
109-
key,
110-
filePaths,
111-
node: null,
112-
});
113-
114-
it("reorders file items to match the given tree order", () => {
115-
const items = [
116-
item("a", ["zeta.txt"]),
117-
item("b", ["alpha.txt"]),
118-
item("c", ["src/x.ts"]),
119-
];
120-
const ordered = sortReviewItemsByTreeOrder(items, [
121-
"src/x.ts",
122-
"alpha.txt",
123-
"zeta.txt",
124-
]);
125-
expect(ordered.map((i) => i.key)).toEqual(["c", "b", "a"]);
126-
});
127-
128-
it("keeps items whose path is not in the order last", () => {
129-
const items = [item("a", ["unknown.ts"]), item("b", ["alpha.txt"])];
130-
const ordered = sortReviewItemsByTreeOrder(items, ["alpha.txt"]);
131-
expect(ordered.map((i) => i.key)).toEqual(["b", "a"]);
132-
});
133-
134-
it("returns the same array when there is no tree order", () => {
135-
const items = [item("a", ["zeta.txt"]), item("b", ["alpha.txt"])];
136-
expect(sortReviewItemsByTreeOrder(items, [])).toBe(items);
137-
});
138-
});

‎packages/ui/src/features/code-review/components/reviewItemBuilders.tsx‎

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,12 @@ import {
55
computeSkipExpansion,
66
} from "@posthog/core/code-review/reviewItemKeys";
77
import type { PrCommentThread } from "@posthog/core/code-review/types";
8-
import {
9-
orderPathsLikeChangeTree,
10-
sortByChangeTreeOrder,
11-
} from "@posthog/core/git-interaction/changeTree";
128
import type { ChangedFile } from "@posthog/shared/domain-types";
139
import { makeFileKey } from "../../git-interaction/utils/fileKey";
1410
import type { ReviewListItem } from "../commentFileFilter";
1511
import type { DiffOptions } from "../types";
1612
import { PatchRow, RemoteRow, UntrackedRow } from "./ReviewRows";
1713

18-
export const orderPathsLikeTree = orderPathsLikeChangeTree;
19-
export const sortReviewItemsByTreeOrder = sortByChangeTreeOrder<ReviewListItem>;
20-
2114
export function changedFileSignature(file: ChangedFile): string | null {
2215
if (file.patch) return contentHash(file.patch);
2316
if (file.sha) return `${file.status}:${file.sha}`;

‎packages/ui/src/features/task-detail/components/ChangesTreeView.tsx‎

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,15 @@ import {
22
buildChangeTree,
33
type ChangeTreeNode,
44
compactChangeTree,
5+
orderedTreeDirs,
6+
orderedTreeFiles,
57
} from "@posthog/core/git-interaction/changeTree";
68
import type { ChangedFile } from "@posthog/shared/domain-types";
79
import { TreeDirectoryRow } from "@posthog/ui/primitives/TreeDirectoryRow";
810
import { useCallback, useMemo, useState } from "react";
911

10-
export type TreeNode = ChangeTreeNode;
11-
12-
export const buildChangesTree = buildChangeTree;
13-
export const compactTree = compactChangeTree;
14-
1512
interface ChangesTreeNodeProps {
16-
node: TreeNode;
13+
node: ChangeTreeNode;
1714
depth: number;
1815
collapsedDirs: Set<string>;
1916
onToggleDir: (path: string) => void;
@@ -28,20 +25,8 @@ function ChangesTreeNode({
2825
renderFile,
2926
}: ChangesTreeNodeProps) {
3027
const isCollapsed = collapsedDirs.has(node.path);
31-
const sortedDirs = useMemo(
32-
() =>
33-
[...node.children.values()].sort((a, b) => a.name.localeCompare(b.name)),
34-
[node.children],
35-
);
36-
const sortedFiles = useMemo(
37-
() =>
38-
[...node.files].sort((a, b) => {
39-
const aName = a.path.split("/").pop() || "";
40-
const bName = b.path.split("/").pop() || "";
41-
return aName.localeCompare(bName);
42-
}),
43-
[node.files],
44-
);
28+
const sortedDirs = useMemo(() => orderedTreeDirs(node), [node]);
29+
const sortedFiles = useMemo(() => orderedTreeFiles(node), [node]);
4530

4631
return (
4732
<>
@@ -80,7 +65,10 @@ interface ChangesTreeViewProps {
8065
}
8166

8267
export function ChangesTreeView({ files, renderFile }: ChangesTreeViewProps) {
83-
const tree = useMemo(() => compactTree(buildChangesTree(files)), [files]);
68+
const tree = useMemo(
69+
() => compactChangeTree(buildChangeTree(files)),
70+
[files],
71+
);
8472
const [collapsedDirs, setCollapsedDirs] = useState<Set<string>>(new Set());
8573

8674
const handleToggleDir = useCallback((path: string) => {

0 commit comments

Comments
 (0)