@@ -9,7 +9,7 @@ Nothing is implicit beyond the language itself. Every shape, builtin, and
99command is declared or defined in one user-owned file, `~/.config/j/config.j`,
1010written in the language. The binary provides the language (its syntax, its
1111operators and their fixities, its primitive kinds), the implementations of the
12−declared builtins, eight reserved words, and the bridge to jj repositories.
12+declared builtins, eleven reserved words, and the bridge to jj repositories.
1313`j` does not require the `jj` binary; it uses jj's on-disk formats through
1414jj-lib.
1515
@@ -40,10 +40,19 @@ echo EXPRESSION | j
4040 `::`, `or`, `.`, and the literals `%name`, `@id`, `./a/b` are all safe
4141 unquoted.
42423. If arguments are present and rule 1 also applies, exit 2 with
43− `j: expression given both as arguments and on stdin`.
44−4. If neither is present, print a one-line usage message to stderr and exit 2.
45−5. `j` accepts no flags. Any argument, including ones beginning with `-`, is
46− part of the expression.
43+ `j: expression given both as arguments and on stdin`. One byte is
44+ sufficient to reject the invocation; the pipe need not reach EOF.
45+4. If neither is present, or the input is only whitespace, print a concise
46+ usage and getting-started guide to stderr and exit 2. The guide points to
47+ `j help`, `j commands`, and commands for starting or inspecting a repository.
48+ It does not read or create configuration or open a repository.
49+5. `--help` and `-h` as the first word alias `help` (§1.1). A separate
50+ argument equal to either flag after the first argument requests help for
51+ the first word, before repository access or evaluation: `j abandon --help`
52+ is `j help abandon`. An initial `help`, `--help`, or `-h` followed by
53+ another help flag opens general help. Other flags are not supported.
54+ Language comments embedded within a single argument or stdin are unchanged:
55+ `j 'new --help'` executes `new`, and `j '"--help"'` prints text.
47566. The expression is text: an argument that is not valid UTF-8 is a usage
4857 error (exit 2) naming it, and so is an expression on standard input that
4958 is not.
@@ -52,10 +61,14 @@ echo EXPRESSION | j
5261
5362Before the expression is parsed, its text is trimmed of leading and trailing
5463whitespace and split on whitespace into words. If the first word is one of the
55−eight reserved words below, the whole text is a reserved command.
64+eleven reserved words below, the whole text is a reserved command. A first word
65+of `--help` or `-h` is treated as `help`.
5666
5767| command | operation |
5868|---|---|
69+| `help [NAME]` | show the command guide, or help for a reserved command or config name; §1.1.1 |
70+| `commands [QUERY]` | show common tasks, search the vocabulary, or use `all` for the full catalog; §1.1.1 |
71+| `setup [NAME EMAIL]` | create the complete reference configuration, optionally setting identity; §6.1.1 |
5972| `init` | create a repository in the current directory; §7.8 |
6073| `clone URL [DIR]` | clone `URL` into `DIR` (default: the repository's name in `URL`); §7.8 |
6174| `remote URL` | set the URL of the remote `origin`, creating it if absent; §7.8 |
@@ -65,24 +78,64 @@ eight reserved words below, the whole text is a reserved command.
6578| `redo` | reverse the newest undo; §7.7 |
6679| `ops` | print the operation log; §7.7 |
6780
68−For every reserved command except `push`, the remaining words are literal
69−arguments (no quoting, no escaping); a wrong number is a usage error (exit 2)
70−and the language is not involved. For `push`, the remainder of the text is an
71−expression, evaluated as in §1.2 steps 2–7 but against the repository as
72−recorded, without snapshotting the working directory; its value must be a list
73−of push records (§7.6).
81+For every reserved command except `push` and `commands`, the remaining words
82+are literal arguments, and the language is not involved. When the first
83+shell argument is the command name, the remaining shell argument boundaries
84+are preserved, so `j clone '/path/to/source repo' 'my project'` accepts both
85+paths. A command supplied as one string or on stdin is split on whitespace;
86+it has no additional quote or escape syntax. A wrong argument count is a
87+usage error (exit 2). `commands` takes the remaining
88+trimmed text as its optional search query, including spaces. For `push`, the
89+remainder of the text is an expression, evaluated as in §1.2 steps 2–7 but
90+against the repository as recorded, without snapshotting the working directory;
91+its value must be a list of push records (§7.6).
7492
7593Matching is on the first word only, so an expression whose first token is one
7694of these words cannot be written on the command line. `(push)` or `x fetch`
7795are ordinary expressions. `config.j` may define functions with these names;
7896they are reachable only inside larger expressions.
7997
98+#### 1.1.1 Help and command discovery
99+
100+`help` without a name prints a getting-started guide to stdout: repository
101+setup, common commands grouped by task, examples, expression quoting and
102+composition, dry runs, and how to find more commands. `help NAME` prints help
103+for that reserved command or for a name in the current configuration, including
104+its signature when present and available documentation. `--help [NAME]` and
105+`-h [NAME]` are equivalent to `help [NAME]`. These successful requests exit 0.
106+An unknown name is a usage error (exit 2) with guidance to use `j commands`.
107+`j help config` is a built-in topic showing the configuration path, identity
108+setup, and repair instructions. It is available even with invalid configuration.
109+
110+`commands` shows a concise catalog of common tasks and their commands.
111+`commands all` lists the reserved commands and vocabulary actually declared
112+or defined in the current configuration, including custom definitions without
113+signatures. The concise catalog includes a bounded selection of custom
114+definitions and points to the full list. `commands QUERY` searches the catalog case-insensitively by name,
115+signature, documentation comments, and common task synonyms such as `commit`,
116+`checkout`, and `branch`, ranking direct matches and everyday commands first.
117+The `stash` query explains the save/new workflow when no custom `stash`
118+definition exists. The catalog does not present an
119+undeclared builtin as available merely because the binary implements it.
120+
121+General help and help for reserved commands do not depend on configuration and
122+remain available if it is unreadable or invalid. Discovery of configuration
123+names reads the configured file (§6.1) without creating or modifying it. If the
124+file is missing, it uses the embedded reference configuration; other read or
125+parse errors exit 3 rather than silently substituting a different catalog.
126+The config is never evaluated for discovery, so its definitions can refer to
127+repository state without preventing their documentation from being read.
128+
129+Neither command locates or opens a repository, takes a repository lock,
130+resolves commit ids, snapshots the working directory, or records operations.
131+
80132### 1.2 Evaluating the expression
81133
821341. Locate the repository (§7.1). If none, exit 2.
831352. Parse the expression (§3). On parse error, exit 3.
841363. Load and validate `config.j` (§6), except for its `Id` literals. If it is
85− missing or invalid, exit 3.
137+ missing, create the reference configuration (§6.1); if loading or
138+ validation fails, exit 3.
861394. Build the current repository value `r` (§7.2). This includes snapshotting
87140 the working directory into the focused commit (§7.4).
881415. Resolve every `Id` literal in `config.j` and in the expression (§4.10).
@@ -93,7 +146,8 @@ they are reachable only inside larger expressions.
931467. If `v` is a function, apply it to `r`, once, and let `v` be the result.
941478. If `v` is a `Repo` (§4.4): if it equals the repository value *as loaded,
95148 before the snapshot of §7.4*, do nothing at all; otherwise persist it
96− (§7.5) and check out its focused commit's files (§7.4). Print nothing.
149+ (§7.5) and check out its focused commit's files (§7.4). Print nothing to stdout;
150+ interactive stderr reports the outcome and focus (§1.5).
97151 Exit 0.
981529. Otherwise display `v` (§5.1) on stdout. Persist nothing. Exit 0.
99153
@@ -103,7 +157,7 @@ persist nothing, leave the working directory untouched, and exit 1.
103157Step 7 applies exactly once. If the result of applying a function to `r` is
104158again a function, that function is displayed, not applied again.
105159
106−Step 8's equality test means `j id` is a no-op when the working directory is
160+Step 8's equality test means `j save` (the reference alias for `id`) is a no-op when the working directory is
107161unchanged and records it when it is not: the snapshot makes `r` differ from
108162the loaded value, and that difference is what gets persisted. The test may be
109163implemented by comparing commit and tree hashes rather than values.
@@ -115,9 +169,11 @@ Two consequences worth knowing:
115169 what a rebase would produce without doing it. `validate` in the chain,
116170 `j tree . validate . rebase trunk`, additionally raises whatever
117171 persistence would refuse, so the preview is of something that could
118− actually be persisted.
119−- **Do, then look.** `j squash` prints nothing. `j squash && j tree` is the
120− idiom; there is no flag for it.
172+ actually be persisted. The reference `preview EDIT` is the convenient
173+ form of `tree . validate . EDIT`: `j 'preview (rebase trunk)'`. Validation
174+ alone returns a `Repo`, so `validate . EDIT` persists a changed result.
175+- **Do, then look.** `j squash` reports the resulting focus on an interactive
176+ terminal. `j squash && j tree` displays the whole tree; there is no flag for it.
121177
122178### 1.3 Interrupts
123179
@@ -170,6 +226,49 @@ is in `goto`. A builtin is not a definition, so a crash in one is in
170226whichever definition applied it, and with none executing the line is only
171227` from <expression>`.
172228
229+### 1.5 Terminal presentation
230+
231+Automatic colors are used only on a terminal, and disabled by `TERM=dumb`
232+or a nonempty `NO_COLOR`. Diagnostics use the stderr terminal; displayed
233+values use stdout. Error prefixes are bright red, informational prefixes
234+bright cyan, and interactive successful commands have a green checkmark.
235+Color adds emphasis without replacing the text of errors or values.
236+Successful edits report `Saved changes` or `No changes`, followed by the
237+resulting focus's copyable, unique `@` prefix and first description line
238+(`(no description)` when empty). Setup reports the effective author identity.
239+Undo and redo identify the affected operation and restored focus.
240+After a saved edit, conflicts newly introduced anywhere in the history and
241+conflicts in the checked-out focus produce a warning with counts, paths, and
242+resolution guidance. This warning remains visible when output is redirected;
243+the operation still succeeds. Guidance identifies `j conflicts`, editing
244+conflict markers, recording the resolution with `j save` or `j id`, and
245+checking again. For another commit, navigate with `by` first.
246+
247+When both stdout and stderr are capable terminals, repository operations,
248+configuration evaluation, and expression evaluation (including implicit application)
249+may show a temporary, color-cycling progress line on stderr. It starts after
250+140 ms, advances every 75 ms, and names the current operation alongside a
251+spinner, a bouncing activity bar, and elapsed time since that operation began.
252+The bar is indeterminate and does not estimate completion. Each frame fits
253+the current stderr terminal width with a spare column, omitting the bar and
254+then the time before truncating the operation's name by display width. The
255+line is cleared before the command reports its result.
256+Fast operations never wait for a progress frame. It does not hide
257+the cursor or change terminal modes. Signal behavior remains as in §1.3.
258+Before an external renderer inherits stderr, progress is cleared and stopped
259+for the remainder of the run so even an unterminated diagnostic stays visible.
260+
261+Colored multiline output is revealed in at most seven batches, with at
262+most 96 ms of total pacing. Its bytes and row order are unchanged. Plain
263+text is written directly. `J_ANIMATIONS=never` (also `off`, `false`, or `0`,
264+case-insensitive) disables all motion while retaining colors. Motion is
265+also disabled by nonempty `NO_COLOR` or `CI`, `TERM=dumb`, or either output
266+stream being redirected. Success messages are omitted only when `CI` is
267+nonempty or either output stream is redirected. `NO_COLOR` and `TERM=dumb`
268+retain plain-text success messages on terminals; `J_ANIMATIONS` alone does
269+not suppress them.
270+Broken pipes and failed writes keep the behavior in §1.4.
271+
173272---
174273
175274## 2. Values
@@ -429,7 +528,9 @@ negation. Ordering operators are defined on `Int` only and crash otherwise.
429528
430529`if`, `or`, `&&`, and `||` do not evaluate all operands. `&&` and `||`
431530short-circuit. The interpreter recognises `&&` and `||` by name; `or` is a
432−keyword.
531+keyword. Like other builtins, `&&` and `||` must be declared. Their contracts
532+check each evaluated operand and the final result; a skipped operand is
533+neither evaluated nor checked.
433534
434535`a or b` evaluates `a`. If that crashes, the crash is discarded and `b` is
435536evaluated and returned. Otherwise, if the value of `a` is a function, `b` is
@@ -495,15 +596,19 @@ as in §4.13.
495596| `replay onto ch` | replay the change `ch` (`{ from, to }`) onto the snapshot `onto`; §7.3 |
496597| `unresolved b` | `true` iff the blob is a conflict |
497598| `blob t` | a resolved regular-file blob with content `t` |
599+| `blobHex kind hex` | a resolved blob of kind `"regular"`, `"executable"`, or `"symlink"`, with bytes decoded from an even-length hexadecimal text; accepts either letter case, crashes for an unknown kind or invalid hex |
498600| `text b` | the content of a blob as text; for an unresolved blob, its conflict-marker rendering |
499601| `by i repo` | the repo refocused on the commit with id `i`; crashes if absent. Backed by the change-id index; its reference definition is in §10 |
602+| `lookupCommit i repo` | `(by i repo).root`, indexed once per repository value without constructing a location for each lookup. Duplicate ids select the first preorder match; malformed trees retain `by`'s traversal behavior |
603+| `lookupContent p s` | `(head (filter (\e -> e.path == p) s)).content or blob ""`, with an index for ordinary paths. The full filter's errors and first-match behavior are preserved; the reference `contentAt` uses this builtin |
500604| `subtreeCommits t` | every commit of the tree `t` (anything with `root` and `children`) in preorder, in time linear in its size: the value of `t.root :: (concat (map subtreeCommits t.children) or [])`. So it crashes only if `t` has no `root`, and a node whose `children` is not a list of values that have a `root` contributes only its own. The reference `commits` is this builtin (§9) |
501605| `diff a b` | a unified diff from the text of `a` to the text of `b`, split into lines after each `\n` (not at a lone `\r`), no `---`/`+++` header lines, three lines of context, each hunk headed `@@ -s,n +s,n @@` with the first line and the number of lines it shows of each side, as `diff -u` writes it, a line that has no trailing newline followed by `\ No newline at end of file`; `""` if equal; crashes if either is not UTF-8 |
502−| `difft p a b` | the output of difftastic comparing `a` to `b` as the file `p`; §7.10. Crashes if `difft` is not on `PATH` |
606+| `difft p a b` | the output of difftastic comparing `a` to `b` as the file `p`, with a unified-diff fallback when the executable is absent; §7.10 |
607+| `unrecorded repo` | paths changed between the invocation's loaded and snapshotted focus files; §7.4. Returns `[]` without snapshot context, including in a push expression |
503608| `treeWith o r` | the history rendered as a tree with options record `o`; §7.11 |
504609| `extract S v` | every subvalue of `v` that is an `S`; §4.12 |
505610| `meta i` | `{ hash, author, email, time }` for the visible commit with change id `i`: its current git hash, author name and email, committer time. Crashes for an id with no stored commit (one minted in this program) |
506−| `validate repo` | `repo` unchanged if persisting it would succeed; otherwise the crash persistence would raise (§7.5 steps 1–3 and 6). Persistence runs the same checks |
611+| `validate repo` | `repo` unchanged if persisting it would succeed; otherwise the crash persistence would raise (§7.5 steps 1–3, the root invariant of 4, and 6). A changed repository also checks the backend's author identity (§7.9). Persistence runs the same checks |
507612
508613### 4.10 Ids: existing and new
509614
@@ -592,6 +697,11 @@ signature is a crash, not a reason to try the other side. A signature on
592697a definition that is not a function (`user`, `conflicts`) is checked once,
593698against the value, when the definition is evaluated at load.
594699
700+Infix `&&` and `||` must be declared like other builtins. Their contracts
701+check each operand that is evaluated and the result, including a result
702+decided by the left operand alone. A skipped right operand is neither
703+evaluated nor checked.
704+
595705The check is one level deep and never inspects inside a list or a record's
596706field values:
597707
@@ -635,7 +745,9 @@ indented under their key or bullet. Colour follows §7.11's rules and is used
635745only when stdout is a terminal; nothing is conveyed by colour alone.
636746
637747Shapes are recognised by their exact field set, as in §4.12: `Commit`, `Entry`,
638−`Subtree`, `Repo`, `Frame`, `Change`. Any other record is generic.
748+`Subtree`, `Repo`, `Frame`, `Change`, checked in that order using their
749+configured definitions. Additional type aliases do not displace these
750+display forms. Any other record is generic.
639751
640752| kind | line form | block form |
641753|---|---|---|
@@ -660,6 +772,21 @@ Shapes are recognised by their exact field set, as in §4.12: `Commit`, `Entry`,
660772| list of other scalars | `a, b, c` | same, wrapped at the terminal width |
661773| any other list | `n items` | one block per item, separated by a blank line |
662774
775+Column tables use copyable shortest unique `@` prefixes for Id cells and
776+show the actual contents of Text-list cells (such as bookmark names), not
777+item counts. Empty Text-list cells are blank. On a terminal, columns are
778+fitted to the available width; if minimum column widths cannot fit, rows
779+are shown vertically with wrapped values. Piped tables retain untruncated
780+cell contents. These presentation choices do not change `show` or values.
781+
782+The reference status record includes `unrecorded : [Path]` alongside
783+`changed : [Path]`. The display labels `changed` as `in commit` to distinguish
784+changes against the commit's parent from file edits since the last recording.
785+Its empty fields read `No changes`, `No conflicts`, `No labels`,
786+`No description`, and `All edits recorded` for empty `unrecorded`.
787+Nonempty paths and labels remain visible. Presentation does not rename the
788+underlying `changed` field. Older five-field status records remain displayable.
789+
663790An empty list of any kind prints `none` (dim) in block form and `0 …` in
664791line form. Author and age in a `Commit` block are read from jj's commit
665792metadata by id;
@@ -683,12 +810,13 @@ Examples:
683810
684811```
685812$ j status
686−changed src/lexer.rs
687− src/parser.rs
688−conflicts src/lexer.rs
689−id wqzt·kpqxmnrvyxskptlmzzzzabcd
690−labels feature
691−message wip
813+in commit src/lexer.rs
814+ src/parser.rs
815+conflicts src/lexer.rs
816+id wqzt·kpqxmnrvyxskptlmzzzzabcd
817+labels feature
818+message wip
819+unrecorded All edits recorded
692820
693821$ j conflicts
694822⊗ wqzt wip feature
@@ -731,14 +859,19 @@ j: crash: expected one revision, got 0
731859
732860### 5.2 `show`
733861
734−`show v` renders `v` as an expression in the language, such that for every
735−value except closures, unresolved blobs, and values nested past the parser's
862+When it succeeds, `show v` renders `v` as an expression in the language, such
863+that for every value except closures, unresolved blobs, and values nested past the parser's
736864bound (§3.4: a list 40,000 deep, a record about 20,000), `show v` parses and
737865evaluates to a value equal to `v` under the same `config.j`. It never uses
738866colour and never truncates. A value holding a part the store cannot supply (a
739867`files` list or a file's content, §7.2) crashes `show` as it crashes any other
740868use: `show` writes no stand-in for the part.
741869
870+Binary blobs and non-regular file kinds require the declaration
871+`blobHex : Text -> Text -> Blob` in `config.j`; if it is missing, `show`
872+crashes with instructions to add it. Existing custom configs are not
873+modified automatically.
874+
742875| kind | rendering |
743876|---|---|
744877| `Int` | decimal |
@@ -747,7 +880,8 @@ use: `show` writes no stand-in for the part.
747880| list | `[v1 v2 …]` |
748881| record | `{ a = v, b = w }`, fields in ascending lexicographic order |
749882| `Id` | `@` followed by the full id, e.g. `@wqztkpqxmnrvyxskptlmzzzzabcd` |
750−| resolved `Blob` | `blob "…"` |
883+| resolved regular `Blob` with valid UTF-8 content | `blob "…"` |
884+| other resolved `Blob` | `blobHex "kind" "…"`, preserving file kind and bytes, with lowercase hexadecimal content |
751885| unresolved `Blob` | `{- unresolved -} blob "…"` with the conflict-marker rendering |
752886| builtin | its declared name, operators parenthesised: `map`, `(++)` |
753887| selector | the selector itself: `.id` |
@@ -785,14 +919,48 @@ Since `show` returns a `Text` and a top-level `Text` displays raw,
785919### 6.1 Location and loading
786920
787921`j` reads `$XDG_CONFIG_HOME/j/config.j`, defaulting to `~/.config/j/config.j`.
788−It is read by every invocation except `remote`, `fetch`, `undo`, `redo`, and
789−`ops`: expression evaluation and `push` need its definitions, `init` and
922+Expression evaluation and `push` load the configuration; `init` and
790923`clone` need `user`. When there is no repository yet (`init`, `clone`), only
791924`user` is evaluated; `Id` literals are not resolved and no other definition
792−is evaluated. If it does not exist, `j` exits 3 with
793−`j: no config at <path>; the reference config.j is installed at <share-path>`.
794−The binary is distributed with the reference `config.j` (§9) at
795−`<prefix>/share/j/config.j`; `j` never writes to the config path itself.
925+is evaluated. `remote`, `fetch`, `undo`, `redo`, and `ops` do not load configuration.
926+When a command needs configuration and none exists, the binary writes its
927+embedded reference `config.j` (§9) to that path as an editable starting point,
928+readable and writable by its owner, and reports its path. An existing file is
929+never overwritten, including when it cannot be read or contains invalid
930+UTF-8; such errors exit 3.
931+Configuration diagnostics identify the actual file; parse errors include the
932+line number and a bounded source excerpt, with a pointer to `j help config`.
933+Excerpts escape terminal controls and are capped at 160 display columns and
934+characters, with an ellipsis when truncated. Expression parse errors, including
935+push expressions, likewise identify their source line. Runtime diagnostics may
936+add typo suggestions from the actual vocabulary, quoting examples for Text
937+arguments, or missing-argument guidance derived from the command signature;
938+they never retry an expression or change its meaning or exit code.
939+An existing user configuration is not updated when the binary changes. The
940+reference file is also distributed at `<prefix>/share/j/config.j`.
941+
942+Help and command discovery are read-only exceptions (§1.1.1): general help and
943+reserved-command help need no config, while the catalog and help for config
944+names read it without evaluation. If it is missing they use the embedded
945+reference in memory without writing a file. The empty-invocation guide does
946+not read or write configuration either.
947+
948+### 6.1.1 Explicit setup
949+
950+`setup` creates the full embedded configuration at the configured path without
951+opening a repository. With no arguments, it creates an editable default and
952+reports the path and instructions to set `user`. With `NAME EMAIL`, it checks
953+the identity rules of §6.2 and writes those values into the complete reference
954+configuration, serialized as language text literals. Names containing spaces
955+are supplied as a separately quoted shell argument.
956+
957+Setup never overwrites an existing file. Without identity arguments, an
958+existing file reports its effective identity when valid, or guidance to
959+edit it otherwise. With identity arguments, an
960+existing file is a usage error (exit 2), with instructions to edit manually.
961+Argument counts other than zero or two are usage errors. Creating the file
962+also creates missing parent directories; exclusive creation preserves a file
963+created concurrently by another invocation. No repository state is changed.
796964
797965### 6.2 Validation
798966
@@ -817,6 +985,13 @@ not declared is simply unbound, and using it is a runtime crash.
817985
818986Definitions may reference each other in any order.
819987
988+Before authoring commits (`init`, `clone`, or an expression that persists a
989+change), the effective identity must also have nonblank name/email and must
990+not retain the shipped `Your Name` or `you@example.com` placeholders. Such
991+identity failures are configuration errors (exit 3) and do not persist the
992+edit. Inspection and unchanged expressions remain usable with placeholder
993+identity, so users can inspect and repair existing repositories.
994+
820995### 6.3 Definitions the interpreter reads
821996
822997Four names in `config.j` are read by the interpreter itself, not only by
@@ -845,6 +1020,10 @@ but nothing here requires the `jj` binary to be installed.
8451020Walk up from the current directory to the first directory containing `.jj`.
8461021Only the default workspace is supported; if the working directory belongs to
8471022another workspace, exit 2.
1023+The working copy must also include the whole repository: sparse patterns
1024+must contain the root path. Otherwise exit 2 without changing the patterns,
1025+files, or operations, with instructions to run
1026+`jj sparse set --clear --add .` before using `j`.
8481027
8491028### 7.2 Building the `Repo` value
8501029
@@ -948,10 +1127,11 @@ some other value in its place.
9481127 `./a` to `./a/b` replayed onto `./a/b`; the deletion of `./a/z` replayed
9491128 onto a snapshot where `./a` is a file gives that file), and it may crash
9501129 where jj carries a conflict that one of the three held at `./a`.
951−- `unresolved`, `blob`, and `text` are as in §4.9. `blob` produces a regular
952− file. File type (executable, symlink) travels with a `Blob` but cannot be
953− changed in the language. `text` of a symlink is its target; `text` of a
954− non-UTF-8 blob crashes.
1130+- `unresolved`, `blob`, `blobHex`, and `text` are as in §4.9. `blob` produces
1131+ a regular file from text. `blobHex` constructs a regular file, executable,
1132+ or symlink from its kind and hexadecimal bytes, so `show` can preserve both
1133+ content and file type. `text` of a symlink is its target; `text` of a
1134+ non-UTF-8 blob crashes. Persisting a symlink with a non-UTF-8 target crashes.
9551135
9561136### 7.4 Working-directory invariant
9571137
@@ -1047,6 +1227,14 @@ directory.
10471227
10481228Reserved commands never snapshot.
10491229
1230+The `unrecorded : Repo -> [Path]` builtin compares the invocation's loaded
1231+focus files with its snapshotted focus files, using the same path/content
1232+comparison as `changed`. The result describes the original working-copy
1233+edits, independently of changes an expression makes to its `Repo` argument.
1234+It returns `[]` when either snapshot context is absent, including reserved
1235+push evaluation. Read-only status and preview calls do not record these paths;
1236+`save = id` persists their snapshot through the normal expression path.
1237+
10501238### 7.5 Persistence
10511239
10521240Given the loaded value `old` and the result `new`, one jj operation is
@@ -1054,11 +1242,14 @@ recorded, described by the expression text (truncated to 200 characters).
10541242Within it:
10551243
105612441. **Validate shape.** `new` has the shape of a `Repo`; every `id` occurs
1057− exactly once; every snapshot has unique paths, none of them the root `./`
1058− and none both a file and a directory (§7.3); every path component of the
1059− focus, of every other commit that is not in `old`, and of every path any
1060− other commit holds that it did not hold in `old` (§7.3), is a name a
1061− checkout can create: not empty, `.`, `..`, `.git` or `.jj`, without `/`
1245+ exactly once; every commit's `message` is a `Text`; every snapshot entry
1246+ has a `path` list of texts and a `content` that is a `Blob`; every snapshot
1247+ has unique paths, none of them the root `./` and none both a file and a
1248+ directory (§7.3); every path
1249+ component of the focus, of every other commit that is not in `old`, and
1250+ of every path any other commit holds that it did not hold in `old`
1251+ (§7.3), is a name a checkout can create: not empty, `.`, `..`, `.git` or
1252+ `.jj`, without `/`
10621253 or NUL, and a name the workspace's filesystem can hold. A name the working
10631254 directory holds (§7.4) is one. A name longer than 255 bytes, the limit of
10641255 ext4, is tried on the filesystem, since exFAT, NTFS and APFS count 255
@@ -1183,10 +1374,18 @@ language: `fetch` and `push` are the only things that change them.
11831374 creating it if absent;
11841375 - `{ delete : Text }` — delete the bookmark of that name.
11851376
1377+ Before sending a valid push, inspect the working directory without saving
1378+ a snapshot or scan state. If it has unrecorded edits, print a warning to
1379+ stderr (also when piped or colors are disabled) explaining that push sends
1380+ recorded content only, and that `j id` followed by another push includes
1381+ those edits. The warning does not change the requested push's targets or
1382+ prevent deliberate publication of recorded commits.
1383+
11861384 Duplicates are merged; two records with the same `name` and different
11871385 effects are a crash. It is refused (exit 1, nothing pushed) if:
1188− - a `name` is not a valid git branch name, an `id` is not a visible commit,
1189− or a delete names a bookmark the remote does not have;
1386+ - a `name` is not a valid git branch name, an `id` is not a visible commit
1387+ or names the synthetic root (which has no Git commit), or a delete names
1388+ a bookmark the remote does not have;
11901389 - any commit that would be sent has an unresolved file or an empty
11911390 description (the commits that would be sent are the records' `id`s and
11921391 their ancestors that no bookmark on `origin` reaches as of the last
@@ -1225,47 +1424,68 @@ language: `fetch` and `push` are the only things that change them.
12251424 bookmarks, and the refused push does not touch the label. After a
12261425 successful push the labels reflect the new positions. It records one
12271426 operation, even when it sends two git pushes. An empty list sends nothing
1228− to the remote.
1427+ to the remote, records no operation, and always reports that no updates
1428+ were selected, with guidance for selecting a first bookmark. A nonempty
1429+ successful push reports published, deleted, and unchanged bookmark counts
1430+ on an interactive terminal, including an entirely unchanged selection.
12291431 If the remote accepts some of the updates and rejects others, that
12301432 operation records the accepted ones, whose labels move, and `push` exits 1
12311433 naming the rejected bookmarks and any deletes it did not send.
12321434
12331435Only the remote named `origin` is supported in this version; `fetch` and
1234−`push` exit 1 with `j: no remote origin` when it is not configured.
1436+nonempty `push` exit 1 with `j: no remote origin` when it is not configured.
12351437Authentication is git's: the ssh agent for ssh URLs, git credential helpers
12361438for https, as jj-lib's git backend provides them; `j` adds no prompting.
12371439Bringing work up to date is a separate expression, e.g. `rebase trunk`.
12381440
12391441### 7.7 Operations, undo, redo, and the lock
12401442
1241−Every persisting `j` program, and each of `fetch` and `push`, records exactly
1242−one jj operation, described by the expression or command text. Crashes record
1243−nothing, except as §7.5 step 7 says.
1244−
1245−**Kinds.** `j` marks each operation it records, in the operation's metadata,
1246−as *normal*, *undo* (naming the operation it undid), or *redo* (naming the
1247−undo it reversed). Operations recorded by other tools are treated as normal,
1248−except that an operation whose description carries jj's own undo or redo
1249−marker is treated accordingly, so mixed use chains correctly where the marker
1250−is present and degrades to "undo the newest operation" where it is not.
1251−
1252−**`undo`** finds the operation to undo: starting at the head, while the
1253−current operation is an undo, move to the operation it undid and then to that
1254−operation's parent; while it is a redo, move past the operation it reapplied.
1255−The first normal operation reached is undone by recording a new operation
1256−that restores its parent's view, marked as an undo of it. If none is found,
1257−or the one found is the operation `init` or `clone` recorded (§7.8), before
1258−which there was no repository, exit 1 with `j: nothing to undo`.
1259−
1260−**`redo`** requires the head to be an undo (possibly reached through redos);
1261−it records an operation restoring the view of the operation that undo
1262−discarded, marked as a redo. Otherwise exit 1 with `j: nothing to redo`.
1443+Every persisting `j` program, each `fetch`, and each nonempty `push` records
1444+exactly one jj operation, described by the expression or command text. A push's
1445+description includes its `push ` prefix, so an expression named `init` or
1446+`clone` cannot be mistaken for the operation that created the repository.
1447+Crashes record nothing, except as §7.5 step 7 says.
1448+
1449+**Kinds.** An operation is *normal*, *undo*, or *redo*, determined by its
1450+description. New undo and redo records use jj's current markers,
1451+`undo: restore to operation <id>` and `redo: restore to operation <id>`.
1452+The id names the exact operation whose view was restored, retaining the
1453+navigation point for later undo and redo commands.
1454+Older `j` markers (`undo: <id>` naming the discarded operation and
1455+`redo: <id>` naming the reversed undo) and jj's legacy `undo operation <id>` /
1456+`redo operation <id>` markers remain readable with their original meanings.
1457+An operation without a recognized marker is normal, so mixed use degrades
1458+to "undo the newest operation" where no marker is present.
1459+
1460+For current markers, start at the head and follow restored-operation links
1461+while their kind matches the command: undo links for `undo`, redo links for
1462+`redo`. The command then selects that operation's parent. Follow links of
1463+the same kind at the selected target too, and record the final target's id
1464+and view. Reversing direction therefore restores the opposite marker's
1465+parent, and either CLI can continue the other's undo/redo chain.
1466+
1467+**`undo`** restores the state before the operation reached by that walk.
1468+If no parent exists, or the operation reached is the `init` or `clone`
1469+operation (§7.8), before which there was no repository, exit 1 with
1470+`j: nothing to undo`.
1471+
1472+**`redo`** requires the operation reached through redo links to be an undo;
1473+it restores the state that undo discarded. Otherwise exit 1 with
1474+`j: nothing to redo`.
1475+
1476+For older `j` markers, undo follows an undo's discarded operation to its
1477+parent, or a redo's reversed undo to the operation it discarded. Redo
1478+follows a redo's reversed undo to its parent until it reaches an undo,
1479+then restores that undo's parent. This preserves pending redos when a
1480+history crosses from older markers into current ones.
12631481
12641482Both refuse, before doing anything, if the working directory holds a change
12651483the focused commit does not, as a snapshot would find it (§7.4; a stale
12661484working copy holds none where the directory holds what was last checked out
1267−or what the commit has): `j: working copy has changes not in @; run \`j id\`
1268−to record them or discard them`. Both record their operation and then check
1485+or what the commit has). The refusal explains that `j id` preserves those
1486+edits in a new operation, and the next undo reverses that recording before
1487+reaching the earlier operation; users should inspect `j ops` and can recover
1488+the recording with redo. Both record their operation and then check
12691489out the restored focus, as persistence does (§7.5 step 7): a checkout that
12701490fails leaves the operation recorded, and `redo` or `undo` goes back.
12711491Undoing a `push` restores the recorded labels and leaves the remote untouched.
@@ -1280,7 +1500,8 @@ operation is marked. Rendered in the binary like `tree`.
12801500duration.
12811501A second `j` exits 2 with `j: another j is running in this repository`.
12821502Display-only runs are not distinguishable in advance, so they take the lock
1283−too; only reserved commands that need no repository (`init`, `clone`) do not.
1503+too; reserved commands that need no repository (`help`, `commands`, `init`,
1504+`clone`) do not.
12841505The lock prevents `j` from ever producing two commits with one change id.
12851506Other tools operating concurrently are outside `j`'s control; if they produce
12861507that state, §7.2 applies.
@@ -1317,7 +1538,9 @@ that state, §7.2 applies.
13171538 it: the last component of `URL`'s path, a final `.git` component skipped,
13181539 minus `.git` (`repo` for `/srv/repo/.git`, `host:repo.git` and
13191540 `https://host/team/repo`); a `URL` that leaves no name, such as `/` or
1320− `..`, is a usage error asking for `DIR`. It requires `user`.
1541+ `..`, is a usage error asking for `DIR`. It requires `user`. Interactive
1542+ confirmation includes a shell-quoted command to enter the clone directory
1543+ and run `j status`; cloning cannot change the caller's working directory.
13211544- **`remote URL`** sets the URL of `origin`, creating the remote if it does not
13221545 exist. It does not fetch.
13231546
@@ -1348,16 +1571,27 @@ Commits are authored and committed as `user.name <user.email>` from
13481571`config.j` (§6.3). `j` reads no other configuration; jj-lib's settings object
13491572is constructed by `j` from `user` and fixed defaults. `init`, `clone`, and
13501573persistence exit 3 if `user` is missing or malformed, since that is a
1351−configuration error.
1574+configuration error. Authoring also refuses blank fields or the shipped
1575+`Your Name` / `you@example.com` placeholders (§6.2), with instructions to
1576+edit the actual config file. Read-only expressions and unchanged results
1577+remain available with placeholder identity. Successful interactive setup
1578+reports the configured identity; terminal control characters are escaped
1579+in confirmations without modifying the identity stored in commits.
1580+`validate` checks the author identity when its argument differs from the
1581+loaded repository, so validated previews report the same identity refusal
1582+before saving. That refusal is a catchable crash, like other `validate`
1583+failures. An unchanged `validate` and previews without `validate` remain usable.
13521584
13531585### 7.10 External diff rendering
13541586
13551587`difft p a b` runs difftastic. It is the only builtin that executes another
13561588program.
13571589
1358−1. Write the content of `a` and `b` to two temporary files whose names end in
1359− the last component of `p` (so difftastic's language detection sees the
1360− real filename), inside a fresh temporary directory.
1590+1. Write the content of `a` and `b` to files in separate `old` and `new`
1591+ subdirectories of a fresh temporary directory. Each file keeps the last
1592+ component of `p` unchanged, so valid names stay within the filesystem's
1593+ length limit and difftastic sees the real filename. A last component
1594+ that is not a filename is a crash; an empty path uses `file`.
136115952. Run `difft OLD NEW` with the process's stdin closed and its environment
13621596 inherited unchanged, except: if `DFT_COLOR` is unset and `j`'s stdout is a
13631597 terminal, set `DFT_COLOR=always`; if `DFT_WIDTH` is unset and `j`'s stdout
@@ -1367,15 +1601,24 @@ program.
136716014. Return stdout as `Text`. A non-zero exit status is not an error (difftastic
13681602 uses it to signal "differences found" under some options), so what a
13691603 `difft` that fails printed, even nothing, is the result; a failure to
1370− execute the program is a crash naming it.
1604+ execute the program because it is absent uses the fallback below. Other
1605+ launch failures are crashes naming the tool and the OS error.
1606+
1607+If the executable is absent (`NotFound`), return a unified diff with three
1608+lines of context and `--- a/PATH` / `+++ b/PATH` headers using the complete
1609+path `p`. Paths containing whitespace or control characters are quoted.
1610+Content that is not UTF-8 or contains NUL receives a binary-difference summary.
1611+Equal bytes produce empty output. This fallback is usable without installing
1612+another program or changing configuration.
13711613
13721614All rendering options are difftastic's own, set through its `DFT_*`
13731615environment variables. `j` adds no options of its own. `diff` remains
1374−available as a dependency-free unified diff; the reference `config.j` uses
1375−`difft` for display, so a user without difftastic edits one line.
1616+available as a dependency-free unified diff without path headers; the
1617+reference `config.j` uses `difft` for display and receives the automatic
1618+fallback when difftastic is absent.
13761619
1377−Packaging: difftastic is a runtime dependency; on Nix, wrap `j` with `difft`
1378−on its `PATH`.
1620+Packaging: difftastic is an optional runtime enhancement; on Nix, wrap `j`
1621+with `difft` on its `PATH`.
13791622
13801623### 7.11 Tree rendering
13811624
@@ -1399,7 +1642,8 @@ run — have no metadata: their margin cells are blank, their size bar is
13991642computed from the value, and they sort last among their siblings. The same
14001643holds for a `Commit` block in display (§5.1).
14011644
1402−Missing fields crash. `color = "auto"` means colour iff stdout is a terminal.
1645+Missing fields crash. `color = "auto"` means colour iff stdout is a terminal
1646+and `TERM` is not `dumb`.
14031647If the environment variable `NO_COLOR` is set and non-empty, colour is never
14041648used regardless of `color`. `config.j` fixes the defaults by defining `tree`.
14051649
@@ -1617,6 +1861,10 @@ Drop = { delete : Label } -- delete the remote's bookmark
16171861------------------------------------------------------------------------------
16181862
16191863(.) : (b -> c) -> (a -> b) -> a -> c -- composition, right to left
1864+-- Return a value unchanged. As a repository command, `j id` records the
1865+-- snapshotted working-copy edits in the focused commit. `j save` is the
1866+-- descriptive alias in this reference config; inspections such as `j status`
1867+-- and `j tree` do not record those edits.
16201868id : a -> a
16211869const : a -> b -> a
16221870crash : Text -> a -- abort the program with a message
@@ -1684,21 +1932,33 @@ splitOn : Text -> Text -> [Text] -- splitOn "/" "a/b" = ["a" "b"]
16841932-- file ./a would, crashes, naming the path (§7.3).
16851933replay : Snapshot -> Change -> Snapshot
16861934unresolved : Blob -> Bool
1935+
1936+-- Paths edited in the working copy since the last recorded repository state.
1937+-- `j status` includes this list; `j save` records it in the focused commit.
1938+-- This measures the working copy, even when composed after an in-memory edit.
1939+unrecorded : Repo -> [Path]
1940+touchedPaths : Change -> [Path] -- paths whose content differs, one pass;
1941+ -- blobs compared by content id, bytes never read
16871942blob : Text -> Blob -- a resolved regular file
1943+blobHex : Text -> Text -> Blob -- kind (regular/executable/symlink), hex bytes
16881944text : Blob -> Text -- its content (markers if unresolved)
16891945by : Id -> Repo -> Repo -- focus the commit with this id; crash if absent
1946+lookupCommit : Id -> Repo -> Commit -- indexed commit lookup without moving the focus
1947+lookupContent : Path -> Snapshot -> Blob -- indexed file content; an empty blob if absent
16901948subtreeCommits : a -> [Commit] -- every commit of a tree in preorder, in one
16911949 -- pass: what `commits` (below) is
16921950meta : Id -> Meta -- git hash, author, email, committer time;
16931951 -- crash for a commit not yet persisted
16941952validate : Repo -> Repo -- the repo unchanged, or the crash persisting
1695− -- it would produce (shape, labels, immutability,
1953+ -- it would produce (shape, root, labels, immutability,
16961954 -- focus). `tree . validate . rebase trunk` is an
16971955 -- honest dry run.
16981956diff : Blob -> Blob -> Text -- unified diff from the first to the second
1699−difft : Path -> Blob -> Blob -> Text -- difftastic's rendering; needs difft on PATH.
1700− -- Options via DFT_* environment variables.
1701−treeWith : { detail : Int, margin : Bool, elide : Bool, icons : Bool, color : Text }
1957+difft : Path -> Blob -> Blob -> Text -- difftastic when difft is on PATH; otherwise
1958+ -- unified diff with path headers and 3 context lines,
1959+ -- or a binary-files summary. DFT_* configures difft.
1960+treeWith : { detail : Int, margin : Bool, elide : Bool, icons : Bool, color : Text
1961+ , lanes : Int, author : Bool, date : Bool, files : Bool }
17021962 -> Repo -> Text -- the history as a tree; see the spec
17031963
17041964-- Every subvalue of a given shape, however deeply nested. A shape is a type
@@ -1784,6 +2044,9 @@ restrict = \m -> filter (\e -> m e.path)
17842044select : Fileset -> Snapshot -> Snapshot -> Snapshot -- m from a, the rest from b
17852045select = \m a b -> restrict m a ++ restrict (neg m) b
17862046
2047+-- Unresolved paths in a snapshot. For the focus, inspect `j status` or
2048+-- `j conflicted . files`, edit the listed files to remove conflict markers,
2049+-- then run `j save` and inspect `j status` again. See `j help conflicts`.
17872050conflicted : Snapshot -> [Path]
17882051conflicted = \s -> map (.path) (filter (\e -> unresolved e.content) s)
17892052
@@ -1868,6 +2131,11 @@ minus = \a b repo -> let ys = b repo in filter (\i -> not (member i ys)) (a repo
18682131matching : (Commit -> Bool) -> Revset -> Revset
18692132matching = \p rs repo -> filter (\i -> p (commitAt i repo)) (rs repo)
18702133
2134+-- Find every commit with unresolved files. `j conflicts` lists candidates;
2135+-- use `j by @ID` to focus a mutable one, then `j status` to find its paths.
2136+-- Edit each conflicted file, remove the conflict markers, and run `j save`.
2137+-- Check `j status` again; `j clean` means the focus has no unresolved files,
2138+-- not that the working copy has no unrecorded edits. `j review` shows the diff.
18712139conflicts : Revset
18722140conflicts = matching (\c -> not (null (conflicted c.files))) all
18732141
@@ -1876,7 +2144,10 @@ firstOf : [Revset] -> Revset
18762144firstOf = \rss repo -> foldl (\acc rs -> if null acc then rs repo else acc) [] rss
18772145
18782146-- The main line: whichever of these names the remote has, first one wins.
1879−-- Empty in a repository with no remote, in which case nothing is immutable.
2147+-- Empty when no fetched remote label is named main, master, or trunk;
2148+-- nothing is then immutable. Configure `j remote URL`, run `j fetch`, and
2149+-- inspect `j tree` to see remote labels. A remote with another default label
2150+-- needs this definition adjusted. Start work with j 'new . goto trunk'.
18802151trunk : Revset
18812152trunk = firstOf [%main %master %trunk]
18822153
@@ -1892,21 +2163,33 @@ immutable = ancestorsOf trunk
18922163-- Focusing. `goto` is the only place a revset must have exactly one member.
18932164------------------------------------------------------------------------------
18942165
2166+-- Move to the single commit selected by a revset.
2167+-- Example: j 'goto %feature'; use j 'new . goto trunk' to start on the main line.
18952168goto : Revset -> Edit
18962169goto = \rs repo ->
18972170 let is = rs repo
2171+ candidate = \i ->
2172+ let c = commitAt i repo
2173+ message = head (splitOn "\n" c.message)
2174+ in " " ++ show i ++ " " ++ (if message == "" then "(no description)" else message) ++ "\n"
18982175 in if length is == 1
18992176 then by (head is) repo
1900− else crash ("expected one revision, got " ++ show (length is))
2177+ else if null is
2178+ then crash "expected one revision, got 0; no matching revision. Run `j tree` to inspect available revisions. For a missing remote label such as %main or trunk, configure `j remote URL`, run `j fetch`, and check the label names in `j tree`."
2179+ else crash ("expected one revision, got " ++ show (length is) ++ "\nCandidates:\n" ++ concat (map candidate (take 8 is)) ++ (if length is > 8 then " More candidates: run `j tree`.\n" else "") ++ "Choose a mutable commit with `j by @ID`, or start a child with `j 'new . by @ID'`; replace @ID with a candidate above.")
19012180
19022181commitAt : Id -> Repo -> Commit
1903−commitAt = \i repo -> (by i repo).root
2182+commitAt = lookupCommit
19042183
19052184prev : Edit -- the parent
19062185prev = goto parents
19072186
1908−next : Edit -- the only child
1909−next = goto kids
2187+-- Move to the only child. At a tip, `j new` starts a child; when there are
2188+-- several children, choose one of the candidates with `j by @ID`.
2189+next : Edit
2190+next = \repo -> if null (kids repo)
2191+ then crash "next: this commit has no children; run `j new` to start a new child, or `j tree` to inspect the history"
2192+ else goto kids repo
19102193
19112194tip : Edit -- end of the line of only-children
19122195tip = \repo -> tip (next repo) or repo
@@ -1932,6 +2215,8 @@ eachChild = forEach kids
19322215guard : (Repo -> Bool) -> Edit -- crash unless p holds
19332216guard = \p repo -> if p repo then repo else crash "guard"
19342217
2218+-- Whether the focused commit has no unresolved files. This says nothing
2219+-- about unrecorded working-copy edits; inspect those with `j unrecorded`.
19352220clean : Repo -> Bool
19362221clean = \repo -> null (conflicted repo.root.files)
19372222
@@ -1970,11 +2255,28 @@ rewrite = \f repo ->
19702255-- Commands
19712256------------------------------------------------------------------------------
19722257
2258+-- Record the current working-copy edits in the focused commit.
2259+-- Example: j save; use j status first to inspect unrecorded paths.
2260+-- This is id restricted to a Repo: the final Repo result triggers recording.
2261+-- It does not publish anything. `j new` starts a child; `j id` records too.
2262+save : Edit
2263+save = id
2264+
2265+-- Preview an edit and validate that its result could be recorded, then show
2266+-- the resulting tree. This returns Text, so it records nothing.
2267+-- Examples: j 'preview squash'; j 'preview (rebase trunk)'.
2268+-- This helper is in the reference config. Existing custom configs are not
2269+-- upgraded automatically; add it there or use `tree . validate . EDIT`.
2270+preview : Edit -> Repo -> Text
2271+preview = \edit -> tree . validate . edit
2272+
19732273-- Set the message of the focused commit.
2274+-- Example: j 'describe "fix login"'
19742275describe : Text -> Edit
19752276describe = \m -> mapRoot (\c -> c { message = m })
19762277
19772278-- Start an empty child of the focus and move to it.
2279+-- Examples: j new; j 'describe "next change" . new'
19782280new : Edit
19792281new = \repo -> addChild (newCommit repo.root.files) repo
19802282
@@ -1982,6 +2284,7 @@ new = \repo -> addChild (newCommit repo.root.files) repo
19822284-- new empty commit on that parent becomes the focus (the parent may be
19832285-- immutable, and the focus never is). Refused for a commit the remote has a
19842286-- name for: push something else under that name first, or forget the name.
2287+-- Example: j abandon; validate a preview first with j 'preview abandon'.
19852288abandon : Edit
19862289abandon = \repo ->
19872290 let c = repo.root
@@ -1991,17 +2294,20 @@ abandon = \repo ->
19912294 else new (remove (eachChild (replayOnto p) repo))
19922295
19932296-- Push the files matching m from the focus down into its parent.
2297+-- Example: j 'contract (ext "rs")'
19942298contract : Fileset -> Edit
19952299contract = \m repo -> let a = repo.root
19962300 in at parents (rewrite (\q -> q { files = select m a.files q.files })) repo
19972301
19982302-- Fold the focus entirely into its parent; a new empty commit becomes the focus.
2303+-- Example: j squash; preview first with j 'preview squash'.
19992304squash : Edit
20002305squash = abandon . contract everything
20012306
20022307-- Push the files matching m from the focus up into a new child, which becomes
20032308-- the focus and inherits the old focus's children. Labels stay with the
20042309-- original commit, since they are the remote's.
2310+-- Example: j 'split (ext "rs")'; use (under ./src) to select a directory.
20052311split : Fileset -> Edit
20062312split = \m repo ->
20072313 let a = repo.root
@@ -2011,6 +2317,7 @@ split = \m repo ->
20112317 in by x.id (repo { root = a', children = [{ root = x, children = repo.children }] })
20122318
20132319-- Move the focused subtree onto another commit. Returns to the moved commit.
2320+-- Example: j 'rebase trunk'; preview with j 'preview (rebase trunk)'.
20142321rebase : Revset -> Edit
20152322rebase = \rs repo ->
20162323 let d = (goto rs repo).root
@@ -2021,6 +2328,7 @@ rebase = \rs repo ->
20212328 in by me (attach x.subtree (by d.id x.rest))
20222329
20232330-- Apply the focused commit's change onto another commit, as a new child there.
2331+-- Example: j 'pick %main'
20242332pick : Revset -> Edit
20252333pick = \rs repo ->
20262334 let ch = changeOf repo
@@ -2028,6 +2336,7 @@ pick = \rs repo ->
20282336 in at rs (addChild (newCommit (replay t.files ch))) repo
20292337
20302338-- A new child of the focus that undoes the change of the given commit.
2339+-- Example: j 'backout %feature'
20312340backout : Revset -> Edit
20322341backout = \rs repo ->
20332342 let ch = changeOf (goto rs repo)
@@ -2073,20 +2382,22 @@ entryAt = \p s -> filter (\e -> e.path == p) s
20732382
20742383-- The content at path p, or an empty file if absent.
20752384contentAt : Path -> Snapshot -> Blob
2076−contentAt = \p s -> (head (entryAt p s)).content or blob ""
2385+contentAt = lookupContent
20772386
20782387-- The paths a change touches.
20792388touched : Change -> [Path]
2080−touched = \ch ->
2081− let paths = map (.path) ch.to ++ filter (\p -> not (member p (map (.path) ch.to))) (map (.path) ch.from)
2082− in filter (\p -> entryAt p ch.to /= entryAt p ch.from) paths
2389+touched = touchedPaths
20832390
2084−-- Paths whose content differs between the focus and its parent.
2391+-- Paths changed by the focused commit compared with its parent, including
2392+-- the working-copy snapshot. These can remain nonempty after `j save`;
2393+-- `j unrecorded` shows only working-copy edits not yet recorded.
2394+-- Example: j changed
20852395changed : Repo -> [Path]
20862396changed = \repo -> touched (changeOf repo)
20872397
2088−-- What the focus changes, rendered by difftastic, one record per changed path.
2089−-- Replace difft with (\p -> diff) to use the built-in unified diff instead.
2398+-- What the focus changes, one record per changed path. Uses difftastic when
2399+-- installed; a missing difft falls back to a unified diff with path headers
2400+-- and 3 context lines, or a binary-files summary.
20902401diffs : Repo -> [{ path : Path, diff : Text }]
20912402diffs = \repo ->
20922403 let ch = changeOf repo
@@ -2094,23 +2405,30 @@ diffs = \repo ->
20942405
20952406-- The same, as one page of text. A Text result prints raw, so `j review` is
20962407-- readable in the terminal. A focus that changes nothing reads "no changes";
2097−-- a difft that cannot be run (not on PATH) is a crash saying so, and
2098−-- otherwise whatever difft prints, even nothing, is the page.
2408+-- missing difft uses the built-in unified diff (path headers, 3 context lines)
2409+-- or a binary-files summary. Other execution errors remain visible; an
2410+-- installed difft's output, even nothing, is the page.
2411+-- Example: j review; difftastic is optional.
20992412review : Repo -> Text
21002413review = \repo ->
21012414 let ds = diffs repo
21022415 in if null ds then "no changes\n" else concat (map (\d -> d.diff) ds)
21032416
2104−-- A summary of the focus.
2105−status : Repo -> { id : Id, message : Text, labels : [Label], changed : [Path], conflicts : [Path] }
2417+-- A summary of the focus. `changed` lists changes in the commit compared
2418+-- with its parent; `unrecorded` lists working-copy edits not yet recorded.
2419+-- Inspection does not record anything. Run `j save` to record those edits.
2420+-- Example: j status
2421+status : Repo -> { id : Id, message : Text, labels : [Label], changed : [Path], conflicts : [Path], unrecorded : [Path] }
21062422status = \repo ->
21072423 { id = repo.root.id
21082424 , message = repo.root.message
21092425 , labels = repo.root.labels
21102426 , changed = changed repo
2111− , conflicts = conflicted repo.root.files }
2427+ , conflicts = conflicted repo.root.files
2428+ , unrecorded = unrecorded repo }
21122429
21132430-- A summary of every commit, top first, in preorder.
2431+-- Example: j log
21142432log : Repo -> [{ id : Id, message : Text, labels : [Label], focus : Bool }]
21152433log = \repo ->
21162434 map (\c -> { id = c.id
@@ -2121,11 +2439,21 @@ log = \repo ->
21212439
21222440-- The history as a tree. Edit these records to change what `j tree` shows.
21232441-- (The parentheses are required: `f { … }` would be a record update of f.)
2442+-- The default `tree` shows every commit, sorted by time (parents always above
2443+-- their children); `treeCompact` folds uninteresting runs and far subtrees.
2444+-- Examples: j tree; j 'tree . squash' previews an edit without saving it.
21242445tree : Repo -> Text
2125−tree = treeWith ({ detail = 1, margin = false, elide = true, icons = false, color = "auto" })
2446+tree = treeWith ({ detail = 1, margin = false, elide = false, icons = false, color = "auto", lanes = 4, author = false, date = false, files = false })
2447+
2448+treeCompact : Repo -> Text
2449+treeCompact = treeWith ({ detail = 1, margin = false, elide = true, icons = false, color = "auto", lanes = 4, author = false, date = false, files = false })
21262450
21272451treeFull : Repo -> Text
2128−treeFull = treeWith ({ detail = 2, margin = true, elide = false, icons = false, color = "auto" })
2452+treeFull = treeWith ({ detail = 2, margin = true, elide = false, icons = false, color = "auto", lanes = 4, author = false, date = false, files = false })
2453+
2454+-- The full tree plus the extra data columns: date, files changed, author.
2455+treeData : Repo -> Text
2456+treeData = treeWith ({ detail = 2, margin = true, elide = false, icons = false, color = "auto", lanes = 4, author = true, date = true, files = true })
21292457```
21302458
21312459---
@@ -2133,9 +2461,9 @@ treeFull = treeWith ({ detail = 2, margin = true, elide = false, icons = false,
21332461## 10. Testing
21342462
21352463The interpreter must be testable without a jj repository. Provide an in-memory
2136−implementation of the domain builtins (`replay`, `unresolved`, `blob`,
2464+implementation of the domain builtins (`replay`, `unresolved`, `blob`, `blobHex`,
21372465`text`, `by`, `extract`, `meta`, `difft` as a stub, and the minting
2138−of `@`) in which a blob is text, a
2466+of `@`) in which a resolved blob carries bytes and a file kind, a
21392467conflict is a record of the three sides, and `replay` resolves a path exactly
21402468when at most one side changed it or both made the same change, path by path
21412469even where a file meets a directory, which jj merges as one value (§7.3).
@@ -2171,9 +2499,10 @@ here is out of scope (§12). Nothing here uses the `jj` binary.
21712499**Getting a repository**
21722500
21732501```
2174−$EDITOR ~/.config/j/config.j -- set `user` once
2502+j setup 'Your actual name' you@your-domain.example -- create the full config once
21752503j init -- new repository here, or adopt an existing .git
2176−j clone git@host:org/repo.git -- or an existing one
2504+j clone git@host:org/repo.git project -- or clone an existing one
2505+cd project -- after clone, enter its directory
21772506j remote git@host:org/repo.git -- attach a remote to an init'd repo
21782507```
21792508
@@ -2182,9 +2511,9 @@ j remote git@host:org/repo.git -- attach a remote to an init'd repo
21822511```
21832512j tree -- the history as a tree, focus marked
21842513j treeFull -- the same with sizes, ages, authors, changed paths
2185−j status -- id, message, labels, changed paths, conflicts of @
2514+j status -- focus, in-commit changes, unrecorded edits, conflicts
21862515j log -- every commit as a record
2187−j review -- difftastic rendering of @ against its parent
2516+j review -- difftastic or unified diff of @ against its parent
21882517j review . by @kpq -- the same for another commit, without moving
21892518j diffs -- the same, one record per path
21902519j here -- @'s id
@@ -2215,7 +2544,8 @@ j rebase trunk
22152544j guard clean . rebase trunk or id -- only if it comes out clean
22162545j pick @kpq
22172546j 'forEach (descendantsOf here) (describe "wip")'
2218−j id -- record the working directory, change nothing else
2547+j save -- record the working directory, change nothing else
2548+j 'preview squash' -- validate and show the result without saving
22192549j tree . squash -- dry run: show the result, persist nothing
22202550j tree . validate . squash -- the same, refusing what persistence would refuse
22212551j squash && j tree -- do, then look
@@ -2237,7 +2567,10 @@ j rebase trunk -- bring the stack up to date
22372567A rebase or fetch that conflicts leaves unresolved files in the affected
22382568commits. `j conflicts` lists them; `j by @…` moves there, which checks the
22392569files out with conflict markers; editing the files and running any persisting
2240−expression (`j id` at minimum) records the resolution.
2570+expression (`j save` or `j id` at minimum) records the resolution. Run
2571+`j status` to verify `No conflicts` and `All edits recorded` in the focus,
2572+and `j conflicts` to check the rest of the history. No continue command is
2573+needed.
22412574
22422575**Mistakes**
22432576
@@ -2252,7 +2585,7 @@ j redo
22522585Any definition added to `config.j` whose value is an `Edit` is a command:
22532586
22542587```haskell
2255−pr = \n -> describe n . squash
2588+pr = \n -> describe n . prev . squash
22562589```
22572590
22582591```
@@ -2265,8 +2598,9 @@ j 'pr "fix login"' && j 'push (label "fix-login" here)'
22652598
22662599Second parents of merge commits (merges are placed under their first parent
22672600and are read-only); hunk-level `split`; conflict resolution inside the
2268−language (blobs are opaque); workspaces other than the default; remotes other
2269−than `origin`; tags; submodules and LFS; ignore rules beyond `.gitignore`;
2270−changing file modes or symlinks from the language; repairing a repository in
2271−which one change id has two visible commits (refused; §7.2); a `--help` flag
2272−or any flag at all; static typing. None of these are precluded by the design.
2601+language (blobs are opaque); workspaces other than the default; sparse working
2602+copies (refused; §7.1); remotes other than `origin`; tags; submodules and LFS;
2603+ignore rules beyond `.gitignore`; repairing a repository in which one change
2604+id has two visible commits (refused; §7.2); general command flags beyond the
2605+`--help` and `-h` discovery arguments; static typing. None of
2606+these are precluded by the design.