Skip to content

Inspecting

Use this page to understand the log: how revision rows are rendered, how point and relation navigation work, and how to copy structured fields. The separate Files, Diffs, and Operations page continues from a selected revision into diffs, blobs, annotations, Ediff, process output, and Jujutsu's operation log.

Log Buffer

The log buffer is the heart of Majutsu. It displays the history graph using a custom template DSL that mirrors the jj log output but adds interactivity.

Log Display

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.

Log Options Transient

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(), or A | 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.

Revision Selection

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.

Log Output Protocol (Sequential Markers)

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:

  • \x1d for module markers

  • \x1e for field boundaries

  • \x1f for 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 \x1f and decode them in postprocessing.

Parsing is strictly sequential and positional (not key/value based):

  1. Detect \x1dS and lock the entry indent column.

  2. Collect heading lines until \x1dT is found.

  3. Parse T -> B -> M -> E payload segments in order on the final heading line (no extra physical line breaks between these module markers).

  4. Keep lines between current entry E and next entry S attached to the current section heading area (graph continuation lines).

majutsu-log-commit-columns

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, or metadata.

:template

Required Majutsu template form, or a bound template variable.

:face

Optional highlighting policy; defaults to t.

:post

Optional postprocessor function, function list, :default, or nil.

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.

Module Semantics

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.

Row Metadata

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.

Face Policy (:face)

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).

Postprocessing (:post)

: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-VALUE

CTX 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.

Configuration Examples

Minimal columns

(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)))

Keep heading multiline + encoded body multiline

;; 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"])

Custom postprocessor

(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))

Copying From Structured Row Buffers

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, or body), 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.