Skip to content

feat: commit-info popup (i hotkey) for git, hg, and jj#119

Merged
umputun merged 5 commits intomasterfrom
commit-info-popup
Apr 18, 2026
Merged

feat: commit-info popup (i hotkey) for git, hg, and jj#119
umputun merged 5 commits intomasterfrom
commit-info-popup

Conversation

@umputun
Copy link
Copy Markdown
Owner

@umputun umputun commented Apr 18, 2026

Adds a read-only popup overlay that shows the commits included in the current ref-based diff range, opened with the i hotkey. Helps reviewers read the narrative context of what the changes are supposed to do — subject and full body per commit — without leaving revdiff.

Supported for git, hg, and jj. Gated to ref-based reviews only; in uncommitted, --staged, --stdin, --only <file>, or --all-files modes the hotkey is a no-op with a transient status-bar hint "no commits in this mode". Results capped at 500 commits with a truncation indicator in the popup title.

Design

  • Capability interface diff.CommitLogger separate from diff.Renderer (filters and readers don't need to implement it)
  • Consumer-side commitLogSource in app/ui/model.go, injected via ModelConfig with typed-nil guard, nil defaults to type-asserting the renderer
  • CommitsApplicable computed in the composition root from ref + mode flags; Model does not re-derive
  • Lazy fetch on first i, cached for the session
  • Manual rendering in the new commitInfoOverlay — no markdown library; hash in accent, subject bold, author/date muted, body default fg, all via theme tokens (no hex literals)
  • Word wrap via ansi.Wrap, scrollable pager (j/k, PgDn/PgUp, g/G), mutual exclusion via overlay.Manager
  • ANSI-escape bytes stripped in the VCS parser layer to neutralize injection via commit messages

What's in each commit

  • 244fccd — implementation plan
  • 907cb7adiff.CommitInfo, diff.CommitLogger, git/hg/jj implementations with testdata, parser tests, hg/jj e2e smoke tests
  • 413cfdd — keymap action, style key, overlay rendering with wrap/scroll, Model wiring, handler, applicability gate
  • 6ba67cf — README, CLAUDE.md, ARCHITECTURE.md, site/docs.html, .claude-plugin and plugins/codex references (byte-identical sync)

umputun added 4 commits April 18, 2026 02:59
captures the design (capability interface, overlay, lazy fetch, i hotkey,
git/hg/jj support) and task breakdown that guided the implementation.
stored under docs/plans/completed since all tasks are done.
introduces the CommitInfo struct and CommitLogger capability interface,
with implementations for all three VCS renderers.

- git: git log -z --format=... with NUL record separator; ref form
  "" (noop), "X" (X..HEAD), "X..Y" (as-is)
- hg: hg log -r <revset> --template ...; revset form X::., X::Y - X
- jj: jj log --no-graph -T <template> -r <revset>; revset form X..@, X..Y

parser strips raw \x1b bytes from subject/body to neutralize ANSI
injection, caps results at 500 commits per range, and wraps CLI failures
with fmt.Errorf. testdata files cover empty, single, many, no-body, and
tricky (CJK, tabs, ANSI-looking) cases. hg and jj also get e2e smoke
tests exercising real binaries.
adds a read-only pager overlay that shows the commits included in the
current ref-based diff range, triggered by the i hotkey. helps reviewers
read the narrative context of what the changes are supposed to do
without leaving revdiff.

- keymap: ActionCommitInfo bound to i by default, rebindable
- style: StyleKeyCommitInfoBox registered in resolver (accent border
  matches help/annotlist/themeselect)
- overlay: commitInfoOverlay with manual rendering, word wrap via
  ansi.Wrap, scroll via j/k PgDn/PgUp g/G, error and empty states,
  truncated-range indicator; sizing clamped at 90 cols wide
- model: consumer-side commitLogSource interface (moq-generated mock)
  with ModelConfig.CommitLog injection point and CommitsApplicable gate
  computed in main.go from ref + mode flags. lazy fetch on first open,
  session-wide cache, typed-nil guard on the config field
- handler: i dispatch opens overlay when applicable and logger present,
  shows transient status-bar hint "no commits in this mode" otherwise
- renderer_setup wires the CommitLogger capability through setup
- README.md: i hotkey in key bindings table, commit-info popup in
  features list
- docs/ARCHITECTURE.md: commitinfo.go in file-by-file breakdown,
  CommitLogger capability interface in the Renderer discussion
- CLAUDE.md: note the capability-interface pattern and new overlay in
  the overlay.Manager gotcha
- site/docs.html: i hotkey in bindings table, feature note
- .claude-plugin and plugins/codex references: usage.md and config.md
  updates, kept byte-identical per the revdiff docs-sync rule
Copilot AI review requested due to automatic review settings April 18, 2026 08:06
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new read-only “commit info” overlay (hotkey i) to show the commit subject + body for the current ref-based diff range, across git/hg/jj, integrated into the UI overlay system and keymap.

Changes:

  • Introduces diff.CommitLogger / diff.CommitInfo and implements commit log extraction + sanitization for git, hg, and jj (with unit + e2e coverage where applicable).
  • Adds a new commitInfoOverlay with wrapping + scroll navigation, wires it through the overlay manager, model, and keymap (commit_info action bound to i).
  • Updates user-facing docs (README/site/plugin references) and internal architecture/plan docs.

Reviewed changes

Copilot reviewed 37 out of 46 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
site/docs.html Documents the new i keybinding and commit_info action.
plugins/codex/skills/revdiff/references/usage.md Syncs keybinding docs for i / commit info popup.
plugins/codex/skills/revdiff/references/config.md Updates plugin config docs and action list to include commit_info.
docs/plans/completed/2026-04-17-commit-info-popup.md Adds completed implementation plan and rationale/details.
docs/ARCHITECTURE.md Documents CommitLogger capability + commit-info overlay wiring and sizing.
app/ui/view.go Displays transient “no commits in this mode” hint via status bar.
app/ui/style/style_key_enum.go Adds StyleKeyCommitInfoBox enum + parsing/iteration entries.
app/ui/style/resolver_test.go Adds coverage ensuring StyleKeyCommitInfoBox resolves correctly.
app/ui/style/resolver.go Defines styles for StyleKeyCommitInfoBox (colored + plain).
app/ui/style/enums.go Extends internal style-key enum with commit-info box key.
app/ui/overlay/overlay_test.go Adds open/close coverage for the commit-info overlay kind.
app/ui/overlay/overlay.go Adds KindCommitInfo, CommitInfoSpec, and manager open/dispatch support.
app/ui/overlay/commitinfo.go Implements commit-info overlay rendering, wrapping, and scrolling.
app/ui/overlay/commitinfo_test.go Adds unit tests for render states, wrapping, scrolling, and close behavior.
app/ui/model.go Adds commit-log source resolution, cached lazy loading, and overlay open handler.
app/ui/model_test.go Adds tests for commit-log source resolution and lazy-load caching behavior.
app/ui/mocks/commit_log_source.go Adds generated moq mock for commitLogSource.
app/ui/handlers_test.go Adds integration tests for i key behavior (open/close/hint/cache/error).
app/renderer_setup.go Threads a commitLogger capability out of VCS setup for composition root use.
app/renderer_setup_test.go Tests commitsApplicable and asserts git/hg/jj implement CommitLogger.
app/main.go Wires commit logger + applicability gate into ui.ModelConfig; adds commitsApplicable.
app/keymap/keymap.go Adds ActionCommitInfo / default binding i / description entry.
app/keymap/keymap_test.go Verifies commit_info validity, default binding, and dump/parse round-trip.
app/diff/diff.go Adds CommitInfo, CommitLogger, sanitization helpers, and git CommitLog implementation.
app/diff/diff_test.go Adds extensive parser/sanitizer and git CommitLog coverage (fixtures + e2e-style tests).
app/diff/hg.go Adds hg CommitLog implementation, revset translation, and parsing.
app/diff/hg_test.go Adds hg CommitLog revset + parser unit tests.
app/diff/hg_e2e_test.go Adds hg CommitLog e2e smoke test against real hg binary.
app/diff/jj.go Adds jj CommitLog implementation, revset translation, and parsing (incl. working-copy filtering).
app/diff/jj_test.go Adds jj CommitLog revset + parser unit tests.
app/diff/jj_e2e_test.go Adds jj CommitLog e2e smoke test against real jj binary.
app/diff/testdata/gitlog_empty.txt Adds git log fixture for empty output.
app/diff/testdata/gitlog_single.txt Adds git log fixture for single commit.
app/diff/testdata/gitlog_many.txt Adds git log fixture for multiple commits.
app/diff/testdata/gitlog_nobody.txt Adds git log fixture for commit with no body.
app/diff/testdata/gitlog_tricky.txt Adds git log fixture for tricky content (CJK/tabs/ANSI injection).
app/diff/testdata/hglog_empty.txt Adds hg log fixture for empty output.
app/diff/testdata/hglog_single.txt Adds hg log fixture for single commit.
app/diff/testdata/hglog_many.txt Adds hg log fixture for multiple commits.
app/diff/testdata/jjlog_empty.txt Adds jj log fixture for empty output.
app/diff/testdata/jjlog_single.txt Adds jj log fixture for single commit.
app/diff/testdata/jjlog_many.txt Adds jj log fixture for multiple commits.
README.md Documents commit-info popup feature and i keybinding.
CLAUDE.md Documents overlay kind + capability interface + applicability gate behavior.
.claude-plugin/skills/revdiff/references/usage.md Syncs keybinding docs for i / commit info popup.
.claude-plugin/skills/revdiff/references/config.md Syncs action list docs to include commit_info.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app/diff/hg.go Outdated
l, r := h.rangeEnds(left, right)
return fmt.Sprintf("%s::%s - %s", l, r, l)
}
return translateRef(ref) + "::."
Comment on lines +183 to +187
return []string{s}
}
wrapped := ansi.Wrap(s, width, "")
return strings.Split(wrapped, "\n")
}
Comment on lines +101 to +105
case !c.spec.Applicable:
return c.centeredMessage("no commits in this mode", innerWidth, true)
case c.spec.Err != nil:
return c.centeredMessage(c.spec.Err.Error(), innerWidth, true)
case len(c.spec.Commits) == 0:
Comment on lines +199 to +203
// applyScroll returns the slice of lines currently visible given the viewport
// height and the overlay's offset. pads with empty lines when content is short.
func (c *commitInfoOverlay) applyScroll(content []string, viewportHeight int) []string {
if viewportHeight <= 0 {
return nil
Comment thread app/diff/jj_e2e_test.go
Comment on lines +117 to +121
cmd.Dir = dir
out, err := cmd.CombinedOutput()
require.NoError(t, err, "jj log for %q failed: %s", rev, string(out))
return string(out)
}
Comment thread app/ui/overlay/overlay.go
Comment on lines +123 to +127
// CommitInfoSpec describes the commit info popup content.
// Applicable is false when the current mode (stdin/staged/only/all-files/no-ref)
// precludes a meaningful commit list; the overlay renders a hint in that case.
// Truncated is true when the commit list was capped at diff.MaxCommits entries.
// Err is a non-nil VCS CommitLog error to surface to the user; takes precedence
Comment thread docs/ARCHITECTURE.md Outdated
- **`helpOverlay`** — two-column keybinding help popup
- **`annotListOverlay`** — scrollable annotation list with cross-file jump
- **`themeSelectOverlay`** — theme picker with fzf-style filter, live swatch preview
- **`commitInfoOverlay`** (`commitinfo.go`) — scrollable read-only pager showing subject + body of every commit in the current ref range. Populated lazily on first `i` press (cached for the session). Sized via `clamp(term_w * 0.8, 30, 80)` × `term_h - 4`, wraps body text at word boundaries using `ansi.Wrap` from `charmbracelet/x/ansi` (ANSI-aware, preserves inline escapes). Renders "no commits in range" / error-italic / "no commits in this mode" placeholders for edge cases.
- hg: single-ref revset `X::.` now excludes X (`X::. - X`), matching
  git's `X..HEAD` and jj's `X..@` single-ref semantics
- commitinfo overlay: reemit SGR across wrapped lines so continuation
  keeps muted/accent color on wrapped meta; flatten multi-line errors
  (runVCS stderr) before centering; clarify applyScroll comment
- jj_e2e_test: TrimSpace the commit_id output so range concat is safe
- overlay.CommitInfoSpec: remove stale "only" from applicability comment
- ARCHITECTURE.md: sync popup sizing (0.9 ratio / 90 max) with code
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Apr 18, 2026

Deploying revdiff with  Cloudflare Pages  Cloudflare Pages

Latest commit: a0a9871
Status: ✅  Deploy successful!
Preview URL: https://ff3bd8e0.revdiff.pages.dev
Branch Preview URL: https://commit-info-popup.revdiff.pages.dev

View logs

@umputun umputun merged commit f9e2ae5 into master Apr 18, 2026
5 checks passed
@umputun umputun deleted the commit-info-popup branch April 18, 2026 08:33
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.

2 participants