In short: jj resolves the operation (default @), loads the repository at it and at its
parent, renders the operation with the templates.op_show template, and hands both
repositories to the same show_op_diff that
jj operation diff uses. A merge operation, one
with several parents, gets the template only and no diff. It never writes a new operation head.
The command is cmd_op_show. The help text: "Show changes to
the repository in an operation" (source). It is registered as
Show(OperationShowArgs) in the operation group
(source, dispatch). In effect it is
jj op diff --op OP with the operation's own template printed above the diff instead of the
"From operation" and "To operation" lines.
1. Command-line surface
| Flag | Meaning |
|---|---|
OPERATION (positional) | "Show repository changes in this operation, compared to its parent(s)". Defaults to @, the current operation, and completes operation ids (source). |
-G, --no-graph | "Show a flat list of modified changes instead of a graph" (source). |
-T, --template TEMPLATE | Render the operation with this template instead of templates.op_show; all zero-argument methods of the Operation type are keywords (source). |
-p, --patch | "Show patch of modifications to changes". "If the previous version has different parents, it will be temporarily rebased to the parents of the new version, so the diff is not contaminated by unrelated changes" (source). |
--no-op-diff | "Do not show operation diff": print the template only. Conflicts with -p and every diff format flag (source). |
Diff format flags (--summary, --stat, --git, …) | The shared DiffFormatArgs (source); see jj diff. Any of them turns patches on, -p or not (source). |
--show-changes-in REVSETS | "Show only changed revisions matching the given revset expression", defaulting to revsets.op-diff-changes-in (source). |
The global flags are on the CLI page.
2. Configuration
| Setting | Default | Effect |
|---|---|---|
templates.op_show | builtin_op_log_compact | The operation's own text, labelled op_show and operation (default, source). It is the same default as templates.op_log; see jj operation log for what it prints. |
templates.commit_summary | Each commit line of the diff, labelled op_show and commit (source). | |
revsets.op-diff-changes-in | mutable() | immutable_heads() | Which changed commits are listed; the rest are counted as elided (default, source). See elision. |
ui.graph.style, ui.log-word-wrap, ui.diff-formatter, ui.pager | Graph style, wrapping, default diff format, and the pager, which the command requests (source, source). See jj log. |
3. End-to-end flow
4. Core architecture touchpoints
| Piece | Role here |
|---|---|
resolve_single_op | Resolves the operation argument against the current operation; see operation expressions. |
merge_operations | "If operations is empty returns the root operation, if it contains a single entry returns that entry, otherwise merges the operations into a single operation". It is passed the current workspace's name but no ui or description, so it prints nothing; the library writes a "merge N operations" operation that is never made an op head (source, source). |
parse_operation_template | Compiles -T or templates.op_show against the Operation type. |
diff_formats_for_log | Turns -p and the format flags into the list of diff formats; an empty list means no patches (source). |
show_op_diff | The comparison, shared with jj op diff and jj op log --op-diff; described on the operation diff page. |
5. What it reads, and snapshotting
It goes through workspace_helper, so the working copy is snapshotted first unless
--ignore-working-copy is given (source). A snapshot writes
an operation, which is then @, so jj op show right after editing files shows that
snapshot. It reads the operation and its parents from the op store, the views and commit indexes of both
repositories, and the commits it prints; with a diff format, also their trees and file contents.
Unlike jj op diff, it does not merge one index into the other: the operation's own repository
is the one commits are looked up in (source). With a single parent that
is enough, since the parent's commits are already in its index. A merge operation is skipped because
"Merged repo may have newly rebased commits, which wouldn't exist in the index"
(source). Nothing is published; a merged parent operation is written to
the op store but is not an op head.
6. Algorithm and invariants
The comparison base
The diff is always against the operation's parents, merged: no parents (the root operation) compares the
root with itself and prints nothing after the template; one parent is compared directly; several are merged
but then not compared (source, source).
To compare any two operations, use jj op diff --from … --to ….
What is printed
First the template output, then show_op_diff's sections, each starting with a blank line and
skipped when empty: changed commits, as a graph or with -G flat; changed working copies; local
bookmarks and tags; remote bookmarks and tags (source,
source). How commits are classified as new, rewritten or abandoned,
which commits are elided, and how patches are inter-diffs against the first predecessor, is on the
operation diff page.
The template
The built-in template ends with a newline, so a blank line separates it from the diff. A custom template without one runs straight into the diff's own leading newline, which upstream's test shows as "Changed commits:" directly under the template text (test).
7. Errors and edge cases
| Situation | Behaviour |
|---|---|
| The root operation | Only 000000000000 root() (test). |
| A merge operation | The template only, with no diff and no message (source). |
--no-op-diff with -p or a format flag | Rejected by clap (source). |
| The operation matches nothing, or several | An operation resolution error, as for jj op diff. |
| Invalid filter revset | Invalid `--show-changes-in` expression: …, or a config error Invalid `revsets.op-diff-changes-in` (source). |
| The filter names something missing in the parent | "Warning: Could not resolve revset expression for elision" with the hint (Use --show-changes-in=all() to see all changes); the commits section is skipped and the rest still print (source, test). |
8. Example output
After jj git fetch and jj bookmark track bookmark-1, upstream's test shows the
latest operation (source):
$ jj op show
3ffa5350aafc test-username@host.example.com default@ 2001-02-03 04:05:10.000 +07:00 - 2001-02-03 04:05:10.000 +07:00
track remote bookmark bookmark-1@origin
args: jj bookmark track bookmark-1
Changed local bookmarks:
bookmark-1:
+ pukowqtp 0cb7e07e bookmark-1 | Commit 1
- (absent)
Changed remote bookmarks:
bookmark-1@origin:
+ tracked pukowqtp 0cb7e07e bookmark-1 | Commit 1
- untracked pukowqtp 0cb7e07e bookmark-1 | Commit 1
A snapshot operation with a Git-format patch, after writing a file and running jj new
(source):
$ jj op show @- -p --git
494872ff8b1e test-username@host.example.com default@ 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
snapshot working copy
args: jj new
Changed commits:
○ + qpvuntsm 6b57e33c (no description set)
- qpvuntsm/1 e8849ae1 (hidden) (empty) (no description set)
diff --git a/file b/file
new file mode 100644
index 0000000000..7898192261
--- /dev/null
+++ b/file
@@ -0,0 +1,1 @@ a
+a
Changed working copy default@:
+ qpvuntsm 6b57e33c (no description set)
- qpvuntsm/1 e8849ae1 (hidden) (empty) (no description set)
See also jj operation log and operations.