In short: jj loads the workspace, which snapshots it, and evaluates the revsets (default
@). It picks the template (-T, else templates.show) and the diff
formats (the flags, else ui.diff-formatter). For each commit it renders the template, then
diffs the merged tree of the commit's parents against the commit's own tree, with copy records from
each parent. Nothing is written: no transaction, no operation.
The command is cmd_show. The help text: "Show
revision metadata and diff" (source). Unlike
jj log, it draws no graph and shows the diff unless --no-patch is given.
1. Command-line surface
| Argument | Type | Default | Meaning |
|---|---|---|---|
REVSETS (positional, repeatable), or the hidden -r REVSETS | revset | @ | "Show changes in these revisions, compared to their parent(s)". Positional and -r values are unioned (source, source). |
--reversed | flag | off | Older revisions first (source). |
-T, --template TEMPLATE | template | templates.show | How to render each commit; every 0-argument method of Commit is a keyword (source). |
--no-patch | flag | off | Don't show the diff. Conflicts with every diff format flag (source). |
Short formats: -s/--summary, --stat, --types, --name-only | flags, at most one | none | Per-path summary, histogram, type letters, or path only (source). |
Long formats: --git, --color-words | flags, at most one | none | A Git-format or word-level diff. One short and one long format can be combined (source). |
--tool NAME | string | none | An external diff command, or :name for a built-in format (source). |
--context N | integer | diff.git.context / diff.color-words.context | Lines of context (source). |
-w/--ignore-all-space, -b/--ignore-space-change | flags | off | Whitespace handling when comparing lines; show gives them their short names (source, source). |
Global flags such as --at-op and --ignore-working-copy are on the CLI page.
2. Configuration
| Key | Default | Effect |
|---|---|---|
templates.show | builtin_log_detailed | The header for each commit (source): commit and change ID, bookmarks, tags, author, committer, an optional signature line, then the indented description or (no description set) (source). |
ui.diff-formatter | :color-words | The format when no diff flag is given (source). A :name picks a built-in; anything else is an external tool (source). |
diff.color-words.* | conflict = "materialize", max-inline-alternation = 3, context = 3 | Options for the default format (source). |
diff.git.* | context = 3, show-path-prefix = true | Options for --git (source). |
ui.conflict-marker-style | diff | How conflicted files are written out when diffed (source, source). |
ui.show-cryptographic-signatures | false | Adds a Signature: line to the default template (source). |
ui.pager, ui.paginate | less -FRXK, auto | Output goes through the pager (source). |
3. End-to-end flow
4. Core architecture touchpoints
| Structure | Read / write | How | Explained on |
|---|---|---|---|
| Revset engine | read | Parses and evaluates the revsets (source). | revsets |
| Commit index | read | Orders the commits, and its changed-path data can show a commit is empty without merging trees (source). | index |
| Commits | read | Each shown commit, for the template, and its parents. | commits |
| Trees | read | The commit's tree and its parents' merged tree, diffed path by path. | trees |
| Copy records | read | From the backend, per parent; shown as (old => new) (source). | trees |
| Conflicts | read | Merge parents are merged recursively; conflicted files are materialized for the diff. | conflicts |
| View | read | @, bookmarks and tags feed the revset and the template. | view |
| Operation log | untouched | No transaction is started. | operations |
5. What it reads, and snapshotting
- Snapshot: yes. It loads the workspace with
workspace_helper(source), which snapshots the working copy first (source), sojj showincludes current edits to@. That snapshot may record asnapshot working copyoperation;showitself records nothing. - Reads: the view at the current (or
--at-op) operation, the index, each shown commit and its parents, their trees, file contents for the diff, and copy records. - Output: through the pager, requested after evaluation and before the first commit is printed (source). The template output is labelled
show commitfor colours (source).
6. Algorithm and invariants
Choosing the formats
diff_formats_for reads at most one short and one long
format from the flags (source). --tool=:name fills the
slot of that built-in's kind; any other --tool fills the long slot with an external tool.
If both slots are empty, it uses ui.diff-formatter. So the list is never empty, and
--stat --git prints a histogram followed by a Git diff. Each format then makes its own pass
over the tree diff, in that order (source).
What a commit is diffed against
show_patch diffs parent_tree() against the
commit's tree. If the index's changed-path data says the commit changed nothing, the parent tree is the
commit's own tree and the diff is empty at no cost (source). A single
parent's tree is used as it is. For a merge, the parents' trees are merged with their merge bases, found
recursively (parent_tree,
merge_commit_trees_no_resolve), and resolved where possible. The diff therefore shows only
what the merge commit itself changed, not everything each side brought in.
Copies and renames come from the backend's copy records between each parent and the commit
(source), filtered by the matcher, which for show is
EverythingMatcher (source). The tree diff pairs them
up, so a rename is printed once as file1 => file3 instead of a deletion and an addition.
Order
Commits are streamed in index order, newest first (source), not
topologically grouped as in jj log. --reversed has to collect the whole stream
first to flip it (source). There is no limit flag.
Invariants: each commit in the set is printed once, header then diff. An empty revset prints nothing and is not an error (test). Nothing in the repository changes.
7. Errors and edge cases
| Message | When | Source |
|---|---|---|
error: a value is required for '--template <TEMPLATE>' but none was supplied, with a hint listing template aliases | jj show -T with no value. | test |
| A clap "cannot be used with" error | --no-patch with a diff flag; two short or two long formats; -w with -b. | show.rs, diff_util.rs |
--tool=<name> cannot be used with --<format> | --tool asks for a format whose slot a flag already filled. | diff_util.rs |
Invalid builtin diff format: <name> | --tool=:name or ui.diff-formatter names no built-in format. | diff_util.rs |
The tool `<name>` cannot be used for diff formatting | --tool or ui.diff-formatter names a configured tool whose diff-args is empty. | diff_util.rs |
Failed to generate diff | An external diff tool failed. | diff_util.rs |
| Revset or template parse errors | Bad revset, -T, or templates.show. | cli_util.rs |
| (empty output) | The revset is empty, e.g. jj show 'none()'. | test |
8. Example output
From upstream's test_show_basic: @ deletes
file1, edits file2, and adds file3 with file1's content,
which the Git backend reports as a rename. The default :color-words format:
$ jj show
Commit ID: 92e687faa4e5b681937f5a9c47feaa33e6b4892c
Change ID: rlvkpnrzqnoowoytxnquwvuryrwnrmlp
Author : Test User <test.user@example.com> (2001-02-03 08:05:09)
Committer: Test User <test.user@example.com> (2001-02-03 08:05:09)
(no description set)
Modified regular file file2:
1 1: foo
2 : baz qux
2: bar
3: baz quux
Modified regular file file3 (file1 => file3):
Several revisions, from test_show_multiple_revisions. Newest first,
and with --reversed oldest first (source):
$ jj show --reversed --no-patch ..
Commit ID: 456f4073ef89a8fbee17002de8cd8429f35e7dc9
Change ID: qpvuntsmwlqtpsluzzsnyyzlmlwvmlnu
Author : Test User <test.user@example.com> (2001-02-03 08:05:08)
Committer: Test User <test.user@example.com> (2001-02-03 08:05:08)
add file1
Commit ID: 9155a29f167b8735506b1c10858a4abb9ef80e21
Change ID: rlvkpnrzqnoowoytxnquwvuryrwnrmlp
Author : Test User <test.user@example.com> (2001-02-03 08:05:09)
Committer: Test User <test.user@example.com> (2001-02-03 08:05:09)
add file2
Commit ID: 997b28cbf801ff095e70f203f75731c955688350
Change ID: kkmpptxzrspxrzommnulwmwkkqwworpl
Author : Test User <test.user@example.com> (2001-02-03 08:05:10)
Committer: Test User <test.user@example.com> (2001-02-03 08:05:10)
modify file1
A custom template replaces the header entirely (source):
$ jj show -T description
a new commit