Majutsu is a Magit-inspired Emacs interface for the Jujutsu (jj) version control system. It provides a powerful, interactive log viewer and a comprehensive set of commands for manipulating revision history with the efficiency and comfort Emacs users expect.
Majutsu is a Magit-inspired Emacs interface for Jujutsu. It provides an interactive log viewer and commands for inspecting and rewriting revision history. It uses Magit’s section and transient systems and provides visual workflows for operations such as rebasing, squashing, and conflict resolution.
Jujutsu (jj) is a version control system whose working copy is represented by a
commit. Unlike Git, it has no staging area: the working-copy commit is marked
@, and jj snapshots filesystem changes into it at the beginning of most
commands. Mutable history and conflicts are first-class parts of Jujutsu’s
model, making rebasing, squashing, and conflict resolution ordinary operations.
Majutsu stands on the shoulders of giants. We are grateful to:
- The Jujutsu community for creating a revolutionary VCS.
- The Magit authors and contributors for defining the gold standard of VCS interfaces.
- Brandon Olivier for the initial codebase of
jj-mode.el.
- Emacs 29.1 or later.
compat31.0.0.1 or later.- Jujutsu (jj) v0.41.0 or later installed and in your
PATH. magit4.4.0 or later (for section management).consult1.0 or later andplz0.9.1 or later (for native Gerrit completion).transient0.7.0 or later (popup menus).with-editor(for descriptive message editing).
Majutsu is currently available via GitHub. You can install it using your preferred Emacs package manager.
Add the following to your packages.el:
(package! majutsu :recipe (:host github :repo "0WD0/majutsu"))(use-package majutsu
:straight (:host github :repo "0WD0/majutsu"))The built-in use-package :vc keyword is available in Emacs 30 and later.
Use :rev :newest to follow the repository’s current default branch:
(use-package majutsu
:vc (:url "https://github.com/0WD0/majutsu"
:rev :newest))Emacs 29 added built-in package-vc support. Install Majutsu directly, then
configure it with use-package:
(unless (package-installed-p 'majutsu)
(package-vc-install
'(majutsu :url "https://github.com/0WD0/majutsu")))
(use-package majutsu)Clone the repository and add it to your load-path:
(add-to-list 'load-path "/path/to/majutsu")
(require 'majutsu)Vanilla: No keymap setup is required; Majutsu’s native bindings are active as soon as its modes load.
Evil: Majutsu detects Evil, installs its normal-state integration by default,
and opens Majutsu buffers in the configured Evil state. Customize this with
M-x customize-group RET majutsu-evil RET.
default-directory is inside an
existing Jujutsu repository, then run M-x majutsu-log (the shorter
M-x majutsu is an alias). With a prefix argument, or when the current
directory is outside a repository, Majutsu asks which directory to use. If
that directory is not a repository either, it asks before running jj git
init; declining leaves the directory unchanged.
The log buffer is the safest place to learn the interface. Find the working
copy revision, marked @.
Vanilla: Use n and p to move by revision section. TAB expands or
collapses the section at point, while [ and ] move to a visible parent or
child.
Evil: Majutsu starts the log in Normal state. Use C-j and C-k to move by
section; g j and g k move between sibling sections. TAB expands or
collapses the section at point, while [ and ] move to a visible parent or
child.
Point is significant: commands opened from the log use the revision under
point as their default. In particular, RET on a revision runs jj edit; it
changes which revision the workspace edits, so do not use it merely to inspect
a revision.
To inspect without running a history-rewriting command, put point on @ and
press D (majutsu-diff-dwim). The resulting diff buffer shows the current
change.
Vanilla: TAB folds files or hunks, n and p move through sections, and
q returns to the log.
Evil: TAB folds files or hunks, C-j and C-k move through sections, and
q returns to the log without leaving Normal state.
For an explicit selection, press d in the log to open the Diff transient.
Its -r/r entry has two paths: -r reads a revset, while r toggles the
revision at point into the --revisions selection. Press d to execute the
diff, or c to clear the selection first. This does not invoke a rewrite
command, although jj may snapshot changed working-copy files before producing
output. Selections only become arguments when an action is executed.
Use ? to open the dispatcher and see the commands valid in the current
buffer.
Vanilla: A safe first loop is: move through the log, inspect a diff, return
with q, and refresh with g. If a jj command fails, press $ to open the
process buffer and read its complete output.
Evil: A safe first loop is: move through the log in Normal state, inspect a
diff, return with q, and refresh with g r. If a jj command fails, press
` to open the process buffer and read its complete output.
Only after this loop is familiar should you move on to commands in Manipulating, which can rewrite repository history and may ask for confirmation.
Majutsu uses several specialized buffer types to provide a rich interface.
The primary interface for Majutsu. It displays the revision graph, working copy
status, and active workspaces. It uses majutsu-log-mode, derived from
magit-section-mode.
Displays changes between revisions or within the working copy. It supports Magit-style hunk and file sections, word-level refinement, and interactive patching.
Allows viewing the contents of a file at a specific revision. You can navigate
through the file’s history using n and p within the blob buffer.
Vanilla: Press $ in a Majutsu buffer to inspect the current repository’s
recent synchronous and asynchronous jj commands, including failures and
long-running remote operations.
Evil: Press ` in Normal state for the same per-repository command history,
without taking over Evil’s $ motion.
JJ descriptions are edited in an edit session. In the background, jj
waits for the editor, usually emacsclient via with-editor, to save the
description file and return. If the editor exits with a non-zero status,
then jj aborts the operation. The most important commands are therefore
those for finishing or canceling the edit session.
- Key: C-c C-c (with-editor-finish)
-
Finish the current edit session successfully.
jjthen uses the description found in the file. - Key: C-c C-k (with-editor-cancel)
-
Cancel the current edit session with a non-zero editor status, causing
jjto abort the operation. Majutsu saves the current text to its in-session description history before canceling.
JJ-generated comment blocks are highlighted using commit-message faces.
Lines beginning with JJ: are treated as comments, and JJ: ignore-rest
marks the remainder of the buffer as comment. You can customize
majutsu-jjdescription-major-mode, majutsu-jjdescription-comment-prefix,
majutsu-jjdescription-change-id-face, and
global-majutsu-jjdescription-mode. Jujutsu currently does not support
changing the comment prefix; the option exists for future compatibility.
Descriptions are also stored in a ring that lives for the duration of the Emacs session. Majutsu saves the current description when an edit session starts, and again when it finishes or is canceled, so older descriptions can be recovered easily.
- Key: C-c M-s (majutsu-jjdescription-save-message)
- Save the current buffer contents to the description ring.
- Key: M-p (majutsu-jjdescription-prev-message)
- Cycle backward through the description ring, after saving the current description to the ring. With a numeric prefix ARG, go back ARG entries.
- Key: M-n (majutsu-jjdescription-next-message)
- Cycle forward through the description ring, after saving the current description to the ring. With a numeric prefix ARG, go forward ARG entries.
- Key: C-c M-p (majutsu-jjdescription-search-message-backward)
- Search backward through the description ring for a substring match.
- Key: C-c M-n (majutsu-jjdescription-search-message-forward)
- Search forward through the description ring for a substring match.
- Key: C-c C-d (majutsu-jjdescription-show-diff)
-
Show the diff for the described change. If the buffer contains a
JJ: Change ID:line, then use that revision; otherwise fall back to@.
Majutsu organizes information into collapsible sections. TAB toggles the
section at point, such as a revision description in the log or a file in a
diff.
Vanilla: n and p move forward and backward through sections.
Evil: C-j and C-k move forward and backward through sections in Normal,
Visual, and Motion states; g j and g k move between siblings.
Commands in Majutsu are grouped into “transient” menus. These popups allow you
to select options and flags before executing a command. For example, pressing
r opens the Rebase transient.
Primary action entries, such as executing a rebase, squash, split, or upload,
also use majutsu-transient-default-action (default RET). When a primary
action has its own command-specific key, the transient displays the command key
and the default action key on the same row and either key invokes the action.
Some transients support saved defaults. Global defaults follow Transient’s
normal storage; repository-local defaults use jj’s secure repo config-id as
the stable project key, so they survive moving the workspace. Where available,
W saves the current transient arguments as defaults for the current jj
repository only. This is intended for project-shaped UI policy such as log
filters, diff format, and Git sync remotes, not for one-off destructive targets.
For commands like Rebase, Squash, or Absorb, Majutsu uses a visual selection system. You can mark “source” and “destination” revisions directly in the log buffer, and they will be highlighted with distinct colors until the operation is executed or cleared.
Selection entries that support both manual input and point toggling show both
keys on one row, for example -r/r: press the option key to read a revset
manually, or the unprefixed key to toggle the revision at point. Majutsu runs
these readers, point toggles, and primary actions in the buffer that opened the
transient, so repository context, point defaults, and buffer-local selections
stay consistent while the transient popup has focus.
Majutsu integrates with Emacs’ completion system (like Vertico or Ivy) for
selecting bookmarks, remotes, and revsets. Revset completion candidates are
annotated with source labels (pseudo/workspace/bookmark/tag) to make ambiguous
names easier to identify. Relation navigation keeps revision ids as the actual
candidates and shows descriptions as annotations; structured-row field selection
likewise keeps field names canonical while showing value previews as annotations.
Destructive operations like abandon or undo will prompt for confirmation.
Majutsu records command output in a process buffer for each repository. Git
push/fetch, Gerrit upload, diffedit, and commands waiting for with-editor
run asynchronously. Log/diff rendering and most ordinary local mutations wait
for jj to finish before the initiating Emacs command returns; the synchronous
side-effect runner still services subprocesses such as an Emacs-based GPG
pinentry while it waits.
jj log output but adds interactivity.
Each revision in the log is a section. The visible anchor line is built from
fields assigned to the heading module plus any auxiliary fields assigned to
the tail module in majutsu-log-commit-columns (for example, author and
timestamp). Graph prefixes and graph-related indentation are rendered as
display-only prefix decoration rather than real buffer text, so they do not get
copied accidentally and point does not land on them as editable text. When a
revision has body module content (for example, continuation lines from a long
description), that content forms the foldable section body; its current
visibility follows the section state instead of being forced closed on every
refresh.
Press [ to jump to a visible parent and ] to jump to a visible child. When
more than one target is visible, completion returns the canonical revision id
while displaying its description as an annotation.
- Key: l (Vanilla) (majutsu-log-transient)
- Open the log options transient.
- Key: L (Evil) (majutsu-log-transient)
- Open the log options transient.
Useful transient entries include:
- Key: -r
- Set a revset filter with Majutsu’s standard revset expression
reader (e.g.,
all(),mine(), orA | B). The current filter is prefilled for editing; submit an empty value to clear it. - Key: -n
- Limit the number of revisions shown.
- Key: -v
- Toggle reverse order.
- Key: -G
- Toggle the ASCII graph.
- Key: –
- Add fileset/path filters to limit the log to matching files.
- Key: W
- Save the current log arguments as defaults for this jj repository.
Majutsu’s transient selection system chooses revision / change values; it is not a general revset builder. For repeatable arguments, an active log region adds each selected commit. Otherwise, when point is in a semantic revision section, the section’s value is used. Only outside such a section does the toggle fall back to literal revision syntax under point and then the surrounding buffer context.
For arguments that accept a single value, only one selected revision is used.
Majutsu does not convert a region into an expression such as A..B or
synthesize revset operators and functions. Enter such expressions directly
with the normal revset reader, for example, the log transient’s -r entry.
When no explicit value is selected, each command applies its own documented
fallback.
Majutsu’s log parser uses an ordered marker protocol embedded in jj template output:
- Entry start:
\x1dS - Tail start:
\x1dT - Body start:
\x1dB - Metadata start:
\x1dM - Entry end:
\x1dE
Within each module payload, fields are separated by \x1e.
Protocol control bytes are reserved:
\x1dfor module markers\x1efor field boundaries\x1ffor encoded logical newlines
Avoid emitting these bytes literally in user templates unless intentionally participating in the protocol.
Newline rules:
- heading module: may contain real physical newlines.
- tail/body/metadata modules: stay in the trailing payload; encode logical
newlines as
\x1fand decode them in postprocessing.
Parsing is strictly sequential and positional (not key/value based):
- Detect
\x1dSand lock the entry indent column. - Collect heading lines until
\x1dTis found. - Parse
T -> B -> M -> Epayload segments in order on the final heading line (no extra physical line breaks between these module markers). - Keep lines between current entry
Eand next entrySattached to the current section heading area (graph continuation lines).
majutsu-log-commit-columns is a flat list of column plists. Each plist
declares one field occurrence in one row module.
:field- Required field symbol.
:module- Required; one of
heading,tail,body, ormetadata. :template- Required Majutsu template form, or a bound template variable.
:face- Optional highlighting policy; defaults to
t. :post- Optional postprocessor function, function list,
:default, ornil.
A field may occur in different modules, but the same field/module pair may
occur only once. :instance is reserved for compiler-generated occurrence
ids.
Only metadata occurrences populate canonical entry fields. The default
columns therefore include metadata occurrences for id, commit-id,
parent-ids, flags, and description. Preserve the fields needed by
stable section identity, relation navigation and annotations, and hash copying
when replacing the entire column list.
heading- Visible anchor-line content on the left (can span physical lines before the tail segment begins).
tail- Single-line auxiliary content rendered on the anchor line with right alignment. It remains searchable as real text, but copying a mixed heading+tail region drops the tail text by default; copying the tail alone preserves it.
body- Foldable section body.
metadata- Parsed/stored for behavior and lookup; not directly shown by default.
Rendered row spans are tagged with majutsu-row-module,
majutsu-row-field, majutsu-row-column, majutsu-row-entry-id, and
majutsu-row-decoration so explicit copy commands can distinguish content
from graph/tail decorations.
Hidden transport fields exist only when declared as metadata columns. The
default list includes canonical metadata for identity, navigation,
annotations, and copying; replacing the whole list also replaces those
guarantees.
t- Preserve jj-provided text properties/highlighting.
nil- Strip text properties (plain string).
FACE-SYMBOL- Repaint field using that face.
If :face is omitted, Majutsu preserves jj-provided text properties (t).
:post can be:
- A function symbol
- A list of functions (applied left-to-right)
- Omitted or
:default(uses default postprocessors) nil(disable column postprocessors)
Function contract:
(fn VALUE &optional CTX) => NEW-VALUECTX includes at least :field, :module, and the normalized :column
spec.
Postprocessors run per column instance, so the same field can appear in
multiple modules and project to different values. Transport decoding (for
example, turning \x1f back into \n) happens before :post runs.
When you provide a function or function list in :post, Majutsu appends it
after the field’s default postprocessors.
(setopt majutsu-log-commit-columns
'((:field change-id :module heading
:template majutsu-log-template-change-id :face t)
(:field description :module heading
:template majutsu-log-template-description :face t)
(:field author :module tail
:template majutsu-log-template-author :face magit-log-author)
(:field timestamp :module tail
:template majutsu-log-template-timestamp :face magit-log-date)
(:field long-desc :module body
:template majutsu-log-template-long-desc :face t)
(:field id :module metadata
:template majutsu-log-template-id :face nil)
(:field commit-id :module metadata
:template [:commit_id] :face nil)
(:field parent-ids :module metadata
:template majutsu-log-template-parent-ids :face nil)
(:field flags :module metadata
:template majutsu-log-template-flags :face nil)
(:field description :module metadata
:template [:description :first_line] :face nil)));; heading field can emit real newlines directly
(setopt majutsu-log-template-description
[:concat [:change_id] "\n" [:description]])
;; non-heading field should encode line breaks as \x1f
(setopt majutsu-log-template-long-desc
[:description :lines :skip 1 :join "\x1f"])(defun my-log-trim (value &optional _ctx)
(if (stringp value) (string-trim-right value) value))
(setopt majutsu-log-commit-columns
(mapcar
(lambda (column)
(if (and (eq (plist-get column :field) 'description)
(eq (plist-get column :module) 'heading))
(plist-put (copy-sequence column) :post #'my-log-trim)
column))
majutsu-log-commit-columns))majutsu-log-mode, majutsu-evolog-mode, and majutsu-op-log-mode share
these copy commands through majutsu-row-copy-transient.
- Key: ? w s (majutsu-copy-section-value)
- Copy the current section value. An active region uses ordinary region copy.
- Key: ? w f (majutsu-row-copy-field)
- Copy the rendered field value at point.
- Key: ? w F (majutsu-row-copy-entry-field)
- Choose any canonical field stored on the current entry, including hidden metadata that is not visible in the current layout. Completion keeps the field name as the candidate and displays a one-line value preview as its annotation.
- Key: ? w m (majutsu-row-copy-module)
- Copy the rendered visible module at
point (
heading,tail, orbody), without graph-prefix or tail-spacer decoration.
This keeps ordinary Emacs copying predictable while still offering precise row-aware copy operations when you want semantic rather than purely visual text.
Start here after choosing a revision in the log. These inspection workflows share the repository and revision context of the buffer that opened them, but some editing and recovery actions are explicitly identified below.- Key: d (majutsu-diff)
- Open the Diff transient.
Useful transient entries include:
- Key: r
- Select revisions to diff (
--revisions). Can specify multiple revisions or a revset likeB::D. - Key: f / t
- Select the
--fromand--torevisions. - Key: –
- Limit diff to selected files/filesets.
Completion shows full repository-relative paths changed by the active
--revisionsor--from/--toselection. Without an explicit selection it uses revisions in the active log region, then the revision at point, and finally@. Fileset expressions remain free-form and can still be entered directly. - Key: -g
- Toggle Git-style diff output.
- Key: -W
- Toggle color-words diff output.
- Key: -S
- Toggle stat output.
- Key: -s
- Toggle summary output.
- Key: -c
- Set context line count.
- Key: -b / -w
- Ignore whitespace amount / ignore all whitespace.
- Key: W
- Save the current diff arguments as defaults for this jj repository.
The --revisions argument accepts any revset that forms a contiguous set of
commits. “Contiguous” means no gaps in the DAG, but forks and merges are
allowed.
Examples:
-r @- Changes in the working copy commit (default).
-r @-- Changes in the parent of the working copy.
-r B::D- Total changes from B through D.
-r 'A | B'- Valid only if A and B form a contiguous set; gaps produce an error.
When visiting a file from such a diff, Majutsu uses the exact range stored by the diff buffer:
- On an added or context line: resolve the
--revisionsexpression itself. - On a removed line: resolve the parents of that expression
(
REVSET-in revset syntax).
Blob buffers require that the chosen side resolve to exactly one revision. Majutsu reports an error rather than arbitrarily choosing one head or root.
Note: When the revset has multiple heads or roots, the target revision for file visits may be ambiguous. For example, in an X-shaped history:
D E <- two heads \ / C / \ A B <- two roots
If you diff -r A::D | B::E, there are two heads (D, E) and two roots (A, B).
The diff shows changes from the merged parents to the merged heads. If D
and E modify the same lines, the diff can show conflict markers. Inspection of
the combined diff still works, but visiting a blob from either ambiguous side
fails the single-revision check. Use --from / --to with single revisions
when precise file navigation matters.
The diff buffer is highly interactive:
For a diff of exactly one change, Majutsu displays collapsible revision
headers and the change description above the diff. This includes the default
working-copy diff and an explicit -r or --revisions revset that resolves to
one change. The metadata is omitted for --from / --to ranges and revsets
that resolve to zero or multiple changes. Bookmark labels use structured jj
template fields available in every supported jj version, so remote-only
bookmarks and names containing spaces, sigils, or @ are preserved. Control
characters are escaped when rendered to keep the collapsible heading on one line.
- Key: RET (majutsu-diff-visit-file)
- Visit the appropriate version of the file at point. For working copy diffs, added/context lines visit the workspace file while removed lines visit the parent-side blob. For committed changes, it visits the blob at the corresponding side.
- Key: C-j / C-<return> (Vanilla) (majutsu-diff-visit-workspace-file)
- Visit the workspace file, regardless of diff type.
- Key: C-<return> (Evil) (majutsu-diff-visit-workspace-file)
- Visit the
workspace file without conflicting with Evil’s
C-jsection navigation. - Key: - (Vanilla) (majutsu-diff-less-context)
- Decrease the context for diff hunks by COUNT lines.
- Key: = (Evil) (majutsu-diff-less-context)
- Decrease the context for diff hunks by COUNT lines.
- Key: + (majutsu-diff-more-context)
- Increase the context for diff hunks by COUNT lines.
- Key: 0 (Vanilla) (majutsu-diff-default-context)
- Reset the context for diff hunks to the default height.
- Key: ~ (Evil) (majutsu-diff-default-context)
- Reset the context for diff hunks to the default height.
- Key: t (Vanilla) (majutsu-diff-toggle-refine-hunk)
- Toggle word-level refinement.
- Key: T (Vanilla) (majutsu-diff-toggle-fontify-hunk)
- Toggle syntax highlighting within diff hunks. With a prefix argument, cycle between highlighting the current hunk on selection and highlighting all hunks immediately.
When Evil mode is active, Majutsu follows evil-collection’s
conventions for Magit diff buffers: + still increases context, =
decreases it, and ~ resets it to the default.
When --color-words is enabled, Majutsu renders the old/new line numbers in
the left margin and uses “…” lines to split hunks.
With refinement enabled, color-words hunks also show a shadow cursor on the
paired side (controlled by smerge-refine-shadow-cursor).
With Vanilla bindings, file visits through RET or C-j still respect the
side at point by using stored line/column metadata from the color-words
backend.
With Evil bindings, RET visits the side at point and C-<return> forces the
workspace file; C-j remains section navigation. File visits use the same
stored line/column metadata from the color-words backend.
Majutsu’s file inspector is implemented by majutsu-file and can be used both
from command prompts and with defaults derived from the current section context.
Commands:
- Command: majutsu-find-file
- Prompt for revset + path (defaulting from point when possible) and open that file as a blob.
- Command: majutsu-find-file-other-window
- Like
majutsu-find-file, but display the blob in another window. - Command: majutsu-find-file-other-frame
- Like
majutsu-find-file, but display the blob in another frame.
Blob buffers are read-only snapshots with history-aware navigation:
- Key: p / n (majutsu-blob-previous / majutsu-blob-next)
- Jump to the previous or next revision that touched this file, while preserving cursor position as much as possible.
- Key: V (Vanilla) (majutsu-blob-visit-file)
- Jump to the workspace version of the same file.
- Key: C-c m (majutsu-blob-visit-magit)
- Open the current blob in Magit’s blob viewer.
- Key: b (majutsu-annotate-addition)
- Start annotate for the current blob.
- Key: g (Vanilla) (revert-buffer)
- Revert or reload the current blob content.
- Key: g r (Evil) (revert-buffer)
- Revert or reload the current blob content.
- Key: e / C-x C-q (majutsu-blob-edit-start)
- Enter editable blob mode (wdired-style).
Blob navigation keys are disabled while editing, so blob-mode bindings do not interfere with text edits.
- Key: C-c C-c (majutsu-blob-edit-finish)
- Apply changes through
non-interactive
jj diffeditand exit edit mode. Majutsu copies current buffer text into the diffedit right side and finishes automatically, without opening the right-side temp file buffer. - Key: C-x C-q (majutsu-blob-edit-exit)
- Leave editable mode. If modified, it prompts to save or abort; if unchanged, it exits immediately.
- Key: C-c C-k (majutsu-blob-edit-abort)
- Abort blob edits, restore the original content, and restore the original cursor position.
Evil normal-state behavior depends on whether the blob is read-only or editable:
- Key: i (majutsu-evil-blob-insert-dwim)
- Enter editable blob mode and stay in normal state.
- Key: i (evil-insert-state)
- Enter insert state.
Editable mode also changes cursor visuals (see
majutsu-blob-edit-cursor-type); with Evil, the normal-state cursor is updated
too.
Evil bindings in blob buffers keep Evil’s motions (p, n, b, e, w…)
untouched; blob commands live on the g prefix and C-j / C-k, mirroring
the annotate-mode keys:
- Key: gj / C-j (majutsu-blob-next)
- Next revision touching the file.
- Key: gk / C-k (majutsu-blob-previous)
- Previous revision touching the file.
- Key: gb (majutsu-annotate-addition)
- Start annotate for the blob.
- Key: gf (majutsu-blob-visit-file)
- Visit the workspace version of the file.
- Key: gr
- Revert or reload the blob content.
- Key: q
- Bury the blob buffer.
- Key: RET (majutsu-edit-changeset)
- Edit the blob’s revision.
Majutsu annotate is implemented by majutsu-annotate and uses
jj file annotate under the hood. The navigation bindings below belong to
majutsu-annotate-read-only-mode, which is enabled by default through
majutsu-annotate-read-only. Setting that option to nil keeps ordinary file
buffers editable with their editing bindings; revision blob buffers remain
read-only snapshots.
- Key: b (Vanilla) (majutsu-annotate-addition)
- Enter annotate from a blob
buffer. From an ordinary file buffer, use
M-x majutsu-annotate-addition. When invoked again on an annotated chunk, jump to its parent revision (when available) and re-annotate there. - Key: c (majutsu-annotate-cycle-style)
- Cycle heading/highlight/line styles
(see
majutsu-annotate-styles). - Key: n / p (Vanilla) (majutsu-annotate-next-chunk / majutsu-annotate-previous-chunk)
- Move to the next or previous chunk.
- Key: C-j / g j / C-k / g k (Evil) (majutsu-annotate-next-chunk / majutsu-annotate-previous-chunk)
- Move to the next or previous chunk.
- Key: N / P (Vanilla) (majutsu-annotate-next-chunk-same-commit / majutsu-annotate-previous-chunk-same-commit)
- Move to the next or previous chunk from the same commit.
- Key: g J / g K (Evil) (majutsu-annotate-next-chunk-same-commit / majutsu-annotate-previous-chunk-same-commit)
- Move to the next or previous chunk from the same commit.
- Key: RET (Vanilla) (majutsu-annotate-show-commit)
- Show the chunk’s revision diff.
- Key: M-w (majutsu-annotate-copy-hash)
- Copy the chunk change-id.
- Key: SPC (Vanilla) (majutsu-annotate-show-or-scroll-up)
- Scroll the diff window up if it is already open.
- Key: S-SPC (majutsu-annotate-show-or-scroll-down)
- Scroll the diff window down if it is already open.
- Key: ? E (Vanilla) (majutsu-ediff)
- Open the Ediff transient.
- Key: E (Evil) (majutsu-ediff)
- Open the Ediff transient.
Selection: choose revisions via --revisions or --from / --to (with
point-toggle variants). In diff buffers, current range is used as default.
- Key: e (majutsu-ediff-dwim)
- Compare based on context (hunk, file, commit, or whole buffer).
- Key: E (majutsu-ediff-edit)
- Run
jj diffeditwith Emacs asui.diff-editor. If no file is at point, Majutsu prompts for a changed file, then launches a two-sided Ediff session for that single file (left/right temp files). Edit the right-side temp file and quit Ediff to return control tojj diffedit. Whenmajutsu-diffedit-finish-on-saveis non-nil, saving a diffedit temp file can finish the with-editor session automatically. - Key: m (majutsu-ediff-resolve)
- Resolve conflicted files. In a
jj-commitsection, it lists conflicted files for that revision; otherwise it uses working copy@. Usesjj resolvewith 3-way Ediff (merge tool:$left,$base,$right) for up-to-2-sided conflicts, and falls back tojj diffeditfor conflicts with more than 2 sides. In this flow, quitting Ediff without editing leaves jj’s output unchanged, so the conflict stays unresolved. If you edited and quit, Majutsu asks whether to save the resolved result; choosing no discards edits and keeps conflicts. When only part of a conflict is resolved, remaining regions are written with git-style conflict markers so jj keeps unresolved regions unresolved. Conflict-file completion preserves exact repository paths and annotates each candidate with its conflict side count. - Key: M (majutsu-ediff-resolve-with-conflict)
- Open the resolve target buffer
(working-copy file or revision blob), enable
majutsu-conflict-mode, and jump to the first conflict.
Resolve entry points are available both from the Ediff transient and as direct commands:
- Command: majutsu-ediff-resolve
- Resolve conflicted files using the default Ediff-based flow.
- Command: majutsu-ediff-resolve-with-conflict
- Open the conflict target
directly and enable
majutsu-conflict-mode.
- Key: $ (Vanilla) (majutsu-process-buffer)
-
Show the current repository’s process buffer. It retains recent synchronous
and asynchronous
jjcommands, including failures and long-running remote operations. - Key: ` (Evil) (majutsu-process-buffer)
-
Show the current repository’s process buffer. It retains recent synchronous
and asynchronous
jjcommands, including failures and long-running remote operations.
- Key: X (majutsu-op-transient)
- Open the operation dispatcher with log, diff, restore, revert, undo, and redo actions.
- Key: v (Vanilla) (majutsu-evolog)
- Open the evolution log for the revision at point.
- Key: ? v (Evil) (majutsu-evolog)
- Open the evolution log from the Majutsu
dispatcher. Contextual
vremains available in operation diff buffers. - Command: majutsu-op-log-transient
- Open the operation-log menu. Supports
--limit,--reversed, and--no-graph. - Command: majutsu-op-log
- View Jujutsu’s operation history with its graph preserved by default.
Rows mirror jj’s compact operation format: normal and snapshot operations show
short id, user, workspace, elapsed time, description, and all attributes; the
root shows only its short id and
root(). Full ids and canonical metadata remain available to actions and the shared? wcopy submenu. Read-only operation views pass--at-op=@ --ignore-working-copyto avoid snapshotting while browsing. Pressdto open an operation-diff transient prefilled from point,uto restore, orrto revert;RETkeeps the ordinary Majutsu visit behavior. - Command: majutsu-op-diff-transient / majutsu-op-diff
- Compare repository
changes by operation,
--from, or--toin a sectionized changed commit/ref view. Explicit--from/--toranges show lightweight metadata for both endpoint operations, including their full ids, user, workspace, end time, and description. Changed lines keep full commit/change IDs internally. PressRETorvon a changed line to open its evolution log; elsewhereRETkeeps ordinary Majutsu visit behavior, anddremains the ordinary Majutsu diff command. Operation metadata plus this diff view replace a separate operation-show buffer. - Command: majutsu-evolog
- View
jj evologwith its graph preserved and each entry parsed through Majutsu’s structured row protocol. It is also available as? vin the dispatcher. Entries mirror jj’s compact evolog format; full change, commit, and optional operation IDs are hidden metadata available through? w. PressRETto open jj’s native Git-format inter-diff for the entry (usingjj evolog --patch --git);dremains the ordinary Majutsu diff command. - Commands: majutsu-op-restore / majutsu-op-revert
- Restore to an operation or revert an individual operation after confirmation.
- Key: o (majutsu-new)
- Open the New transient to specify options.
Direct log-buffer shortcuts:
- Key: O (majutsu-new-dwim)
- Create a new change on top of the revision at point (DWIM).
- Key: A (majutsu-new-with-after)
- Create a new change after the revision at point.
- Key: B (Vanilla) (majutsu-new-with-before)
- Create a new change before the revision at point.
- Key: I (Evil) (majutsu-new-with-before)
- Create a new change before the revision at point.
Useful transient entries include:
- Key: -r
- Parent revisions.
- Key: -A
- Insert after constraint.
- Key: -B
- Insert before constraint.
- Key: -m
- Set a message.
- Key: -e
- No edit (don’t switch to the new change).
Runs: jj new -r REV
- Key: c (majutsu-describe)
- Edit the description of the revision at point.
Supports
--ignore-immutablefor immutable revisions.
Runs: jj describe -r REV
- Key: C (majutsu-commit)
- Run
jj committhroughwith-editor. This edits the working-copy description, finalizes that change, and lets jj create the next empty working-copy change on top.
Runs: jj commit
- Key: e (majutsu-edit-changeset)
- Move the working copy (@) to the revision
at point. Supports
--ignore-immutablefor immutable revisions.
Runs: jj edit REV
- Key: s (majutsu-squash)
- Open the Squash transient. You can select multiple
source revisions (
--from) to squash into a destination (--into).
Transient arguments:
- Key: -f
- Source revisions (
--from). - Key: -t
- Destination revision (
--into). - Key: -o
- Onto destination.
- Key: -A / -B
- Insert after or before constraints.
- Key: –
- Limit squash to specific filesets.
- Key: -k
- Keep commits that become empty after squashing.
- Key: -u
- Use the destination revision’s description and discard source
descriptions (
--use-destination-message).
Runs: jj squash --from SRC --into DEST [FILESETS...]
- Key: a (majutsu-absorb)
- Open the Absorb transient.
Transient arguments:
- Key: -f
- Source revision (
--from, default@). - Key: -t
- Destination revset filters (
--into, repeatable, defaultmutable()). - Key: –
- Limit absorb to specific filesets.
Runs: jj absorb --from REV --into REVSETS... [FILESETS...]
- Key: r (majutsu-rebase)
- Open the Rebase transient. This is one of Majutsu’s most powerful features, allowing you to visually select sources and destinations.
Transient arguments:
- Key: -s
- Rebase a revision and its descendants (
--source). - Key: -b
- Rebase a whole branch relative to the destination (
--branch). - Key: -r
- Rebase only the selected revisions while preserving dependencies
among them (
--revision). - Key: -o
- Rebase onto destination parent(s) (
--onto). - Key: -A / -B
- Insert after or before the selected destination revisions.
- Key: -p
- Remove redundant parent edges while rebasing (
--simplify-parents).
Exactly one source style (-s, -b, or -r) and one destination style
(-o, -A, or -B) may be active. The destination is required before
Majutsu executes the command.
Runs: jj rebase --source SRC --onto DEST
- Key: ? y (majutsu-duplicate)
- Open the Duplicate transient. Allows selecting source revisions and destination.
Transient arguments:
- Key: -r
- Source revisions to duplicate.
- Key: -o
- Destination (
--onto). - Key: -A / -B
- Insert after or before constraints.
Runs: jj duplicate -r REV
- Key: k (Vanilla) (majutsu-abandon)
- Remove the revision at point from visible history and rebase its descendants onto its parent(s). With an active region selection, Majutsu abandons all selected revisions. The operation remains recoverable through jj’s operation history until that history is pruned.
- Key: x (Evil) (majutsu-abandon)
- Remove the revision at point from visible history and rebase its descendants onto its parent(s). With an active region selection, Majutsu abandons all selected revisions. The operation remains recoverable through jj’s operation history until that history is pruned.
Runs: jj abandon REV
- Key: C-/ / C-? (Vanilla) (majutsu-undo / majutsu-redo)
- Undo or redo.
- Key: u / C-r (Evil) (majutsu-undo / majutsu-redo)
- Undo or redo.
Runs: jj undo or jj redo
- Key: S (majutsu-split)
- Open the Split transient. This allows you to split a revision into multiple parts.
Transient arguments:
- Key: -r
- Specify the revision to split.
- Key: -o
- Specify the destination (
--onto). - Key: -A / -B
- Insert after or before constraints.
- Key: -m
- Set a message for the first part.
- Key: -p
- Parallel split mode.
- Key: –
- Limit split to selected files or filesets.
Runs: jj split -r REV [FILESETS...]
- Key: R (majutsu-restore)
- Open the Restore transient for undoing changes.
Transient arguments:
- Key: -f
- Restore from a specific revision (
--from). - Key: -t
- Restore to a specific revision (
--to). - Key: -c
- Undo changes introduced by a revision (
--changes-in). - Key: -d
- Restore descendants as well.
- Key: –
- Limit restore to selected files or filesets.
Runs: jj restore --from REV [FILESETS...] or
jj restore --changes-in REV [FILESETS...]
- Key: V (Vanilla) (majutsu-revert)
- Open the Revert transient to apply reverse changes in one or more new revisions.
- Key: _ (Evil) (majutsu-revert)
- Open the Revert transient to apply reverse changes in one or more new revisions.
Transient arguments:
- Key: -r
- Source revisions to revert (
--revision, repeatable). - Key: -o
- Apply reverse changes on top of revisions (
--onto). - Key: -A / -B
- Insert reverse changes after or before selected revisions.
Runs: jj revert --revision REV --onto DEST
- Key: > (majutsu-sparse)
- Open the sparse working copy transient (set, add, remove, list, edit).
Sparse transient actions:
- Key: l (majutsu-sparse-list)
- List current sparse patterns in
*Majutsu Sparse*. - Key: s / S (majutsu-sparse-set / majutsu-sparse-set-clear)
- Set patterns by appending, or replace the entire set after clearing it first.
- Key: a / r (majutsu-sparse-add / majutsu-sparse-remove)
- Add or remove patterns incrementally.
- Key: e (majutsu-sparse-edit)
- Edit patterns via
jj sparse edit. - Key: R (majutsu-sparse-reset)
- Reset to all files (
jj sparse reset).
Notes:
- Editing patterns opens a temporary
.jjsparsebuffer. - Lines starting with
JJ:are comments and are ignored by Jujutsu. - Pattern completion includes current directories/files from
@. - Default “all files” state is represented by single pattern
..
Interactive selection is integrated into the Split (S), Squash (s), and
Restore (R) transients. When you open one of these transients from a Diff
buffer, a “Patch Selection” group appears with the following commands:
| Key | Command | Description |
|---|---|---|
H | majutsu-interactive-toggle-hunk | Toggle selection of hunk at point |
F | majutsu-interactive-toggle-file | Toggle all file hunks or a whole-file change with no text hunks |
R | majutsu-interactive-toggle-region | Toggle selection of active region |
C | majutsu-interactive-clear | Clear all patch selections |
The -i argument is a separate, jj-native workflow. When no H/F/R patch
selection is active, Majutsu forwards --interactive (and Split/Squash’s
--tool argument) to jj; --interactive therefore uses the user’s configured
ui.diff-editor. Majutsu does not silently replace that editor with Ediff.
The H/F/R workflow instead supplies Majutsu’s own temporary patch tool when the
operation is executed.
When opening Split, Squash, or Restore from a Diff buffer, each transient maps the compatible part of the diff context to its own command:
- Split converts a single
--revisionssource to--revision. - Squash converts
--revisionssources to repeatable--fromarguments. - Restore converts
--revisionsto--changes-inand also inherits explicit--from/--toranges.
Use a revision diff for Split or Squash. An arbitrary --from / --to range
is meaningful as inherited context only for Restore.
This means you can:
- View a diff with specific
--from/--torevisions - Open the Restore transient
- Select specific hunks to restore
- The restore will apply only to those hunks, using the diff’s revision context
Selected hunks are highlighted with the majutsu-interactive-selected-hunk
face (green background by default). Selected regions within hunks use the
majutsu-interactive-selected-region face (purple background). Selected file
changes with no text hunks use the
majutsu-interactive-selected-file face.
The meaning of “selected” differs by operation:
For Split and Squash, selected content is what gets moved:
- Split: Selected hunks/regions go into the first commit; unselected content stays in the second commit.
- Squash: Selected hunks/regions move into the chosen destination; by default Majutsu infers the source revision’s external parent. Unselected content remains in the source revision.
Example: You have a revision with changes to files A, B, and C. You want to squash only the changes to file A into the parent:
- Open the diff for the revision (
D) - Open Squash transient (
s) - Press
Fon file A to select all its hunks - Execute squash - file A’s changes go to parent, B and C stay
For Restore, selected content is what gets restored (undone):
- Selected hunks/regions are reverted to their state in the source revision
- Unselected content is left unchanged
Majutsu uses a custom merge tool to apply partial patches. When you execute an operation with selections:
- Patch Generation: Majutsu generates a unified diff patch containing only
the selected hunks/regions.
For Restore, it instead builds complementary replay data: the unselected hunks and whole-file changes. Hunkless whole-file changes are represented by structured jj metadata rather than inferred from rendered Git headers.
- Tool Invocation: Jujutsu’s
-i --toolmechanism is used with a custommajutsu-applypatchtool.Each operation stores its patch file and helper script in a separate temporary directory, so overlapping asynchronous operations do not share input files.
- Patch Application:
- For Split/Squash: The tool resets
$right(current state) to$left(parent state), then applies the patch forward. This results in$rightcontaining only the selected changes.- For Restore:
$rightstarts at the source state. The tool applies the complementary text patch and whole-file changes forward, reconstructing the result with the selected changes restored to their source state.
- For Restore:
- For Split/Squash: The tool resets
This approach avoids the complexity of reverse patch application
(git apply -R), which has edge cases with new files, deleted files, and
content starting with + or -.
Text-hunk selection supports the file operations below. Hunkless whole-file selection is available for all three operations as described at the end of this section.
When splitting or squashing a new file:
- If you select the entire file, it goes to the first commit / gets squashed
- If you select only part of the file, only those lines go; the rest stays
For Restore, selected lines are restored to the source state and unselected lines are retained. This also covers a selected portion of a newly added file without reverse-applying a new-file patch.
Note: Partial text selection for deleted files is currently limited.
Select the entire deletion or none. This limitation is independent of hunkless whole-file selection, which handles file changes that have no text hunks.
Renamed and copied files are handled correctly:
- Text-hunk patches preserve rename/copy information
- You can select specific hunks within renamed files just like regular modifications
In a Git-format diff buffer, Split, Squash, and Restore can select a file change with no text hunks, such as a binary or mode-only change, as a unit. Majutsu uses structured metadata from jj as the source of the file operation and paths, and verifies it against the displayed file headers. If the selection cannot be verified, Majutsu refuses it. Restore replays the complementary whole-file changes, so the selected file change is restored as a unit.
- Open a diff with
Dord - Open Split (
S), Squash (s), or Restore (R) transient - Use
Hto select individual hunks, orFto select all hunks in a file - For fine-grained control, mark a region and press
Rto select only those lines - Press
Cto clear selections if needed - Execute the operation - only selected changes will be affected
- Face: majutsu-interactive-selected-hunk
- Face for selected hunks.
- Face: majutsu-interactive-selected-region
- Face for selected regions.
- Face: majutsu-interactive-selected-file
- Face for selected file changes with no text hunks.
- Key: ? m (majutsu-metaedit)
- Open the Metaedit transient. Revisions in the
active region are selected initially, followed by the revision at point, then
@as fallbacks.
Transient arguments:
- Key: r / -r / c
- Toggle revisions at point or in the active region, read a revset, or clear the visual revision selections. Multiple revisions are supported.
- Key: -m / -a / -t
- Set message, author, and author timestamp.
- Key: -c / -u / -U / -f
- Toggle update-change-id, update-author, update-author-timestamp, or force-rewrite.
- Key: -I
- Ignore immutable revisions.
Runs: jj metaedit [OPTIONS...] [REVSETS]...
- Key: ? j (majutsu-sign)
- Open the Sign transient. Revisions in the active
region are selected initially, followed by the revision at point, then
@as fallbacks. - Key: ? J (majutsu-unsign)
- Open the Unsign transient with the same visual revision selection.
Transient arguments:
- Key: r / -r / c
- Toggle revisions at point or in the active region, read a revset, or clear the visual revision selections.
- Key: -k
- Override the configured signing key for
jj sign. Majutsu completes secret signing keys for thegpgandgpgsmbackends, and the configured key plus~/.ssh/*.pubfor thesshbackend. Arbitrary values remain available for backend-specific key formats. A commit-signing backend must be configured in jj. - Key: -I
- Ignore immutable revisions.
Runs: jj sign --revision REVSETS... or jj unsign --revision REVSETS...
- Key: ? P (majutsu-simplify-parents)
- Open the Simplify Parents transient.
Transient arguments:
- Key: -s
- Simplify specified revision(s) together with descendant trees (
--source). - Key: -r
- Simplify specified revision(s) only (
--revision). - Key: -I
- Ignore immutable revisions.
If neither --source nor --revision is set, the Simplify action defaults to
selected region revisions, then the revision at point, then @.
Runs: jj simplify-parents [--source REVSET] [--revision REVSET]
JJ marks conflicts in the log with a “conflict” label. Expanding the revision shows each exact conflicted path together with its conflict side count.
You can enter conflict handling from multiple places:
- Key: m in the Ediff transient (majutsu-ediff-resolve)
- Use the automatic resolve strategy.
- Key: M in the Ediff transient (majutsu-ediff-resolve-with-conflict)
- Force
the conflict workflow. This opens the working-copy file (or a revision blob
buffer for non-working-copy revisions), enables
majutsu-conflict-mode, and jumps to the first conflict. - Command: majutsu-conflict-ensure-mode
- Enable conflict handling in a file with conflict markers.
majutsu-conflict auto-detects marker styles:
- JJ conflict markers (
%%%%%%%/+++++++/-------) -> enablemajutsu-conflict-mode. - JJ long conflict markers (e.g., 15-char marker runs) are treated the same as normal markers.
- JJ conflicts with missing terminating newline markers are parsed with jj’s newline-compensation semantics.
- Git-style markers (
|||||||/ =======) only -> enablesmerge-mode.
Vanilla bindings use the prefix C-c ^:
- Key: n / p (majutsu-conflict-next / majutsu-conflict-prev)
- Jump to the next or previous conflict.
- Key: b (majutsu-conflict-keep-base)
- Keep the conflict base (snapshot / rebase destination section).
- Key: 1..9 (majutsu-conflict-keep-side)
- Keep side N (the “after” side in JJ diff blocks).
- Key: M-1..M-9 (majutsu-conflict-keep-side)
- Keep side N’s “before” variant.
- Key: R (majutsu-conflict-refine)
- Refine conflict regions with word-level highlighting.
Evil bindings are available in Normal state:
- Key: ]] / gj (majutsu-conflict-next)
- Jump to the next conflict.
- Key: [[ / gk (majutsu-conflict-prev)
- Jump to the previous conflict.
- Key: gb (majutsu-conflict-keep-base)
- Keep the base.
- Key: gr 1..9 (majutsu-conflict-keep-side)
- Keep side N (the “after” side).
- Key: gR 1..9 (majutsu-conflict-keep-side)
- Keep side N’s “before” variant.
- Key: ge (majutsu-conflict-refine)
- Refine the conflict.
In Jujutsu, bookmarks are named references similar to Git branches. A local bookmark normally targets one revision and may explicitly track a remote bookmark; concurrent moves can also leave a bookmark with conflicted targets.
The bookmark list always includes local and remote bookmarks, including synchronized and untracked remote bookmarks. No prefix argument is needed.
Local bookmarks and Remote bookmarks are separate sections;
remote bookmarks are grouped by remote name, with fetch URLs and distinct
push URLs shown in the remote headings. Configured remotes without bookmarks
show No bookmarks. On a remote heading, r opens remote management,
f fetches only that remote, and the delete action removes the remote. Both sections remain visible
when empty. Git’s special @git references appear separately under
Git tracking bookmarks when present.
Remote rows show their tracking state. For tracked bookmarks, synced
means there are no commits ahead or behind the local bookmark; +N/-N
counts are relative to the local bookmark, from the remote’s perspective.
Deleted local bookmarks remain visible, and their tracked remote rows say
local deleted. Conflict targets appear beneath their own bookmark.
Sections initially open and retain their folding state on refresh.
When Embark is loaded, embark-act recognizes bookmark list sections and
completion candidates. No additional Majutsu key binding is installed;
use your configured Embark key in either Emacs or Evil.
Local bookmarks offer inspect, edit, move, rename, delete and forget actions.
Remote bookmarks offer inspection and exactly scoped tracking actions: t
tracks an untracked reference and u untracks a tracked reference. The
special @git records offer revision inspection without local bookmark
mutation actions. Conflict children target their own commit, not their
parent bookmark. Remote headings offer fetch, rename and remove, including
remotes with no bookmarks. Copying remains available through Embark’s
standard actions.
The default bookmark/revision action shows its diff; e explicitly edits the
working copy. Commands such as move may still prompt for their remaining
arguments. The ordinary bookmark, fetch and push transients retain their
parameter controls and remain available without Embark.
- Key: b (majutsu-bookmark)
- Open the bookmark transient.
Transient actions:
- Key: l (majutsu-bookmark-list)
- List all bookmarks.
- Key: c (majutsu-bookmark-create)
- Create a new bookmark at point.
- Key: a (majutsu-bookmark-advance)
- Advance bookmarks using jj’s configured default selection and target revset.
- Key: A (majutsu-bookmark-advance-to)
- Advance bookmarks using jj’s default selection to an explicit target revset.
- Key: p (majutsu-bookmark-advance-patterns)
- Advance specific bookmark name(s)/pattern(s) using jj’s configured default target revset.
- Key: s (majutsu-bookmark-set)
- Set or move an existing bookmark to the current revision.
- Key: m (majutsu-bookmark-move)
- Move a bookmark.
- Key: M (majutsu-bookmark-move-allow-backwards)
- Move bookmark(s) with
--allow-backwards. - Key: r (majutsu-bookmark-rename)
- Rename a bookmark.
- Key: d (majutsu-bookmark-delete)
- Delete a bookmark locally and record the deletion for propagation on a later push; this command does not contact a remote itself.
- Key: f (majutsu-bookmark-forget)
- Forget a bookmark locally without affecting remotes.
- Key: t (majutsu-bookmark-track)
- Track a remote bookmark to create a local one.
- Key: u (majutsu-bookmark-untrack)
- Untrack a remote bookmark.
Bookmark lists nest tracked remote bookmarks under their local bookmark. When a bookmark target is conflicted, its removed and added targets appear as child revision sections identified by their full commit IDs.
In Jujutsu, tags are lightweight refs that point to revisions, similar to Git
tags. Majutsu wraps jj tag set/list/delete and follows jj semantics where
creating and moving tags both use tag set (moving requires --allow-move).
Tag commands are intentionally dispatch-first.
- Key: ? t (majutsu-tag)
- Open the tag transient.
Transient actions:
- Key: l (majutsu-tag-list)
- List tags in a dedicated buffer.
- Key: s (majutsu-tag-set)
- Set tag(s). Uses completion and allows new names.
- Key: m (majutsu-tag-move)
- Move existing tag(s) to another revision (internally
--allow-move). - Key: d (majutsu-tag-delete)
- Delete tag(s) by name or pattern.
Tag prompts use completion candidates from local tags. Existing tags are shown for selection, while set/delete prompts still allow entering string patterns manually.
- Key: G (Vanilla) (majutsu-git-transient)
- Open the Git transient. Jujutsu can interact directly with Git remotes.
- Key: ? G (Evil) (majutsu-git-transient)
- Open the Git transient. Jujutsu can interact directly with Git remotes.
- Key: p (majutsu-git-push-transient)
- Open the Push transient. You can push bookmarks, tags, revisions, or changes.
Transient arguments:
- Key: -a
- Push all bookmarks and tags.
- Key: -t
- Push tracked bookmarks and tags only.
- Key: -b
- Push specific bookmark(s).
- Key: -T
- Push specific tag(s) or tag patterns.
- Key: -c
- Push a specific change.
- Key: -r
- Push specific revision(s).
- Key: -o
- Pass a Git push option; may be repeated.
- Key: -y
- Show what would be pushed without pushing (
--dry-run). - Key: W
- Save stable push options as defaults for this jj repository.
Majutsu remembers sync policy such as
--remote,--all,--tracked,--deleted,--allow-empty-description, and--allow-private, but does not remember one-off targets like--bookmark,--tag,--change,--revision,--named,--option, or--dry-run.
Runs: jj git push
- Key: f (majutsu-git-fetch-transient)
- Open the Fetch transient to pull changes from a remote.
Transient arguments:
- Key: -R
- Fetch from a specific remote; may be repeated.
- Key: -b
- Fetch a specific branch; may be repeated.
- Key: -T
- Fetch specific tag(s) or tag patterns; may be repeated.
- Key: -t
- Fetch only tracked bookmarks and tags.
- Key: -A
- Fetch from all remotes.
- Key: W
- Save stable fetch options as defaults for this jj repository.
Majutsu remembers
--remote,--tracked, and--all-remotes, but not one-off--branchor--tagselections.
Runs: jj git fetch
- Key: u (majutsu-gerrit-upload-transient)
- Open the Gerrit upload transient.
Transient arguments and selection controls:
- Key: -r
- Upload specific revision(s).
- Key: r
- Toggle the revision at point into or out of the upload selection.
If no revision is selected, Majutsu leaves revision selection to
jj gerrit upload, which uploads@when it has a description and@-otherwise. - Key: -b
- Target remote branch (
--remote-branch). - Key: -R
- Gerrit remote (
--remote). - Key: -n
- Preview without pushing (
--dry-run). - Key: W
- Save stable upload options as defaults for this jj repository.
Majutsu remembers
--remote,--remote-branch,--notify, and--ignore-attention-set, but not revisions, dry-run, metadata, state, or submit-related flags.
The remaining groups expose:
- Review options include reviewers, CCs, labels, topic, hashtags, and a patch set message.
- State/notification options include change edit, WIP/ready, private/remove-private, publishing comments, notification policy, and attention-set handling.
- Advanced options expose submit, skip validation, merged uploads, deadlines, custom keyed values, and trace values.
Runs: jj gerrit upload
Reviewer/CC prompts use Majutsu’s native Gerrit REST client and Gerrit’s
/accounts/ suggestion endpoint. Topic completion uses the same REST client to
query open changes and aggregate existing topics. Label and hashtag prompts are
free-form minibuffer input.
REST-backed completion is enabled only when Majutsu can determine a reliable
Gerrit web/API origin. HTTP(S) remotes work directly. For SSH remotes such as
ssh://user@host:29418/project, configure Gerrit’s web URL explicitly, for
example:
jj config set --repo gerrit.review-url http://host:8080Majutsu intentionally does not guess a web/API URL from SSH port 29418.
This setting is not required for jj gerrit upload itself; upstream jj uses
gerrit.review-url only to generate Link: trailers instead of Change-Id:
trailers. Majutsu also uses it as a reliable web/API base for native Gerrit
completion.
- Key: r (majutsu-git-remote-transient)
- Open the Remote Management transient.
Transient actions:
- Key: l (majutsu-git-remote-list)
- Open the bookmark list at its Remote bookmarks section. Configured remotes with no bookmarks are included.
- Key: a (majutsu-git-remote-add-transient)
- Add a new Git remote as
jj git remote add [OPTIONS] REMOTE URL. Majutsu prompts for a new remote name, rejects names that already exist, prompts for the URL/path positional, and exposes--fetch-tagsand--push-url. - Key: d (majutsu-git-remote-remove)
- Remove an existing remote. In the bookmark list buffer, the remote at point is used as the default.
- Key: r (majutsu-git-remote-rename)
- Rename a remote.
- Key: u (majutsu-git-remote-set-url-transient)
- Set remote URLs. With no
option, Majutsu prompts for the fetch URL/path positional; use
--fetchand/or--pushto set them explicitly. In the bookmark list buffer, the remote at point is used as the default.
- Key: c (majutsu-git-clone-transient)
- Clone a Git repository into a jj repo.
Source may be a URL or path;
--branchcan be repeated. - Key: i (majutsu-git-init-transient)
- Initialize a new Git-backed jj repository.
In a colocated Git-backed workspace, Jujutsu automatically imports from and exports to the underlying Git repository on each command. In other Git-backed layouts, use the commands below to synchronize the repositories explicitly:
- Key: e (majutsu-git-export)
- Run
jj git export. - Key: m (majutsu-git-import)
- Run
jj git import. - Key: o (majutsu-git-root)
- Show the Git directory path (
jj git root).
Jujutsu supports multiple workspaces sharing the same repository storage. This is similar to Git worktrees but more integrated.
When Embark is loaded, workspace completion candidates offer RET / v to
visit the workspace, d to open its root in Dired, and W to copy its root.
Actions run in the repository directory associated with the completion.
- Key: Z / % (Vanilla) (majutsu-workspace)
- Open the workspace transient.
- Key: * (Evil) (majutsu-workspace)
- Open the workspace transient.
Transient actions:
- Key: l (majutsu-workspace-list)
- List all workspaces.
- Key: v (majutsu-workspace-visit)
- Visit a workspace by changing
default-directoryand refreshing Majutsu buffers. - Key: a (majutsu-workspace-add)
- Add a new workspace.
Forget option:
- Key: -t
- Toggle trashing workspace directories after forgetting them.
More transient actions:
- Key: f (majutsu-workspace-forget)
- Forget a workspace. With
-tenabled, also move its local directory to the system trash afterjj workspace forgetsucceeds; this trash step accepts local roots only, while plain forget can still forget a remotely located workspace. Before moving a directory, Majutsu verifies that the path still identifies the directory the user confirmed and does not own the shared.jj/repostore. It checks all selected workspaces and reports all paths requiring manual cleanup together. - Key: u (majutsu-workspace-update-stale)
- Update a stale workspace.
- Key: n (majutsu-workspace-rename)
- Rename a workspace.
- Key: r (majutsu-workspace-root)
- Show and copy the current workspace root.
Workspace sections are rendered from a structured jj workspace list -T
template. By default they show each workspace name, working-copy ids,
description, and resolved root path when available. If jj cannot resolve a
workspace root, Majutsu treats it as unavailable instead of showing the raw
<Error: ...> text in the section body. Workspace paths containing embedded
newlines or other control characters are preserved as complete values.
- User Option: majutsu-jj-executable
- Path to the
jjbinary (default:"jj"). - User Option: majutsu-jj-global-arguments
- Global arguments passed to all jj
commands (default:
("--no-pager" "--color=always")). - User Option: majutsu-jj-diffstat-columns
- Column width used only for
jj diff --stat(default:80). Set tonilto inherit terminal width. Raise it if you want more full path visibility; keep it near 80 if you prefer compact stat bars. - User Option: majutsu-transient-default-action
- Key used for primary/default
action suffixes in Majutsu transients (default:
RET). Set it to another key description, such as., to move the default action key. - User Option: majutsu-show-process-buffer-hint
- Whether to append a
process-buffer hint to jj errors. The displayed key is resolved from the
active bindings, so Vanilla normally shows
$and Evil normally shows`. - User Option: majutsu-debug
- Enable debug logging for jj operations.
- User Option: majutsu-show-command-output
- Show jj command output in messages.
- User Option: majutsu-confirm-critical-actions
- If non-nil, prompt for confirmation before critical operations like abandon, undo, redo, rebase.
- User Option: majutsu-no-confirm
- A list of symbols for actions Majutsu
should not confirm, or
tto never confirm. Valid symbols:undo,redo,abandon,bookmark-delete,tag-delete,git-remote-remove,rebase,workspace-forget, andworkspace-trash. - User Option: majutsu-slow-confirm
- A list from the same action set that
should use
yes-or-no-pinstead ofy-or-n-p, ortfor all of them.
- User Option: majutsu-process-popup-time
- Popup the process buffer if a
command takes longer than this many seconds.
-1means never,0means immediately. - User Option: majutsu-process-log-max
- Maximum number of sections to keep in a process log buffer (default: 32).
- User Option: majutsu-process-apply-ansi-colors
- When non-nil, convert ANSI escapes in jj output to text properties.
- User Option: majutsu-process-timestamp-format
- Format string for process log section timestamps.
- User Option: majutsu-jj-environment
- Extra
KEY=VALUEentries prepended to subprocess environments for all jj invocations (local and TRAMP), applied centrally bymajutsu-process.
- User Option: majutsu-log-commit-columns
- Flat log row column declarations controlling templates, modules, face policy, and postprocessing. For protocol details, module semantics, and examples, see Inspecting -> Log Buffer.
- Template family: majutsu-log-template-<field>
- Template forms consumed by log fields. For newline and control-character rules, see Inspecting -> Log Buffer.
- User Option: majutsu-log-sections-hook
- Hook run to insert sections in the log buffer.
- User Option: majutsu-display-buffer-function
- Magit-style display strategy
(default:
majutsu-display-buffer-traditional). Built-in alternatives aremajutsu-display-buffer-same-window-except-diff-v1,majutsu-display-buffer-fullframe-log-v1,majutsu-display-buffer-fullframe-log-topleft-v1,majutsu-display-buffer-fullcolumn-most-v1, anddisplay-buffer. - User Option: majutsu-pre-display-buffer-hook
- Hook run before displaying a buffer.
- User Option: majutsu-post-display-buffer-hook
- Normal hook run after displaying a buffer.
- User Option: majutsu-bury-buffer-function
- Function used by
qto bury/quit Majutsu buffers (default:majutsu-mode-quit-window; alternatives includequit-windowandmajutsu-restore-window-configuration).
- User Option: majutsu-diff-refine-hunk
- Whether to show word-granularity
differences inside hunks.
nildisables,trefines the current hunk, and'allrefines all hunks. - User Option: majutsu-diff-fontify-hunk
- Whether to apply syntax highlighting
to diff hunks.
nildisables,tfontifies a hunk when it becomes current, and'allfontifies all hunks immediately. This is synchronous and can be slow on large diffs. - User Option: majutsu-diff-refine-ignore-whitespace
- Whether to ignore whitespace while refining hunks.
- User Option: majutsu-diff-paint-whitespace
- Whether to highlight whitespace issues inside diff hunks.
- User Option: majutsu-diff-highlight-trailing
- Whether to mark trailing whitespace in diff hunks.
- User Option: majutsu-create-buffer-hook
- Normal hook run when a new Majutsu buffer is created.
- User Option: majutsu-setup-buffer-hook
- Normal hook run after displaying the buffer.
- User Option: majutsu-post-create-buffer-hook
- Normal hook run after the initial refresh.
- User Option: majutsu-refresh-buffer-hook
- Normal hook run after refreshing.
- User Option: majutsu-jjdescription-major-mode
- Major mode used for editing
JJ description buffers (default:
text-mode). - User Option: majutsu-jjdescription-summary-max-length
- Column beyond which
summary line characters are highlighted (default: 68, same as Magit). Set
this option to
nilto disable highlighting; zero highlights the entire nonempty summary. - User Option: majutsu-jjdescription-fill-column
- Local
fill-columnused in JJ description buffers (default: 72). A positive integer enables Auto Fill for the body but never folds the summary line; filling is delegated to the selected major mode, so constructs that mode protects (such as Org headings, tables, and blocks) remain intact. Set this option tonilto disable Auto Fill and leave the buffer’s currentfill-columnunchanged. - User Option: majutsu-jjdescription-comment-prefix
- Comment prefix used in JJ
description buffers (default:
"JJ:"). - Face: majutsu-jjdescription-change-id-face
- Face used for JJ Change ID values.
- Minor mode: global-majutsu-jjdescription-mode
- Global minor mode to enable JJ description buffer enhancements.
- User Option: majutsu-evil-enable-integration
- Set to
nilto disable automatic Evil bindings. - User Option: majutsu-evil-initial-state
- The Evil state to start in (default:
normal).
When Evil integration is enabled, the dispatcher displays and accepts the same
Evil-facing top-level keys used in Majutsu buffers, including x, L, _,
*, u, C-r, and `. The global Evil v binding remains Visual state;
open evolog from the dispatcher with ? v.
Majutsu includes a domain-specific language (DSL) for building Jujutsu
templates in Emacs Lisp. The main entry point is majutsu-tpl, which compiles
a vector-based DSL form into a jj template string.
The DSL provides several advantages over writing raw template strings:
- Early validation: Constant vector forms with an explicit constant self type are validated during macro expansion/byte-compilation. Dynamic forms and forms that rely on the configurable default self type are validated when Majutsu compiles them, before they are sent to jj.
- Automatic escaping: String literals are properly escaped (quotes, backslashes, control characters) without manual intervention.
- Elisp integration: Embed Elisp expressions that are evaluated when Majutsu compiles the template—during macro expansion for constant forms and at runtime for dynamic forms—so configuration and context can shape the result.
- Composability: Define reusable template functions with
majutsu-template-defunthat lower into the final template instead of requiring jj-side aliases. - Type awareness: The DSL understands jj’s type system, enabling self-type
context for cleaner keyword syntax (
[:description]instead of[:method [:self] :description]). - Readability: Vector-based syntax with keywords is more readable than deeply nested string concatenation.
Example comparison:
;; Raw string (error-prone, hard to read)
"if(self.root(), \"(root)\", self.commit_id().short())"
;; DSL (validated, composable, readable)
(majutsu-tpl [:if [:root] "(root)" [:commit_id :short]] 'Commit)Vectors without a leading keyword are implicitly concatenated:
(majutsu-tpl ["A" "B"]) ; => "concat(\"A\", \"B\")"
(majutsu-tpl [:concat "A" "B"]) ; => "concat(\"A\", \"B\")"Bare strings inside vectors are automatically treated as string literals (:str).
Use :str for explicit string literals with proper escaping:
(majutsu-tpl [:str "Hello \"World\""]) ; => "Hello \"World\""Use :raw to inject template code directly without escaping:
(majutsu-tpl [:raw "self.commit_id().short()"]) ; => "self.commit_id().short()"Untyped raw snippets now default to semantic type Unknown rather than
pretending to be Template. If you want method-chain typing to keep flowing,
add an explicit annotation:
[:raw "self" :Commit]The callable position of :call can be written as a quoted symbol, keyword, or
string name:
(majutsu-tpl [:call 'coalesce [:str ""] [:str "X"]]) ; => "coalesce(\"\", \"X\")"Elisp t and nil map to true and false. Numbers pass through directly:
(majutsu-tpl [:if t "yes" "no"]) ; => "if(true, \"yes\", \"no\")"
(majutsu-tpl [:call 'pad_end 8 "x"]) ; => "pad_end(8, \"x\")"Use :method to call methods on objects. Methods can be chained:
(majutsu-tpl [:method [:raw "commit" :Commit] :commit_id])
; => "commit.commit_id()"
(majutsu-tpl [:method [:raw "commit" :Commit] :parents :len])
; => "commit.parents().len()"
(majutsu-tpl [:method [:raw "commit" :Commit] :diff "src"])
; => "commit.diff(\"src\")"At public compile entry points, when a self type is provided (or the default
self type is configured), Majutsu installs a root self binding and bare
keywords become method calls on that receiver:
(majutsu-tpl [:description] 'Commit) ; => "self.description()"
(majutsu-tpl [:parents :len] 'Commit) ; => "self.parents().len()"Use [:self] when you need the current implicit receiver as a value. When
nested lambdas or helper-local :bind-self scopes introduce new receivers,
[:self N] selects the outer binding N levels up ([:self 0] is the same
as [:self]):
(majutsu-tpl [:method [:self] :description] 'Commit)
; => "self.description()"
(majutsu-tpl [:method [:raw "self" :Commit]
:parents
:map
[:|p| [:method [:self 1] :description]]])
; => "self.parents().map(|p| self.description())"Ordinary helpers can temporarily rebind the implicit self inside their body
with :bind-self:
(majutsu-template-defun show-canonical-log-id ((object Commit :optional t))
(:returns Template :bind-self object)
[:canonical-log-id])When the bound parameter is nil, the helper inherits the outer self binding.
Arithmetic and logical operators are supported:
(majutsu-tpl [:+ 1 2]) ; => "(1 + 2)"
(majutsu-tpl [:and [:> 3 1] [:<= 2 2]]) ; => "((3 > 1) && (2 <= 2))"
(majutsu-tpl [:not t]) ; => "(!true)"
(majutsu-tpl [:++ "L" "R"]) ; => "(\"L\" ++ \"R\")"(majutsu-tpl [:if [:root] "(root)" [:commit_id]])
; => "if(self.root(), \"(root)\", self.commit_id())"
(majutsu-tpl [:separate " " [:label "a" "A"] [:label "b" "B"]])
; => "separate(\" \", label(\"a\", \"A\"), label(\"b\", \"B\"))"Elisp expressions are evaluated when the template is compiled.
(let* ((tmp 1)
(s1 `[:concat ,(if (> 2 tmp) [:str "T"] [:str "F"]) [:str "!"]])
(s2 [:concat (if (> 2 tmp) [:str "T"] [:str "F"]) [:str "!"]])
(tmp 3))
(concat (majutsu-tpl s1) (majutsu-tpl s2)))
; => "concat(\"T\", \"!\")concat(\"F\", \"!\")"Anonymous functions are first-class template values:
(majutsu-tpl [:lambda [c] [:description]])
; => "|c| c.description()"
(majutsu-tpl [:|c| [:description]])
; => "|c| c.description()"
(majutsu-tpl [:parents :map [:|c| [:description]]])
; => "self.parents().map(|c| c.description())"
(majutsu-tpl [:call [:|c| [:description]] [:raw "item" :Commit]])
; => "item.description()"
(majutsu-tpl [[:lambda [c] [:description]] [:raw "item" :Commit]])
; => "item.description()"The shorthand [:|c| BODY] is equivalent to [:lambda [c] BODY].
Lambda parameters are lexical variables and can act as a deferred implicit self
for bare keyword dispatch. A surface lambda such as [:lambda [c] [:description]]
is therefore kept generic first and can later be specialized from a typed call
argument or a higher-order container element. In those cases [:description]
becomes equivalent to [:method 'c :description]. If you prefer, you can also
write that explicit [:method 'c ...] form directly, but most examples use bare
keywords because it better matches the implicit-self model. Explicit lexical
references also keep working across nested lambdas, so inner bodies may still
refer to outer parameters by name when that is clearer than rebinding receiver
context. This deferred behavior is limited to lambda parameters; Majutsu no
longer uses unknown non-lambda receivers as a general bare-keyword fallback. If
you need an outer receiver explicitly inside a nested lambda, use [:self N].
For example, the inner body below uses all three forms at once: [:description]
for the inner receiver, [:self 1] for the outer receiver, and explicit
[:method 'o ...] for the outer lexical parameter:
(majutsu-tpl
[[:|o|
[[:|i|
[:if [:method 'o :root]
[:description]
[:method [:self 1] :description]]]
[:raw "inner" :Commit]]]
[:raw "outer" :Commit]])
; => "if(outer.root(), inner.description(), outer.description())"List-oriented methods can be written in several equivalent styles:
;; Historical explicit-binder sugar (lowers to a native lambda internally)
(majutsu-tpl [:map [:raw "self.bookmarks()"] b [:raw "b.name()"]])
; => "self.bookmarks().map(|b| b.name())"
;; Direct jj-style method call with an anonymous lambda
(majutsu-tpl [:method [:raw "refs"] :map [:lambda [c] [:description]]])
; => "refs.map(|c| c.description())"
;; Dash-style explicit lambda
(majutsu-tpl [:-map [:lambda [c] [:description]] [:raw "refs"]])
; => "refs.map(|c| c.description())"
;; Dash-style anaphoric shorthand
(majutsu-tpl [:--map [:method 'it :description] [:raw "refs"]])
; => "refs.map(|it| it.description())"
(majutsu-tpl [:method
[:map [:raw "self.parents()"] p [:raw "p.commit_id()"]]
:join [:str ", "]])
; => "self.parents().map(|p| p.commit_id()).join(\", \")"
(majutsu-tpl [:method [:raw "self.parents()"]
:map [:lambda [p] [:raw "p.commit_id()"]]
:join [:str ", "]])
; => "self.parents().map(|p| p.commit_id()).join(\", \")"Prefer direct lambdas and :map + :join composition in new code. The
historical binder form [:map collection var body] remains as compatibility
sugar, but it now lowers through the same core :method + :lambda path as
direct method calls. Similarly, :-map remains a value-level explicit-lambda
helper while the :--map family is syntax sugar layered on top of the same
lambda support.
This is a deliberate Majutsu DSL adaptation. Upstream jj centers the
higher-order story around expressions such as collection.map(|x| body);
Majutsu keeps that core model and layers additional sugar on top of it.
Reusable lambda bodies can therefore also be defined as ordinary helpers that
return native lambda values:
(majutsu-template-defun description-fn ()
(:returns Lambda)
[:lambda [c] [:description]])
(majutsu-tpl [:description-fn])
; => "|c| c.description()"
(majutsu-tpl [:method [:raw "refs"] :map [:description-fn]])
; => "refs.map(|c| c.description())"
(majutsu-template-defun description-with-suffix ((suffix Template))
(:returns Lambda)
`[:lambda [c] [:concat [:description] ,suffix]])
(majutsu-tpl [:method [:raw "refs"] :map [:description-with-suffix [:str "!"]]])
; => "refs.map(|c| concat(c.description(), \"!\"))"Use majutsu-template-defun to define reusable template functions:
(majutsu-template-defun my-helper ((label Template) (value Template :optional t))
(:returns Template)
`[:concat ,label [:str ": "] ,(or value [:str ""])])
(majutsu-tpl [:my-helper [:str "ID"] [:str "VAL"]])
; => "concat(\"ID\", \": \", \"VAL\")"When a helper omits its body, majutsu-template-defun defaults to a simple
wrapper around the same jj callable name:
(majutsu-template-defun my-fill ((width Integer) (content Template))
(:returns Template))
(majutsu-tpl [:my-fill 8 "x"])
; => "my-fill(8, \"x\")"Syntax-level sugar should be defined separately with
majutsu-template-defspecial, which receives raw forms and lowers them to more
primitive template syntax:
(majutsu-template-defspecial :wrap-angle (body)
`[:concat [:str "<"] ,body [:str ">"]])
(majutsu-tpl [:wrap-angle [:str "x"]])
; => "concat(\"<\", \"x\", \">\")"Owner-bound methods can also be defined locally in the DSL. A body-less
majutsu-template-defmethod / majutsu-template-defkeyword declaration just
registers metadata for a native/rendered method, but providing a body turns it
into a local owner-bound lowering:
(majutsu-template-defkeyword canonical-log-id Commit
(:returns Template)
[:if [:or [:hidden]
[:divergent]]
[:commit_id :shortest 8]
[:change_id :shortest 8]])
(majutsu-tpl [:canonical-log-id] 'Commit)
; => "if((self.hidden() || self.divergent()), self.commit_id().shortest(8), self.change_id().shortest(8))"
(majutsu-tpl [:method [:raw "p" :Commit] :canonical-log-id])
; => "if((p.hidden() || p.divergent()), p.commit_id().shortest(8), p.change_id().shortest(8))"The DSL supports Jujutsu’s type system: Any, String, Boolean, Integer,
Template, Commit, Signature, Timestamp, List, Option, and more.
Type annotations can be added to :raw nodes:
[:raw "self" :Commit] ; Declares the raw value has type CommitWithout an annotation, :raw remains Unknown. This keeps the DSL honest:
raw snippets still compile, but richer type propagation only kicks in once the
expression is explicitly typed or inferred through later semantic steps.
Majutsu distinguishes between broad Any expression placeholders and the
narrower printable Template capability. In practice, Any means “some
expression”, while Template means content that can actually be rendered or
concatenated.
Majutsu also propagates core result types through the normalized AST. For
example, parents() is tracked as a list of commits, lines() as a list of
strings, trailers() as (:list Trailer), mapped lists use ordinary
container refs such as (:list String), and list methods such as first()
preserve the element type.
A few upstream categories are still intentionally simplified in Majutsu’s
checker. For parameters that upstream models as StringLiteral, Majutsu
currently recognizes obvious literal strings, but it does not try to prove that
more complex expressions become literals after helper expansion or constant
folding. Upstream also has distinct AnyList-style result categories; Majutsu
currently models those as ordinary container refs instead.
Templates can be used with these jj commands:
| Command | Self Type |
|---|---|
| log | Commit |
| show | Commit |
| evolog | CommitEvolutionEntry |
| diff | TreeDiffEntry |
| bookmark list | CommitRef |
| tag list | CommitRef |
| file annotate | AnnotationLine |
| file list | TreeEntry |
| file show | TreeEntry |
| op log | Operation |
| op show | Operation |
| workspace list | WorkspaceRef |
| config list | Named keyword fields; value is ConfigValue |
Unlike the object-oriented rows above, jj config list exposes named
keywords (name, value, overridden, source, and path) rather than a
single ConfigValue receiver.