feat: commit-info popup (i hotkey) for git, hg, and jj#119
Merged
Conversation
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
There was a problem hiding this comment.
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.CommitInfoand implements commit log extraction + sanitization for git, hg, and jj (with unit + e2e coverage where applicable). - Adds a new
commitInfoOverlaywith wrapping + scroll navigation, wires it through the overlay manager, model, and keymap (commit_infoaction bound toi). - 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.
| 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 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 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 |
| - **`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
Deploying revdiff with
|
| Latest commit: |
a0a9871
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://ff3bd8e0.revdiff.pages.dev |
| Branch Preview URL: | https://commit-info-popup.revdiff.pages.dev |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a read-only popup overlay that shows the commits included in the current ref-based diff range, opened with the
ihotkey. 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-filesmodes 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
diff.CommitLoggerseparate fromdiff.Renderer(filters and readers don't need to implement it)commitLogSourceinapp/ui/model.go, injected viaModelConfigwith typed-nil guard, nil defaults to type-asserting the rendererCommitsApplicablecomputed in the composition root from ref + mode flags; Model does not re-derivei, cached for the sessioncommitInfoOverlay— no markdown library; hash in accent, subject bold, author/date muted, body default fg, all via theme tokens (no hex literals)ansi.Wrap, scrollable pager (j/k, PgDn/PgUp, g/G), mutual exclusion viaoverlay.ManagerWhat's in each commit
244fccd— implementation plan907cb7a—diff.CommitInfo,diff.CommitLogger, git/hg/jj implementations with testdata, parser tests, hg/jj e2e smoke tests413cfdd— keymap action, style key, overlay rendering with wrap/scroll, Model wiring, handler, applicability gate6ba67cf— README, CLAUDE.md, ARCHITECTURE.md, site/docs.html,.claude-pluginandplugins/codexreferences (byte-identical sync)