Jujutsu UI

jjui is a terminal user interface for working with Jujutsu version control system. I have built it according to my own needs and will keep adding new features as I need them. I am open to feature requests and contributions.

Features

Core Features

  • Revisions: Main view for exploring and managing revisions
  • Preview: View diffs and details of commits and files
  • Details: Explore files in a commit, with diff, split, and restore capabilities
  • Oplog: View the operation log of your repository
  • Command Execution: Execute shell and jj commands directly from jjui
  • Custom Commands: Define your own commands with custom keybindings
  • Fuzzy File Finder: Fuzzy find on all files and explore changes on them.
  • Ace Jump: Quickly move between revisions using fast key strokes.
  • Exec: Run interactive jj and shell commands
  • Flash Messages: See command output and errors in the UI
  • Bookmarks: Manage bookmarks with a pop-up menu
  • Git: Run git commands from a pop-up menu

Revision Operations

  • Rebase: Interactive rebase of commits
  • Absorb: Automatically absorb changes into commits
  • Abandon: Remove commits from your history
  • Duplicate: Copy revisions with interactive UI
  • Squash: Squash multiple revisions into one
  • Evolog: Explore and restore changes from the evolution log
  • Inline Describe: Edit revision descriptions inline
  • Bookmark: Set or move bookmarks on revisions

Customization

  • Configuration: How to configure JJUI to your needs
  • Command Line Options: Customize behavior with command line flags
  • Leader Key: Create powerful mnemonic shortcuts with nested keymaps
  • Themes: Customize the appearance of the UI with detailed styling options
  • Tracing: Highlight lanes and dim out-of-lane revisions

Revisions

The Revisions view is the default interface when you start jjui. It provides a powerful and interactive way to explore your repository's history, inspect changes, and perform revision operations.

Overview

The Revisions view is divided into three main sections:

1. Revset Bar

The top line displays the current revset, which determines the set of revisions shown in the log graph. You can change the revset by pressing L. Previously used revsets are kept in session history, and you can cycle through them using the up and down arrow keys.

2. Log Graph

The log graph visualizes your repository's commit history, generated from the output of the jj log command. The currently selected revision is highlighted using the configured "selected" background color. Navigate between revisions using j (down) and k (up). Press @ to quickly jump back to the working copy revision.

3. Status Bar

The status bar provides contextual information about the current selection and available actions.

Preview Pane

You can open the preview pane by pressing p. This panel displays detailed information about the selected revision, file, or operation, using commands defined in your configuration. By default, the following commands are used:

[preview]
revision_command = ["show", "--color", "always", "-r", "$change_id"]
oplog_command = ["op", "show", "$operation_id", "--color", "always"]
file_command = ["diff", "--color", "always", "-r", "$change_id", "$file"]

While the preview pane is open, you can scroll using ctrl+n (down), ctrl+p (up), ctrl+d (half page down), and ctrl+u (half page up).

For more details, see the Preview page.

Revision Operations

From the Revisions view, you can perform a variety of operations:

  • Details View: Press l to open the details view for the selected revision. Here, you can split, restore, and inspect files. See Details.
  • Rebase: Press r to enter rebase mode and move revisions, branches, or sources. See Rebase.
  • Absorb: Press A to run jj absorb on the highlighted revision. See Absorb.
  • Abandon: Press a to abandon selected revisions. See Abandon.
  • Duplicate: Press y to duplicate selected revisions. See Duplicate.
  • Squash: Press S to squash selected revisions into one. See Squash.
  • Inline Describe: Press Enter to edit the description of a revision in an inline editor. See Inline Describe.
  • Set Bookmark: Press B to set or move a bookmark on the selected revision.
  • Bookmark Menu: Press b to open the bookmark pop-up menu for managing bookmarks. See Bookmarks.

Customization

You can customize the appearance and behavior of the Revisions view using the configuration file. For example, you can set the number of revisions shown with the --limit flag or configure colors and key bindings. See Configuration and Command Line Options for more information.

Preview

You can open the preview window by pressing p.

Preview window displays output of the jj show (or jj op show) command of the selected revision/file/operation.

You can specify commands to display the content of different item types in the preview window using the configuration options under the preview table in your configuration file.

The default commands are:

[preview]
revision_command = ["show", "--color", "always", "-r", "$change_id"]
oplog_command = ["op", "show", "$operation_id", "--color", "always"]
file_command = ["diff", "--color", "always", "-r", "$change_id", "$file"]

While the preview window is showing, you can press; ctrl+n to scroll one line down, ctrl+p to scroll one line up, ctrl+d to scroll half page down, ctrl+u to scroll half page up.

GIF

Configuration

By default preview window has the following configuration:

[keys.preview]
  mode = ["p"]
  scroll_up = ["ctrl+p"]
  scroll_down = ["ctrl+n"]
  half_page_down = ["ctrl+d"]
  half_page_up = ["ctrl+u"]
  expand = ["ctrl+h"]
  shrink = ["ctrl+l"]
[preview]
  show_at_start = false
  width_percentage = 50.0
  width_increment_percentage = 5.0
  revision_command = ["show", "--color", "always", "-r", "$change_id"]
  oplog_command = ["op", "show", "$operation_id", "--color", "always"]
  file_command = ["diff", "--color", "always", "-r", "$change_id", "$file"]

Preview Window Position

Pressing P will move the preview window to the bottom of the screen, and pressing it again will move it back to the side. You can configure jjui to show the preview window at the bottom by default by setting:

[preview]
show_at_bottom = true

Details

The details view provides an in-depth look at the files and changes within a selected revision. To open the details view, press l while a revision is selected.

In details mode, you can:

  • Leave the details mode using h
  • Split selected files using s
  • Restore selected files using r
  • View the diff of the highlighted file by pressing d
  • Change the revset to show all revisions that touched the selected file by pressing *

You can toggle file selection using m or space keys. These actions allow you to manage files within a revision, split changes, and restore previous states efficiently.

Showing Diff

Press d to show the diff of the selected file. This helps you review changes before making further modifications.

Splitting Files in a Revision

Press s to split the selected files into two revisions:

  • Selected files remain in the current revision.
  • Unselected files move to a new revision. If no file is selected, the currently highlighted file will be split.

GIF

Restoring Files in a Revision

Press r to restore the selected files to their previous state, discarding changes as needed.

Show Revisions That Touched the File

Press * to change the revset to show all revisions that have affected the highlighted file. This is useful for tracking the history of specific files.

Conflict Markers

The details view displays conflict markers next to files with conflicts, making it easy to identify and resolve issues directly from the panel.

Details mode works together with other revision operations such as rebase, squash, and abandon, providing a comprehensive workflow for managing changes in your repository.

GIF

GIF image

Op Log

You can switch to op log view by pressing o.

Op log view is limited to show last 200 operations by default. This can be changed by setting the oplog.limit configuration.

You can use the preview window (opened by pressing p) to display the contents of the selected operation.

Pressing r will restore the working directory to the state of that operation, essentially executing jj op restore <operationid>. You can always undo this operation by pressing u.

Pressing d will show the operation changes in full diff view.

GIF

Command Execution

jjui allows you to execute both shell and jj commands directly from the user interface, making it easy to perform advanced operations without leaving the application.

To run interactive jj commands, press :. jjui will suspend and the command will run in your terminal (for example, : restore -i). You will return to jjui when the command finishes.

To run shell commands, press $. This lets you execute any shell command (such as $ man jj or $ htop) from within jjui.

Both command types support context-aware placeholders, which are replaced with values from your current selection:

  • $file: The currently selected file
  • $change_id: The ID of the selected revision
  • $operation_id: The ID of the selected operation
  • $revset: The current revset query
  • $checked_commit_ids: Commit ids of all the selected revisions
  • $checked_files: File names of all the selected files

These placeholders make it easy to create powerful commands that operate on your current selection and context.

Command history for both : and $ commands is stored in the $XDG_CACHE_HOME/jjui/history/ directory. Separate history files are used for jj and shell commands (exec_jj and exec_sh respectively). Each line in the history files represents a command entry.

To import your shell history and make it available in jjui's shell prompt, you can use:

mkdir -p ~/.cache/jjui/history
history >> ~/.cache/jjui/history/exec_sh

Custom commands can be defined in the custom_commands section of the configuration:

[custom_commands]
"show diff" = { key = ["U"], args = ["diff", "-r", "$change_id", "--color", "always"], show = "diff" }
"show oplog diff" = { key = ["ctrl+o"], args = ["op", "show",  "$operation_id", "--color", "always"], show = "diff" }
"resolve vscode" = { key = ["R"], args = ["resolve", "--tool", "vscode"], show = "interactive" }
"new main" = { args = ["new", "main"] }
"tug" = { key = ["ctrl+t"], args = ["bookmark", "move", "--from", "closest_bookmark($change_id)", "--to", "closest_pushable($change_id)"] }

Custom commands can have placeholder arguments like $change_id, $operation_id, $file, and $revset.

You can also use the following placeholders for more advanced context-aware commands:

  • $commit_id: Refers to the currently selected commit ID.
  • $checked_commit_ids: Refers to all checked (selected) commit IDs.
  • $checked_files: Refers to all checked (selected) files.

Custom commands can also change the revset. For example, the following custom command will filter the view to only show descendants of the selected revision:

[custom_commands]
"show after revisions" = { key = ["M"], revset = "::$change_id" }

There is also a show argument which you can set it to be:

  • none (default); command will run as is and will only be displayed in the status bar.
  • diff: the output of the command will be displayed in the diff viewer.
  • interactive: the command run in interactive mode similar to diffedit, split, commit etc.

Custom commands menu can be opened by pressing x key. Each custom command can have a dedicated optional custom key binding which you can use to invoke it without having to open the custom commands menu.

Custom command window is context aware so it won't display the commands that have place holder but not applicable to the selected item.

image

Example custom commands from the community

Move commit up and down

[custom_commands."move commit down"]
key = ["J"]
args = ["rebase", "-r", "$change_id", "--insert-before", "$change_id-"]

[custom_commands."move commit up"]
key = ["K"]
args = ["rebase", "-r", "$change_id", "--insert-after", "$change_id+"]

Toggle selected revision as parent to the working copy

"toggle parent" = { key = ["ctrl+p"], args = ["rebase", "-r", "@", "-d", "all:(parents(@) | $change_id) ~ (parents(@) & $change_id)"] }

New Note commit (insert an empty commit inline after @. Idea from #278)

[custom_commands."new note commit"]
key = ["N"]
args = ["new", "--no-edit", "-A", "$change_id"]
  • Hit <ret> to immediately begin editing its description message inline! (inline describe)
  • This is great for keeping TODO notes as chains of empty commits

Loading jj aliases as custom_commands. (idea from #211)

You can use the following to import all your aliases as custom commands.

echo "[custom_commands]" >> $JJUI_CONFIG_DIR/config.toml
jj config list aliases -T '"\"" ++ name ++ "\" = { args = [ \"" ++ name ++ "\" ] }\n"' --no-pager |\
    sed -e 's/aliases.//g' |\
    tee -a $JJUI_CONFIG_DIR/config.toml

jjui features a fuzzy file finder that can be activated using ctrl+t shortcut by default.

Selecting a file

When activated, you will see the list of all files that are present on the current revision. Press esc to cancel the file search.

When you select a file (via enter) the current revset will be changed to show all revisions that have touched the selected file.

You can navigate the candidates list using up/down and ctrl+n/ctrl+p as you'd expect on shell prompts.

Using tab will accept the selected file, updating the revset, but will not close the file search like enter does.

You can filter by typing parts of the path name. Space ( ) is used to refine search, that is, to search again but only on the currently matching elements.

We use the excellent sahilm/fuzzy library to perform search, which is also used in some charmbracelet UI widgets.

Refined search is useful particularly for finding files, because sahilm/fuzzy ranks results higher if they have a better match closer to the beginning of the string. However when finding files, you most likely remember the file name (being the furthest part of the whole path), so if you type the file name first and then space you can then filter by directory path.

Live mode

Upon entering fuzzy file search with ctrl+t, if you press ctrl+t again, live preview will be activated.

Live mode will show the revset for the file as you type it and also the show the revision preview.

When live mode is active, up and down are used to move on the revision list, allowing you to see the diff of the selected revision for the file being explored. This is useful when exploring the changes made to a file and quickly moving to explore other files if needed.

Also, during live mode, ctrl+n and ctrl+p are used to scroll the diff preview.

Ace Jump

Ace Jump is a navigation feature inspired by tools like Emacs and IntelliJ, designed to help you quickly move to specific revisions in your repository with minimal keystrokes. In jjui, you can activate Ace Jump mode by pressing f (mnemonic: find), which is also familiar to users of vim-like environments.

When Ace Jump mode is enabled, change IDs and commit IDs are highlighted with shortcut keys. You can press the corresponding key to instantly jump to the desired revision. The jump is performed as soon as you enter the shortest non-ambiguous prefix, usually requiring only 1-3 keystrokes.

Ace Jump is especially useful when working with large repositories or when you need to quickly switch between different parts of your history.

Exec Feature

The jj exec feature in jjui brings command execution capabilities inspired by Vim's : key. It allows users to run both Jujutsu (jj) commands and arbitrary shell commands directly from the UI, using the status bar line-editor for input.

Overview

  • Footer Line-Editor: Commands are entered in the status bar, similar to how searches are performed with /.
  • Context-Aware Placeholders: You can use placeholders like $file, $checked_files, $change_id, $operation_id, $commit_id, and $checked_commit_ids in your commands. These will be replaced with the relevant context value, just as in custom commands.

Running Commands

: Key — Jujutsu Interactive Commands

Pressing : opens the line-editor for entering jj commands. These commands are run interactively, allowing jj to use the terminal for its own TUI if needed.

Examples:

  • : help — Show help for jj
  • : restore -i — Run jj restore in interactive mode
  • : squash -i — Run jj squash interactively

$ Key — Shell Commands

Pressing $ opens the line-editor for entering shell commands. These are run via $SHELL -c "<input>", giving you full control of stdio and allowing you to see all output and errors as you would in a normal terminal.

Examples:

  • $ man jj — Open the manual for jj
  • $ jq . $file | bat --paging always — Process the current file with jq and display with bat
  • $ htop — Run htop interactively

Interactive Behavior

  • Programs are run interactively with full control of stdio.
  • The main UI does not show a status spinner or error notification for these commands.
  • If a program terminates quickly (less than 5 seconds), it is assumed to be non-interactive (e.g., : version or $ ls -la). In these cases, jjui will ask for confirmation before closing the terminal and returning to the main UI.

Log Batching in Revisions

Log batching is now enabled by default in jjui. Instead of loading all revisions at startup, jjui will load the first 50 revisions and fetch additional chunks as you scroll past the 50th revision. This greatly improves startup times for large repositories, while having minimal impact on small ones.

Previously, jjui loaded the entire log before displaying revisions. With batching, you get a faster and more responsive experience.

You can control this feature with the following configuration value:

[revisions]
log_batching = false

Set log_batching to false if you want to revert to the old behavior and load all revisions at once.

Note: This feature was previously experimental and controlled by experimental_log_batching_enabled. It is now enabled by default and the configuration location has changed as shown above.

Flash Messages

Flash messages provide instant feedback for commands executed within jjui. When you run a command, its output is displayed as a flash message in the bottom-right corner of the Revisions view.

Behavior:

  • Success messages: If the command completes successfully, the message is shown for 4 seconds and then automatically dismissed.
  • Error messages: If the command fails, the error message remains visible until you dismiss it manually by pressing esc.

Flash messages help you quickly see the result of your actions without interrupting your workflow.

Bookmark Menu

The bookmark menu in jjui provides an interface for managing bookmarks directly from the Revisions view. This feature allows you to organize, move, track, untrack, forget, and delete bookmarks efficiently.

To use the bookmark menu, press b to open the pop-up menu. The menu displays available bookmark commands for the selected revision. You can select multiple revisions using space before opening the menu.

Available actions include:

  • Move: Press m to filter the list to move bookmark commands, allowing you to move bookmarks to the selected revision.
  • Untrack: Press u to filter the list to untrack bookmark commands, letting you untrack bookmarks from the selected revision.
  • Forget: Press f to filter the list to forget bookmark commands, which will forget bookmarks from the selected revision.
  • Track: Press t to filter the list to track bookmark commands, enabling you to track bookmarks on the selected revision.
  • Delete: Press d to filter the list to delete bookmark commands, removing bookmarks from the selected revision.
  • Free Text Filter: Press / to activate the free text filter, allowing you to filter the bookmark list by typing a bookmark name.

The bookmark menu streamlines bookmark management and works alongside other revision operations such as abandon, rebase, and squash. Filtering with shortcut keys helps you quickly find the desired command for efficient workflow.

image

Git Menu

The Git menu in jjui provides an interface for running Git-related commands directly from the Revisions view. This feature makes it easy to perform common Git operations without leaving the application.

To use the Git menu, press g to open the pop-up menu. The menu displays available Git commands for the current context, allowing you to quickly access fetch, push, and other actions.

Filtering commands:

  • Fetch: Press f to filter the list to fetch commands.
  • Push: Press p to filter the list to push commands.

You can use assigned shortcuts to quickly invoke items in the menu. For example:

  • gpa: Invokes git push --all.
  • gpc: Invokes git push --change.
image

Rebase

You can rebase a revision, branch or source onto/before/after another revision in the revision tree.

GIF

Pressing r enter the rebase mode and by default source is set to source and target is set to onto.

During rebase, there are source and target options that you can set to choose what type of rebase you want. Source defines which revisions you want to be rebased and target defines where and how you want to be rebased.

Source

revision

Can be set by pressing r. Only the selected revision will be moved to the target.

branch

Can be set by pressing B. All revisions in the branch of the selected revision will be moved to the target.

source

Can be set by pressing s. Selected revision and its descendants will be moved to the target.

Target

onto

Can be set by pressing d. Source revisions will be branched off the target revision.

image when applied: image

after

Can be set by pressing a. Source revision(s) will be placed after the target revision. image when applied: image

before

Can be set by pressing b. Source revision(s) will be placed before the target revision.

image

when applied:

image

insert

The revision that's selected by pressing i is set to be the --insert-after argument and the one that you have selected by pressing enter is going to be the --insert-before argument. Effectively running the following command:

jj rebase -r <the revision when r was pressed> --insert-after <the revision when i was pressed> --insert-before <revision when enter was pressed>

before:

image

after:

image

Absorb Operation

The absorb operation lets you automatically incorporate changes from the working copy or child revisions into the selected revision. To use this feature, press A to run jj absorb on the highlighted revision.

After absorbing changes, affected revisions will display a hint line indicating they were updated by the last operation. This helps you track which commits have been modified.

Absorb is commonly used together with other revision management features such as abandon, squash, and rebase, to keep your history clean and organized.

image

Abandon Operation

The abandon operation allows you to remove unwanted revisions from your repository history. To use this feature, press a to run jj abandon on the currently highlighted or selected revisions. You can select multiple revisions by pressing space to check them.

This operation is useful for cleaning up your history and discarding changes that are no longer needed. After abandoning revisions, the log graph will update to reflect the new state of your repository.

Abandon is typically used alongside other revision operations such as rebase, squash, and absorb, which help you manage and organize your commit history.

image

Duplicate Operation

The duplicate operation allows you to copy one or more revisions directly from the user interface. This feature is useful for creating new branches or experimenting with changes without affecting the original history.

To duplicate revisions, select the revision(s) you want to copy and press y (the default key) to enter duplicate mode.

Workflow:

  1. Select the source revision(s) you want to duplicate.
  2. Press y to start the duplicate operation.
  3. Navigate to the target revision where you want to place the copy.
  4. Fine-tune the placement using sub-keys:
    • a: Place the copy after the target revision
    • b: Place the copy before the target revision
    • d: Place the copy onto the target revision (as the destination)
  5. Press Enter to confirm and execute the command.

The UI provides a live preview of the operation, allowing you to see the result before confirming. Duplicate works alongside other revision operations such as rebase, squash, and abandon, giving you flexible control over your repository history.

Squash Operation

The squash operation allows you to combine multiple revisions into a single commit, streamlining your repository history. This feature is accessible from the Revisions view and provides interactive options for advanced control.

To activate squash, press S in the Revisions view. The cursor will automatically move to the parent of the selected revisions, making it easy to choose the target for the squash.

Options:

  • Press e to toggle the --keep-emptied argument, which keeps any emptied commits after the squash.
  • Press i to toggle the --interactive argument, enabling interactive mode for fine-tuning the squash process.

Workflow:

  1. Select the revisions you want to squash.
  2. Press S to activate squash mode.
  3. The cursor moves to the parent revision.
  4. Use e and i to set your desired options.
  5. Confirm to execute the squash operation.

Example:

  • To squash two commits interactively and keep emptied:
    • Select the commits
    • Press S
    • Press e and i to enable both options
    • Confirm to squash

Evolog Operation

The evolog operation provides a specialized view for exploring and restoring changes from the evolution log of your repository. This feature is accessible from the Revisions view and offers interactive controls for inspecting and restoring historical changes.

To activate evolog, press v in the Revisions view to open the evolog output for the selected revision(s).

Available actions include:

  • Restore: Press r to restore the selected evolog item. This is equivalent to running jj restore --from <selected evolog commit id> --into <selected target revision change id> --restore-descendants.
  • Show Diff: Press d to display the diff for the selected evolog item.

Evolog is often used together with other revision operations such as squash, duplicate, and abandon, to help you manage and recover changes in your repository history.

Inline Describe

Inline describe allows you to quickly edit the description of a revision directly from the Revisions view without having to leave the UI.

To use inline describe:

  • Select the revision you want to edit.
  • Press Enter to load the current description into the inline editor.
  • Make your changes in the editor.
  • Accept the changes by pressing Alt+Enter or Ctrl+S.

Bookmark

JJUI loads configuration on start up from the system's config directory.

  • MacOS: ~/Library/Application Support/jjui/config.toml
  • Linux: ~/.config/jjui/config.toml
  • Windows: %AppData%/jjui/config.toml (Might have to manually create folder).
  • Custom: $JJUI_CONFIG_DIR/config.toml

You can edit the configuration in your $EDITOR by passing the --config argument:

jjui --config

Key bindings

Key binding support is limited by the key handling capabilities of the terminal emulator you are using.

Note: Order of the modifiers matter; for example ctrl+alt+up is read as alt+ctrl+up, so alt should come before ctrl.

Note: shift combined with letters is not supported by the underlying library that jjui is using for rendering and key handling. So, ctrl+shift+f is read as ctrl+f. However, shift+f can be defined as F, and it should work.

Colours and Themes

UI appearance is configured through the theming system. You can customize the colors and styles of various UI elements in your config.toml file or by creating custom theme files.

For example, to customize the appearance of selected items:

[ui.colors]
"selected" = { bg = "your colour" }

For more detailed customization options, see the Themes page.

Overriding Default Revset and Log Format

By default, jjui reads and uses the default revset and log format as configured in jj. You can override these values in your configuration file:

[revisions]
template = 'builtin_log_compact' # overrides jj's templates.log
revset = ""  # overrides jj's revsets.log

This allows you to customize how revisions and logs are displayed in jjui, independent of your jj configuration.

Log Batching in Revisions

Log batching is enabled by default in jjui. Instead of loading all revisions at startup, jjui loads the first 50 revisions and fetches more as you scroll. This improves startup times for large repositories, with minimal effect on small ones.

This feature is turned on by default but you can turn it off with the following configuration:

[revisions]
log_batching = false

Default configuration

You can find the default configuration in the repo here: https://github.com/idursun/jjui/blob/main/internal/config/default/config.toml

Command Line Options

jjui provides several command line options to customize its behavior. These options can be passed when starting the application.

Usage

jjui [flags] [location]

Where [location] is an optional path to a jj repository. If not provided, the current directory is used.

Available Flags

FlagAliasDescriptionDefault
--revset=<revset>-rSet default revsetEmpty (uses jj config)
--period=<seconds>-pOverride auto-refresh interval in secondsFrom config (set to 0 to disable)
--limit=<n>-nNumber of revisions to show0 (no limit)
--versionShow version information
--configOpen configuration file in $EDITOR
--helpShow help information

Examples

# Open jjui in the current directory
jjui

# Open jjui in a specific directory
jjui /path/to/repo

# Show only 10 revisions
jjui --limit 10

# Use a custom revset
jjui --revset "remote() & trunk()"

# Disable auto-refresh
jjui --period 0

# Open with a 5-second refresh interval
jjui --period 5

Configuration

jjui uses a configuration file for additional settings. You can edit this file using jjui --config.

Leader key is inspired by vim's Leader, and emacs' hydra/which-key/transient.

Leader is a prefix key that allows navigating a tree of keymaps where leafs are actions to be performed in the UI as if the user typed them directly.

When Leader is activated (default keybinding is backslash: \), the user can navigate a keymap tree using single letter keystrokes. Actions are leafs on this tree and represent key sequences sent to the UI.

Leader keymaps represent mnemonic shortcuts that can do anything that is possible via jjui key-bindings, and allow user defined workflows that fit each person's mental model.

Leader configuration (on your config.toml)

Leader keymaps are configured via the leader table.

[!IMPORTANT] Each entry is identified by an alphanumeric key-sequence.

And it can have the following optional attributes:

context: An array of context placeholders required to enable this entry.

help: Human message to show for keybinding.

send: An array of keys to send into the UI.

Each element of the send array is either a tea.keyName like enter, ctrl+s, down, etc. or a custom string sent directly into the UI.

Examples

The most basic example could be a Leader key h that sends ? into the main UI.

From the main UI, use the following key sequence to invoke it: \ h

[leader.h]
help = "Help"
send = ["?"]

More interesting are nested keymaps:

[leader.n]
context = ["$change_id"]
help = "New change"

[leader.na]
help = "After"
send = [ ":", "new -A $change_id", "enter" ]

[leader.nb]
help = "Before"
send = [ ":", "new -B $change_id", "enter" ]

In this example, \n does not have a send sequence, it is only used to set a help message for the n keymap. But it does have a context requirement, meaning that the n key and its nested keymaps are only visible when $change_id context value is available. That is, when a revision is selected.

From the main UI, entering the following key sequence: \na will send the configured keys into jjui's event loop as if typed by the user:

  • : Opens "exec jj" interactive command.
  • types new -A $change_id
  • executes the command by sending enter key.

Contribute Leader keys that might be useful to others.

Leader keys are intended to be user defined, so they fit your workflow and help you easily do repetitive tasks.

You are free to adapt these examples as you like.

If you want to share some keymaps that might be valuable or serve as inspiration for others, this is the place :).

Edit a file from revision detail. (idea from #184)

The following key \E is enabled only when the cursor is over a file in details view. It will open $file after making $change_id current.

[leader.e]
context = [ "$file", "$change_id" ]
help = "Edit file in @"
send = [ "$", "$EDITOR $file", "enter" ]

[leader.E]
context = [ "$file", "$change_id" ]
help = "Edit file in change"
send = [ "$", "jj edit $change_id && $EDITOR $file", "enter" ]

Save the current revset under a new alias. (idea from #169)

NOTE: uses gum to prompt for the revset alias.

[leader.R]
context = [ "$revset" ]
help = "Save revset"
send = [ "$", "jj config set --repo revset-aliases.$(gum input --placeholder 'Revset Alias') $revset", "enter" ]

Create a bookmark on current change (idea from #67)

[leader.bn]
help = "Set new bookmark"
send = [ "$", "jj bookmark set -r $change_id $(gum input --placeholder \"Name of the new bookmark\")", "enter" ]

Themes allow for detailed control over the application's appearance.

The following configuration loads a base theme from ~/.config/jjui/themes/my-theme.toml.

# ~/.config/jjui/config.toml
[ui]
theme = "my-theme"
# ~/.config/jjui/themes/my-theme.toml
"selected" = { bg = "red", bold = true }

Available themes.

Overriding theme colors

You can still override certain parts of theme by defining the overrides inside ui.colors section. For example the following configuration will load the theme from my-theme file but will set the selected style's background colour to red.

[ui]
theme = "my-theme"

[ui.colors]
"selected" = { bg = "red" }

[!NOTE] Theme support is actively being developed. The information on this page is subject to change as the application evolves.

Light and Dark Theme Syntax

Theme configuration now allows you to choose different themes for light and dark modes. Use the following syntax in your configuration:

[ui.theme]
light = "my-light-theme"
dark = "my-dark-theme"

The previous syntax is still supported, which sets both light and dark themes to the same value:

[ui]
theme = "my-theme"

This flexibility allows you to customize the appearance of jjui for different display environments.

Style Format

Themes are defined as a series of key-value pairs in a TOML file. Each entry consists of a selector (the key) that targets a UI element, and a style table (the value) that defines its appearance.

Example:

"selected" = { fg = "#FF8C00", bg = "#2B2B2B", bold = true }
"border" = { fg = "bright black" }

Style Properties

The style table can contain any of the following properties:

PropertyTypeDescription
fgColorSets the foreground (text) color.
bgColorSets the background color.
boldboolIf true, makes the text bold.
underlineboolIf true, adds an underline to the text.
strikethroughboolIf true, adds a strikethrough line.
italicboolIf true, makes the text italic.

Color Formats

Colors can be specified in one of three formats:

  • TrueColor (Hex): A string representing a hex color code (e.g., "#FF4500").
  • Base16 Names: A string for standard terminal colors (e.g., red, bright green, white).
  • ANSI256 Codes: An integer from 0 to 255.

Selector Inheritance

Style resolution uses a fallback system to apply styles. This allows you to define general styles and override them with more specific ones, reducing repetition.

You can use broad styles like "selected" or "border" to apply globally. Then you can define component-level styles like "revisions" to style an entire section. Then, you can use more specific selectors like "revisions selected" to override the appearance for a specific state within that component.

Resolution Example for "revisions details selected" selector:

  1. The engine looks for a "revisions details selected" style.
  2. It then inherits from "revisions details".
  3. It then inherits from "revisions"
  4. It then inherits from "details selected".
  5. It then inherits from "details".
  6. Finally, it inherits from the base "selected" style.

UI Elements & Selectors

Global Styles

These are base elements that apply throughout the application unless overridden by a more specific selector.

  • text: The default style for all text.
  • dimmed: Less important text, such as hints, descriptions, and inactive elements.
  • selected: The style for a currently highlighted or active item in a list or menu.
  • border: The style for borders around windows, panes, and pop-ups.
  • title: The style for titles in windows, panes, and menus.
  • shortcut: The style for keyboard shortcuts (e.g., [Enter], [q]).
  • matched: The style for the part of the text that matches user input, typically in a completion or filter.

Operation-Specific Styles

These styles appear during interactive operations like rebase, squash or duplicate.

  • source_marker: The marker for the revision being moved or acted upon.
  • target_marker: The marker for the destination of the operation.

Application Sections

Revset (Top Bar)

The input bar at the top of the screen.

  • revset title: The "Revset:" label.
  • revset text: The user input area. It's recommended to make this bold.
  • Completions Dropdown:
    • revset completion selected: The highlighted item in the completions list.
    • revset completion matched: The part of a completion that matches the input.
    • revset completion dimmed: The auto-suggested part of a completion.

Revisions / Oplog (Main List View)

The central list of commits or operations.

  • revisions: The base style for the entire list area.
  • revisions selected: The currently highlighted line.
  • revisions dimmed: Hint text shown during interactive operations.

Status (Bottom Bar)

The bar at the bottom showing the current mode and available actions.

  • status: The base style for the entire bar. A distinct bg is recommended.
  • status title: The current mode indicator (e.g., NORMAL). A contrasting bg helps it stand out.
  • The actions also uses shortcut and dimmed styles.

Evolog (Sub-List View)

The pop-up list showing the evolution history for a revision.

  • evolog: Base style for the view.
  • evolog selected: The highlighted item. Can be styled differently from revisions selected to show which pane is active.

Pop-up menus for primary actions.

  • menu: Base style for menus. Should have a border.
  • Selected item is styled with menu selected
  • Filter is styled with menu matched.
  • Items use menu shortcut, menu title, and menu dimmed styles.

Help Window

The pop-up window displaying key-bindings and help text.

  • help: The base style for the window. To avoid a "patchy" look, define a bg color here. This color will serve as the background for the entire content area.
  • The window uses border and title styles.

Preview (Side Pane)

The pane on the right that shows diffs or other details.

  • preview: The base style for the pane.
  • Uses preview border style for its frame.

Confirmation Dialog

The small inline dialog for confirmations (e.g., "Abandon all?").

  • confirmation: Base style for the dialog. Should have a border.
  • The message uses the global text style.
  • Options use selected (for the highlighted choice) and dimmed (for other choices) styles.

Example "Fire" theme

fire theme screenshot
"text" = { fg = "#F0E6D2", bg = "#1C1C1C" }
"dimmed" = { fg = "#888888" }
"selected" = { bg = "#4B2401", fg = "#FFD700" }
"border" = { fg = "#3A3A3A" }
"title" = { fg = "#FF8C00", bold = true }
"shortcut" = { fg = "#FFA500" }
"matched" = { fg = "#FFD700", underline = true }
"source_marker" = { bg = "#6B2A00", fg = "#FFFFFF" }
"target_marker" = { bg = "#800000", fg = "#FFFFFF" }
"revisions rebase source_marker" = { bold = true }
"revisions rebase target_marker" = { bold = true }
"status" = { bg = "#1A1A1A" }
"status title" = { fg = "#000000", bg = "#FF4500", bold = true }
"status shortcut" = { fg = "#FFA500" }
"status dimmed" = { fg = "#888888" }
"revset text" = { bold = true }
"revset completion selected" = { bg = "#4B2401", fg = "#FFD700" }
"revset completion matched" = { bold = true }
"revset completion dimmed" = { fg = "#505050" }
"revisions selected" = { bold = true }
"oplog selected" = { bold = true }
"evolog selected" = { bg = "#403010", fg = "#FFD700", bold = true }
"help" = { bg = "#2B2B2B" }
"help title" = { fg = "#FF8C00", bold = true, underline = true }
"help border" = { fg = "#3A3A3A" }
"menu" = { bg = "#2B2B2B" }
"menu title" = { fg = "#FF8C00", bold = true }
"menu shortcut" = { fg = "#FFA500" }
"menu dimmed" = { fg = "#888888" }
"menu border" = { fg = "#3A3A3A" }
"menu selected" = { bg = "#4B2401", fg = "#FFD700" }
"confirmation" = { bg = "#2B2B2B" }
"confirmation text" = { fg = "#F0E6D2" }
"confirmation selected" = { bg = "#4B2401", fg = "#FFD700" }
"confirmation dimmed" = { fg = "#888888" }
"confirmation border" = { fg = "#FF4500" }
"undo" = { bg = "#2B2B2B" }
"undo confirmation dimmed" = { fg = "#888888" }
"undo confirmation selected" = { bg = "#4B2401", fg = "#FFD700" }
"preview" = { fg = "#F0E6D2" }

Tracing (Experimental)

The experimental tracing feature in jjui enhances the visualization of revision lanes in the log graph. When enabled, it highlights the lanes associated with the currently selected revision and dims revisions that are outside of those lanes, making it easier to follow the ancestry and relationships in complex histories.

Enabling Tracing

To enable tracing, add the following section to your configuration file:

[ui.tracer]
enabled = true

How It Works

  • Lane Highlighting: When you select a revision, all lanes (branches) related to that revision are highlighted in the log graph.
  • Dimming: Revisions that do not belong to the selected lanes are fainted, helping you focus on the relevant part of your repository's history.

Limitations

  • It's off by default
  • Works only with the curved graph style

How do I .... ?

Welcome to our How do I ...? questions and answers program.

The intention of this page is to document some use-cases that are totally possible
but maybe are not (as of today) directly implemented as key-bindings on jjui.

It would be very difficult to have key-bindings for every imaginable workflow,
so instead, we have tried for jjui to be flexible enough to allow you do stuff
even if they are not directly implemented as jjui features.

We are of course, open to implementing features that are valuable to most of jjui users,
so if something on this page seems like can be made into jjui's codebase, please open a
Feature-Request, an Implementation-Proposal or Pull-Request.
Feel free to open an Q&A discussion for anything not covered here.

Also, remember that jjui's UI is scriptable, and anything that is possible via the UI
can be assigned key-bindings using Leader-Key or Custom-Commands

How do I edit files with conflicts?

Move to the revision marked as having conflicts,

  • Use d to enter the details view, you will be presented with a list of files changed
    in that revision and will see the files containing conflicts.
  • Use (space) to check (✓) the files you want to edit.
  • Use $ vim $checked_files to edit all of them in vim.

How do I create a new change directly upon the current revision?

  • Use : new -A $commit_id

How do I create a mega merge?

  • Use (space) to check (✓) the revisions you want to merge.
  • Use : new all:$checked_commit_ids to create a merge commit having multiple parents.

How do I squash together specific files from multiple revisions?

  • Use (space) to check (✓) the revisions you want to squash from.
  • Open d (details) on one of these revisions and
    use (space) to check (✓) the files from details view.
  • Use : squash --from $checked_commit_ids --into @ $checked_files

This will squash the content of checked files from the checked revisions into the working copy.