Skip to content

Show per-file details for commits, and stop review from freezing the app - #385

Open
0x92 wants to merge 2 commits into
dcouple:mainfrom
0x92:feature/commit-file-details
Open

Show per-file details for commits, and stop review from freezing the app#385
0x92 wants to merge 2 commits into
dcouple:mainfrom
0x92:feature/commit-file-details

Conversation

@0x92

@0x92 0x92 commented Aug 11, 2026

Copy link
Copy Markdown

Description

Reviewing a session meant reading one combined patch: which files a commit
touched, and how much, was not visible anywhere. Two changes, both in the diff
path.

Per-file detail. Commit history rows expand to a per-file list with status and
line counts, and clicking a file opens that file's diff directly.

The review freeze. Reviewing a large working tree blocked the UI for seconds
at a time. It was real work, not a hang: capturing the working-directory diff
spawned one cat per untracked file and one wc -l per file, all synchronously
on the main thread — 800+ process spawns for a session with many untracked files.
That path is now async and batched, with the file list gathered once instead of
once per consumer.

The parsers for --numstat -z, --name-status -z and porcelain -z are pure
functions with their own tests; the -z format packs XY path into a single
token, which is easy to get wrong and impossible to notice by eye.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Performance improvement

Checklist

  • I have read the CONTRIBUTING.md guidelines
  • My code follows the code style of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • New and existing unit tests pass locally with my changes
  • I have run pnpm typecheck and pnpm lint locally
  • I have tested the Electron app locally with pnpm electron-dev
  • I have added tests that prove my fix is effective or that my feature works

Critical Areas Modified

  • State management/IPC events (sessions:get-commit-files)

Screenshots (if applicable)

image

Additional Notes

Tests: commit file changes (23) and the unified diff parser (11).

frontend/src/utils/parseUnifiedDiff.ts also appears in the commit-graph PR —
same file, same content, needed by both.

These four feature PRs are independent but all add an entry to the same
navigation plumbing (navigationStore's ActiveView, the two sidebar
components, preload.ts, api.ts, electron.d.ts). Whichever lands first,
the others need a small rebase there — no logic overlaps.

Not done: the packaged-build check from CONTRIBUTING. I develop on Windows,
so pnpm build:mac was not run.

0x92 added 2 commits August 11, 2026 11:51
Reviewing a session meant reading one combined patch: which files a commit
touched, and how much, was not visible anywhere. Two changes, both in the
diff path:

- Commit history rows expand to a per-file list with status and line
  counts, and clicking a file opens that file's diff directly
- Reviewing a large working tree no longer blocks the UI

The freeze was real work, not a hang: capturing the working-directory diff
spawned one `cat` per untracked file and one `wc -l` per file, all
synchronously on the main thread — 800+ spawns for a session with many
untracked files, several seconds each pass. The path is now async and
batched, with the file list gathered once instead of per consumer.

The parsers for `--numstat -z`, `--name-status -z` and porcelain `-z` are
pure functions with their own tests; the `-z` format packs `XY path` into a
single token, which is easy to get wrong and impossible to notice by eye.

Tests: commit file changes (23) and the unified diff parser (11).
Two defects, both in the path that inlines untracked files into a
working-tree diff.

The listing used `git ls-files --others --exclude-standard` split on
newlines. Git delimits with newlines there, which a filename may contain,
and quotes anything non-ASCII into a C-style escape — `täst.txt` arrived
as the literal `"t\303\244st.txt"`, a name matching no file on disk, so
the file vanished from the diff and from the stats without a word. The
listing now uses `-z` and is split on NUL, and nothing is trimmed: a
leading or trailing space is part of the name.

Content and line counts were then read with `cat "<worktree>/<file>"` and
`wc -l "<worktree>/<file>"`, built by interpolation. Git allows `$`,
backticks and parentheses in a filename, and inside bash double quotes
those are still syntax: a file named `back`whoami`.txt` in a repository
was enough to run a command, with no interaction beyond opening the
session. Both now go through `fs` — readFile for content, a streamed
newline count for the totals — so a repository-controlled name never
reaches a shell. untrackedFilePath() resolves the worktree-relative name
git reports, going through the UNC mount for a WSL project the same way
gitPlumbingCommands already does.

The performance fix this PR made is kept and improved: capture spawns a
fixed handful of commands whatever the file count, where before it was
one `cat` and one batched `wc` per file. MAX_UNTRACKED_INLINE_FILES and
MAX_UNTRACKED_INLINE_BYTES still bound what is inlined, and a per-file
ceiling stands in for the 1 MB buffer `cat` used to have.

chunkByCommandLength() goes with them — nothing builds a command line out
of paths any more.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant