Help us shape the future of Go! Take the 2026 Go Developer Survey

jj-diff

module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 30, 2026 License: MIT

README

jj-diff

.github/assets/demo.gif

A fast, keyboard-driven TUI for viewing and manipulating diffs in Jujutsu (jj). Move changes between commits, split commits interactively, or use as a drop-in replacement for jj's builtin diff editor.

Features

  • Browse Mode: View diffs with syntax highlighting, search, and fuzzy file finding
  • Interactive Mode: Select and move hunks or individual lines between revisions
  • Diff-Editor Mode: Use with jj split, jj diffedit, jj amend -i, jj squash -i
  • Multi-Split Mode: Tag changes and split commits into multiple focused commits
  • Fast: Handles 5000+ files and 20K+ line diffs instantly
  • Beautiful: Catppuccin themes with syntax highlighting for 100+ languages

Installation

# Build from source
mise install && hk install --mise
mise run build

# Or install with go
go install github.com/kyleking/jj-diff/cmd/jj-diff@latest

Quick Start

Browse Diffs

View and search through changes:

# View working copy changes
jj-diff

# View specific revision
jj-diff -r @-

# Search with '/', fuzzy find with 'f'
Move Changes Between Commits

Interactively select hunks or lines to move:

jj-diff --interactive

# Press 'd' to select destination
# Press Space to select hunks
# Press 'v' for line-level selection
# Press 'a' to apply changes
Use as jj's Diff-Editor

Replace jj's builtin editor:

# ~/.config/jj/config.toml
[ui]
diff-editor = "jj-diff"
diff-instructions = false

Then use with jj commands:

jj split          # Split current commit
jj diffedit       # Edit changes in commit
jj amend -i       # Amend interactively
jj squash -i      # Squash interactively
Split Commits into Multiple Parts

Tag changes and create focused commits:

jj-diff --interactive

# Press 'S' to enter multi-split mode
# Tag hunks with 'a', 'b', 'c', etc.
# Press 'D' to assign tags to commits
# Press 'P' to preview and apply

Keybindings

Press ? for help overlay with all keybindings.

Essential Keys
Key Action
j/k or ↓/↑ Navigate files or scroll diff
Tab Switch focus between file list and diff
n/p Next/previous hunk
/ Search files and content
f Filter files (type to search)
? Show help
q Quit
Interactive Mode
Key Action
d Select destination revision
Space Toggle hunk selection
v Enter visual mode (line selection)
a Apply selected changes
S Toggle multi-split mode
Multi-Split Mode
Key Action
a-z Tag hunk with letter
D Assign tags to commits
P Preview and apply split
Visual Indicators
  • > Current hunk/line
  • [X] Selected hunk
  • [A] Tagged hunk (multi-split)
  • █ Visual selection range
  • • Selected line

Common Workflows

Move a Few Lines to Previous Commit
jj-diff --interactive
# Press 'd', select @-, press Enter
# Navigate to desired hunk with 'n'
# Press 'v' to enter visual mode
# Select lines with 'j/k'
# Press Space to confirm selection
# Press 'a' to apply
Split a Large Commit into Focused Changes
jj-diff --interactive
# Press 'S' to enter multi-split mode
# Tag related changes: press 'a' on UI changes, 'b' on tests, etc.
# Press 'D' to open assignment modal
# Assign tags to existing commits or create new ones
# Press 'P' to preview and apply split
Review Changes Before Committing
# Browse mode (read-only)
jj-diff

# Use '/' to search for TODO comments
# Use 'f' to quickly jump between files
# Press Tab to focus diff and scroll with j/k
Edit a Commit Interactively
# Configure jj-diff as diff-editor first
jj diffedit -r @-

# Select changes to keep with Space
# Line-level editing with 'v'
# Press 'a' to save and exit

Performance

jj-diff is optimized for real-world use:

  • Handles 5,000+ files instantly (7μs render time)
  • 20,000+ line diffs with no lag (52μs render time)
  • Search across 100K lines in 17ms
  • Syntax highlighting for 100+ languages

Tested on large repositories with excellent responsiveness.

Contributing

See CONTRIBUTING.md for development setup, tasks, and the git workflow, and DESIGN.md for the architecture overview, patterns, and testing guide.

Troubleshooting

TUI rendering issues: Try setting TERM=xterm-256color

Performance issues: Disable syntax highlighting or use browse mode for very large diffs

jj integration fails: Ensure jj 0.9.0+ is installed and in PATH

License

MIT

Directories

Path Synopsis
cmd
jj-diff command
Command jj-diff is a terminal diff viewer for Jujutsu.
Command jj-diff is a terminal diff viewer for Jujutsu.
internal
components/commitmsg
Package commitmsg prompts for the commit message of one split tag during a multi-split.
Package commitmsg prompts for the commit message of one split tag during a multi-split.
components/destpicker
Package destpicker lists candidate revisions so the user can choose where changes move.
Package destpicker lists candidate revisions so the user can choose where changes move.
components/diffview
Package diffview renders one file's hunks as a scrollable pane, either unified or side by side.
Package diffview renders one file's hunks as a scrollable pane, either unified or side by side.
components/filefinder
Package filefinder is a fuzzy picker modal over a list of items.
Package filefinder is a fuzzy picker modal over a list of items.
components/filelist
Package filelist renders the changed-file list, either as a one-line summary of the selected file or as a full list, with an inline filter over the paths.
Package filelist renders the changed-file list, either as a one-line summary of the selected file or as a full list, with an inline filter over the paths.
components/help
Package help renders the keybinding overlay.
Package help renders the keybinding overlay.
components/searchmodal
Package searchmodal shows the incremental search prompt and its match counter.
Package searchmodal shows the incremental search prompt and its match counter.
components/splitassign
Package splitassign renders the modal that pairs each split tag with a destination, either an existing revision or a commit to create.
Package splitassign renders the modal that pairs each split tag with a destination, either an existing revision or a commit to create.
components/splitpreview
Package splitpreview renders the read-only summary of a pending multi-way split, one row per tag with its destination and the amount of work assigned to it.
Package splitpreview renders the read-only summary of a pending multi-way split, one row per tag with its destination and the amount of work assigned to it.
components/statusbar
Package statusbar renders the single-line footer: the mode, the diff's source and destination, and the keybinding hints for whatever the user is doing.
Package statusbar renders the single-line footer: the mode, the diff's source and destination, and the keybinding hints for whatever the user is doing.
config
Package config resolves the diff render settings for a session, layering the JJ_DIFF_* environment variables over a set of built-in defaults.
Package config resolves the diff render settings for a session, layering the JJ_DIFF_* environment variables over a set of built-in defaults.
diff
Package diff parses unified diff text into files, hunks, and lines, and turns a user's hunk and line selections back into a patch or into reconstructed files on disk.
Package diff parses unified diff text into files, hunks, and lines, and turns a user's hunk and line selections back into a patch or into reconstructed files on disk.
fuzzy
Package fuzzy scores candidate strings against a subsequence query and ranks them, backing the file and revision pickers.
Package fuzzy scores candidate strings against a subsequence query and ranks them, backing the file and revision pickers.
highlight
Package highlight applies chroma syntax highlighting to diff content, picking a lexer from the file path.
Package highlight applies chroma syntax highlighting to diff content, picking a lexer from the file path.
jj
Package jj shells out to the jj binary for the diff, status, and revision data the UI needs, and carries the one write path that moves selected changes into another revision.
Package jj shells out to the jj binary for the diff, status, and revision data the UI needs, and carries the one write path that moves selected changes into another revision.
model
Package model is the Bubble Tea root: it owns the diff, the selection, and every child component, and routes each key to the handler for the current mode.
Package model is the Bubble Tea root: it owns the diff, the selection, and every child component, and routes each key to the handler for the current mode.
search
Package search finds a literal substring across parsed diffs and tracks the cursor over the hits, including the view position to restore when a search is canceled.
Package search finds a literal substring across parsed diffs and tracks the cursor over the hits, including the view position to restore when a search is canceled.
theme
Package theme holds the Catppuccin palette as package-level colors that every component reads directly.
Package theme holds the Catppuccin palette as package-level colors that every component reads directly.
tests

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL