From a1294598a56f8d65705d568f1fc674444a334272 Mon Sep 17 00:00:00 2001 From: CS-Bob <31512182+CS-Bob@users.noreply.github.com> Date: Fri, 30 Oct 2026 19:20:48 +0800 Subject: [PATCH] fix(vcs): sync Git Log file tree with diff navigation (#445) Moving between files in a commit's diff (F7 / next file) left the Git Log changed-files tree on the previous file. The tree now follows the active diff when it belongs to this commit, expanding and revealing the file without taking focus or reopening the diff. Co-authored-by: CS-Bob <31512182+CS-Bob@users.noreply.github.com> --- src/features/vcs/GitLog.tsx | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/features/vcs/GitLog.tsx b/src/features/vcs/GitLog.tsx index 9fac5b34..958176d5 100644 --- a/src/features/vcs/GitLog.tsx +++ b/src/features/vcs/GitLog.tsx @@ -18,6 +18,7 @@ import clsx from "clsx"; import { useVirtualizer } from "@tanstack/react-virtual"; import type { Branch, ChangedFile, Commit, RevisionChange } from "@/bindings"; import { call, commands } from "@/platform/ipc"; +import { maybeEditorService, type EditorInput } from "@/workbench/contributions"; import { EmptyState, Icon, @@ -32,6 +33,7 @@ import { fileIconName, showContextMenu, type MenuItem, + type TreeHandle, T, copyText, } from "@/ui"; @@ -76,6 +78,7 @@ import { } from "./branches"; import { chooseDateRange, chooseMany, promptText } from "./dialogs"; import { buildFileTree, dirKeys, type FileTreeNode } from "./fileTree"; +import { DIFF_KIND, diffInput } from "./diffInput"; import { absolutePath, filesUnder, @@ -1236,6 +1239,9 @@ export function CommitDetails({ commits }: { commits: Commit[] }) { const groupByDirectory = useLogOptions((s) => s.groupByDirectory); const [selectedKeys, setSelectedKeys] = useState>(() => new Set()); + const treeRef = useRef(null); + const [diffToReveal, setDiffToReveal] = useState<{ key: string } | null>(null); + const syncingDiff = useRef(false); const changes = useMemo( () => (files && commit ? toLogChanges(files, many ? null : commit, commitRoot) : []), [files, commit, many, commitRoot], @@ -1256,6 +1262,7 @@ export function CommitDetails({ commits }: { commits: Commit[] }) { useEffect(() => () => clearTimeout(previewTimer.current), []); const previewSelection = (sel: ReadonlySet) => { clearTimeout(previewTimer.current); + if (syncingDiff.current) return; if (!logOptions().diffPreview || sel.size !== 1) return; const file = fileNodeAt(tree, [...sel][0]!)?.file; // A preview that cannot open (no editor yet, the file went away) is just skipped. @@ -1263,6 +1270,36 @@ export function CommitDetails({ commits }: { commits: Commit[] }) { previewTimer.current = setTimeout(() => void showChangesDiff([file], true).catch(() => undefined), PREVIEW_DELAY); }; + // Follow only this pane's exact diff (path AND revisions); another commit or + // Compare with Local must not move its selection. Never take focus from F7. + useEffect(() => { + const editor = maybeEditorService(); + const followDiff = (input: EditorInput | null) => { + if (input?.kind !== DIFF_KIND) return; + const change = ordered.find((c) => diffInput(c.diff).key === input.key); + if (!change) return; + clearTimeout(previewTimer.current); + const rel = relTo(root, change.path); + setExpanded((current) => { + const next = new Set(current ?? allDirs); + for (const key of allDirs) if (rel.startsWith(key.slice(2) + "/")) next.add(key); + return next; + }); + setDiffToReveal({ key: `f:${rel}` }); + }; + followDiff(editor?.activeInput?.() ?? null); + return editor?.onActiveInputChange?.(followDiff); + }, [ordered, allDirs, root]); + + // Tree.reveal needs the expanded rows to have rendered. Suppress the reverse + // preview update so it cannot reopen the diff and lose its requested chunk. + useLayoutEffect(() => { + if (!diffToReveal) return; + syncingDiff.current = true; + treeRef.current?.reveal(diffToReveal.key); + syncingDiff.current = false; + }, [diffToReveal]); + useEffect(() => { useLogChanges.setState({ commits: chosen, all: ordered, selected: chosenChanges }); }, [chosen, ordered, chosenChanges]); @@ -1344,6 +1381,7 @@ export function CommitDetails({ commits }: { commits: Commit[] }) { )} {files && ( > + handleRef={treeRef} roots={tree} getKey={(n) => n.key} getLabel={(n) => n.name}