Skip to content

fix: pass non-unified-diff input through to stdout (closes #115)#132

Merged
dlvhdr merged 2 commits into
dlvhdr:mainfrom
Booyaka101:fix/git-diff-stat-115
May 16, 2026
Merged

fix: pass non-unified-diff input through to stdout (closes #115)#132
dlvhdr merged 2 commits into
dlvhdr:mainfrom
Booyaka101:fix/git-diff-stat-115

Conversation

@Booyaka101
Copy link
Copy Markdown
Contributor

@Booyaka101 Booyaka101 commented May 9, 2026

Summary

When diffnav is configured as pager.diff (per the README), git pipes the output of every git diff invocation through diffnav, including non-unified-diff variants like git diff --stat, --shortstat, --name-only, and --name-status. Those produce summary text with no diff --git markers, which causes two latent bugs:

  1. Panic. The bluekeyes/go-gitdiff parser returns zero files, the TUI tries to render a scrollbar over an empty viewport, and the program panics with runtime error: integer divide by zero in RenderScrollbar (pkg/ui/common/scrollbar.go:18).
  2. Escape-sequence leak. Before the panic, bubbletea's terminal-capability handshake leaks [?2026\$p[?2027\$p query bytes to the user's stdout — the symptom in the bug report.

Fix

Detect non-unified-diff input early in cmd/root.go (after stdin is fully read, before opening the TTY) and pass it through verbatim. The caller sees the same output they would have without diffnav as the configured pager — same behaviour as cat/the implicit pager-fallthrough that other TUI pagers use for non-diff input.

Detection is a strings.Contains(input, "diff --git ") check, extracted into a small isUnifiedDiff helper for unit testing. Both git diff and git show always emit at least one diff --git header in the unified form, so the check matches the TUI's actual input contract.

Test plan

  • 8 new unit tests in cmd/input_test.go covering:
    • git diff (unified)
    • git show (preamble + unified)
    • git diff --stat
    • git diff --shortstat
    • git diff --name-only
    • git diff --name-status
    • git log (no patch)
    • empty input
  • Empirically reproduced on Windows with the canonical --stat output:
    • Pre-fix: [?2026\$p[?2027\$p bytes leak, then runtime error: integer divide by zero panic.
    • Post-fix: stat output is printed verbatim and diffnav exits 0.
  • Real unified-diff input still routes to the TUI (verified by piping gh_dash_pr.diff test fixture into the post-fix binary — TUI starts, hits the same pre-existing panic the upstream test environment hits, confirming the input is reaching the renderer).

Notes

  • The two pre-existing pkg/ui/panes/filetree test failures on main (TestNoLastPath, TestCollapseTree) are unrelated lipgloss-styling differences and not touched by this PR.
  • This is a strict superset: any input that previously rendered now still renders; only inputs the renderer would have crashed on now exit gracefully.
  • Closes Causes git diff --stat to not work #115. Should also fix the git log symptom from Does not work with git log #28 (re-emerged after the recent v2 rewrite, since git log without -p produces no diff --git line and would hit the same path).

AI usage disclosure (per AI_POLICY.md)

Tool: Claude Code (Opus 4.7).
Extent: pattern analysis (root cause for the panic + escape leak), code drafting for the isUnifiedDiff helper and the early-return path, and the test cases. I reviewed every change manually, ran go build, go test ./cmd/..., and reproduced the pre-/post-fix behaviour locally on Windows before pushing. Happy to walk through any line on request.

When diffnav is configured as `pager.diff` (per the README), git pipes
the output of every `git diff` invocation through diffnav, including
non-unified-diff variants like `git diff --stat`, `--shortstat`,
`--name-only`, and `--name-status`. Those produce summary text with no
`diff --git ` markers, which causes two latent bugs:

1. The `bluekeyes/go-gitdiff` parser returns zero files, the TUI tries
   to render a scrollbar over an empty viewport, and the program panics
   with `runtime error: integer divide by zero` in `RenderScrollbar`
   (`pkg/ui/common/scrollbar.go:18`).
2. Before the panic, bubbletea's terminal-capability handshake leaks
   `[?2026$p[?2027$p` query bytes to the user's stdout — the symptom in
   the bug report.

Detect non-unified-diff input early in `cmd/root.go` (after stdin is
fully read, before opening the TTY) and pass it through verbatim, so
the caller sees the same output they would have without diffnav as the
configured pager.

Detection is a simple `strings.Contains(input, "diff --git ")` check,
extracted into `isUnifiedDiff` for unit testing. Both `git diff` and
`git show` always emit at least one `diff --git ` header in the unified
form, so this matches the renderer's actual input contract. Eight unit
tests cover the unified-diff happy path (`diff`, `show`), the four
summary forms (`--stat`, `--shortstat`, `--name-only`, `--name-status`),
patch-less `git log`, and empty input.

Empirically reproduced on Windows (`go build` + piping the canonical
`--stat` output). Pre-fix: terminal-capability bytes leak then divide-
by-zero panic. Post-fix: stat output is printed verbatim and diffnav
exits 0. Real unified-diff input still routes to the TUI as before.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Owner

@dlvhdr dlvhdr left a comment

Choose a reason for hiding this comment

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

thank you!

Comment thread cmd/root.go Outdated
Comment on lines +205 to +215

// When configured as `pager.diff`, git pipes the output of every
// `git diff` invocation here, including non-unified-diff variants
// like `git diff --stat`, `--shortstat`, `--name-only`, and
// `--name-status`. Those produce summary text with no `diff --git`
// markers, which (a) gives the TUI zero files and panics with a
// divide-by-zero in the scrollbar renderer, and (b) leaks
// terminal-capability query bytes from bubbletea init back to the
// user's prompt. Pass such input straight through to stdout so the
// caller sees the same output they would have without diffnav as
// the configured pager.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Let's remove this explanation

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — removed in 0b8101b. isUnifiedDiff's godoc already covers the intent.

The isUnifiedDiff helper already documents the intent; the explanation
is repeated in the PR description for context.
Copy link
Copy Markdown
Owner

@dlvhdr dlvhdr left a comment

Choose a reason for hiding this comment

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

thank you!

@dlvhdr dlvhdr merged commit a2722c2 into dlvhdr:main May 16, 2026
3 checks passed
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.

Causes git diff --stat to not work

2 participants