Skip to content

feat(git): review branch changes in a Changes view with read-only diff tabs - #9

Merged
jkyberneees merged 1 commit into
mainfrom
feat/changes-review
Oct 29, 2026
Merged

jkyberneees merged 1 commit into
mainfrom
feat/changes-review

Conversation

@jkyberneees

Copy link
Copy Markdown
Contributor

Summary

  • Changes view (third sidebar tab, ⌘⇧G): every file the branch touched since it left its base branch (working tree included), or only the uncommitted ones, with +/− per file, a one-line summary with a proportional bar and the commit count. Surprises sort first (conflicts, deletions, new files); reviewed rows dim until the file changes again. The scope control appears only when a base branch exists and HEAD has commits on top of it.
  • Diff tabs: a virtualized, syntax-highlighted unified diff per file with two line-number columns, collapsed context and expandable gaps; ⌥F5/⌥⇧F5 walk the hunks and move on to the next/previous file; deleted and renamed files open; "Open file" jumps to the live file at its first change. Live: tabs re-diff when the file or the base moves.
  • ⌘⇧G and ⌥F5 were advertised in the README and palette but never bound; they are now. The palette's changed-files list is replaced by the view.
  • Server: /api/git/changes?scope= (git diff --raw --numstat -z -M, untracked files with streamed line counts, conflicts from ls-files --unmerged, base-branch detection via origin/HEAD → main/master/develop/trunk → upstream, 1,000-file cap) and a validated full-oid rev on /api/git/base.

Design and review

DESIGN.md §15 records the three-panelist design round (interaction, visual, developer-experience) and the adversarial review that followed. Confirmed and fixed, each with a regression test:

  • hostile .git/config (diff.orderFile, mailmap.file → /dev/zero) could hang git without bound: -O/dev/null, --no-mailmap, mailmap/bigFileThreshold overrides in HARDEN, 30 s kill timer on every git run
  • diff.relative=true blanked the list in a subfolder view: --no-relative
  • merge conflicts never appeared (git diff <commit> never emits unmerged entries)
  • entries over the cap lost their counts; stale diff-tab status when a file left the list; git rm --cached duplicates; submodules; orphan branch; EOL-only changes
  • no coalescing of concurrent changes runs; old blob refetched on every live reload

Test plan

  • bun test: 1165 pass (new: servergit.changes, http.changes, client.changes, client.diffview)
  • Screenshots of the view, diff tab, hunk stepping across files and light theme via a WebKit snapshot of the running app
  • Rebased on 1.6.1; diff tabs get the new dispose() lifecycle

🤖 Generated with Claude Code

…f tabs

A third sidebar view lists every file the branch touched since it left its base branch
(working tree included) or only the uncommitted ones, with +/− per file, a one-line summary
and a commit count. Each file opens as a virtualized unified diff with collapsed context;
⌥F5 walks the hunks and moves on to the next file; reviewed rows dim until the file changes
again. ⌘⇧G and ⌥F5 are now actually bound (they were advertised but never wired).

Server: /api/git/changes (git diff --raw --numstat -z -M, untracked files with line counts,
conflicts from ls-files --unmerged, base-branch detection, 1,000-file cap) and a validated
`rev` on /api/git/base. Hardened after an adversarial review: -O/dev/null, --no-relative,
--no-mailmap and mailmap/bigFileThreshold overrides against hostile configs, a 30 s kill
timer on every git run, coalesced concurrent runs, streamed untracked counting.

Design round and review table in DESIGN.md §15.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@jkyberneees
jkyberneees merged commit 0a3c2c6 into main Oct 29, 2026
4 of 5 checks passed
@jkyberneees
jkyberneees deleted the feat/changes-review branch October 29, 2026 19:23
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