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 runjj 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.
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.
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.
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.
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 todiffedit
,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.
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 N
ote 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.
Refined search
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.
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
: Invokesgit push --all
.gpc
: Invokesgit push --change
.
Rebase
You can rebase a revision, branch or source onto/before/after another revision in the revision tree.
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.
after
Can be set by pressing a
. Source revision(s) will be placed after the target revision.
when applied:
before
Can be set by pressing b
. Source revision(s) will be placed before the target revision.
when applied:
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:
after:
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.
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.
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:
- Select the source revision(s) you want to duplicate.
- Press
y
to start the duplicate operation. - Navigate to the target revision where you want to place the copy.
- Fine-tune the placement using sub-keys:
a
: Place the copy after the target revisionb
: Place the copy before the target revisiond
: Place the copy onto the target revision (as the destination)
- 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:
- Select the revisions you want to squash.
- Press
S
to activate squash mode. - The cursor moves to the parent revision.
- Use
e
andi
to set your desired options. - Confirm to execute the squash operation.
Example:
- To squash two commits interactively and keep emptied:
- Select the commits
- Press
S
- Press
e
andi
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 runningjj 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
orCtrl+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
Flag | Alias | Description | Default |
---|---|---|---|
--revset=<revset> | -r | Set default revset | Empty (uses jj config) |
--period=<seconds> | -p | Override auto-refresh interval in seconds | From config (set to 0 to disable) |
--limit=<n> | -n | Number of revisions to show | 0 (no limit) |
--version | Show version information | ||
--config | Open configuration file in $EDITOR | ||
--help | Show 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 likeenter
,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.
- @vic maintains a collection of 462 jjui color themes generated from base16 and base64 color schemes.
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:
Property | Type | Description |
---|---|---|
fg | Color | Sets the foreground (text) color. |
bg | Color | Sets the background color. |
bold | bool | If true , makes the text bold. |
underline | bool | If true , adds an underline to the text. |
strikethrough | bool | If true , adds a strikethrough line. |
italic | bool | If 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
to255
.
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:
- The engine looks for a
"revisions details selected"
style. - It then inherits from
"revisions details"
. - It then inherits from
"revisions"
- It then inherits from
"details selected"
. - It then inherits from
"details"
. - 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 thisbold
.- 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 distinctbg
is recommended.status title
: The current mode indicator (e.g.,NORMAL
). A contrastingbg
helps it stand out.- The actions also uses
shortcut
anddimmed
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 fromrevisions selected
to show which pane is active.
Menus (Git, Bookmarks, etc.)
Pop-up menus for primary actions.
menu
: Base style for menus. Should have aborder
.- Selected item is styled with
menu selected
- Filter is styled with
menu matched
. - Items use
menu shortcut
,menu title
, andmenu 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 abg
color here. This color will serve as the background for the entire content area.- The window uses
border
andtitle
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 aborder
.- The message uses the global
text
style. - Options use
selected
(for the highlighted choice) anddimmed
(for other choices) styles.
Example "Fire" theme
"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
- 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
- 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
- Open
d
(details) on one of these revisions and
use - 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.