@@ -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, ten 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
@@ -46,10 +46,13 @@ echo EXPRESSION | j
4646 usage and getting-started guide to stderr and exit 2. The guide points to
4747 `j help`, `j commands`, and commands for starting or inspecting a repository.
4848 It does not read or create configuration or open a repository.
49−5. `--help` and `-h` are aliases for the reserved command `help` only when
50− they are the first whitespace-separated word (§1.1). There are no other
51− flags or trailing help flags: `j 'new --help'` is an expression whose
52− `--help` is a language comment, and `j '"--help"'` prints text.
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.
53566. The expression is text: an argument that is not valid UTF-8 is a usage
5457 error (exit 2) naming it, and so is an expression on standard input that
5558 is not.
@@ -58,13 +61,14 @@ echo EXPRESSION | j
5861
5962Before the expression is parsed, its text is trimmed of leading and trailing
6063whitespace and split on whitespace into words. If the first word is one of the
61−ten reserved words below, the whole text is a reserved command. A first word
64+eleven reserved words below, the whole text is a reserved command. A first word
6265of `--help` or `-h` is treated as `help`.
6366
6467| command | operation |
6568|---|---|
6669| `help [NAME]` | show the command guide, or help for a reserved command or config name; §1.1.1 |
67−| `commands [QUERY]` | list the vocabulary, optionally searching names, signatures, and comments; §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 |
6872| `init` | create a repository in the current directory; §7.8 |
6973| `clone URL [DIR]` | clone `URL` into `DIR` (default: the repository's name in `URL`); §7.8 |
7074| `remote URL` | set the URL of the remote `origin`, creating it if absent; §7.8 |
@@ -75,8 +79,12 @@ of `--help` or `-h` is treated as `help`.
7579| `ops` | print the operation log; §7.7 |
7680
7781For every reserved command except `push` and `commands`, the remaining words
78−are literal arguments (no quoting, no escaping); a wrong number is a usage
79−error (exit 2) and the language is not involved. `commands` takes the remaining
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
8088trimmed text as its optional search query, including spaces. For `push`, the
8189remainder of the text is an expression, evaluated as in §1.2 steps 2–7 but
8290against the repository as recorded, without snapshotting the working directory;
@@ -96,11 +104,18 @@ for that reserved command or for a name in the current configuration, including
96104its signature when present and available documentation. `--help [NAME]` and
97105`-h [NAME]` are equivalent to `help [NAME]`. These successful requests exit 0.
98106An unknown name is a usage error (exit 2) with guidance to use `j commands`.
99−
100−`commands` lists the reserved commands and the vocabulary actually declared or
101−defined in the current configuration, including custom definitions without
102−signatures. `commands QUERY` searches the catalog case-insensitively by name,
103−signature, and documentation comments. The catalog does not present an
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
104119undeclared builtin as available merely because the binary implements it.
105120
106121General help and help for reserved commands do not depend on configuration and
@@ -131,7 +146,8 @@ resolves commit ids, snapshots the working directory, or records operations.
1311467. If `v` is a function, apply it to `r`, once, and let `v` be the result.
1321478. If `v` is a `Repo` (§4.4): if it equals the repository value *as loaded,
133148 before the snapshot of §7.4*, do nothing at all; otherwise persist it
134− (§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).
135151 Exit 0.
1361529. Otherwise display `v` (§5.1) on stdout. Persist nothing. Exit 0.
137153
@@ -141,7 +157,7 @@ persist nothing, leave the working directory untouched, and exit 1.
141157Step 7 applies exactly once. If the result of applying a function to `r` is
142158again a function, that function is displayed, not applied again.
143159
144−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
145161unchanged and records it when it is not: the snapshot makes `r` differ from
146162the loaded value, and that difference is what gets persisted. The test may be
147163implemented by comparing commit and tree hashes rather than values.
@@ -153,9 +169,11 @@ Two consequences worth knowing:
153169 what a rebase would produce without doing it. `validate` in the chain,
154170 `j tree . validate . rebase trunk`, additionally raises whatever
155171 persistence would refuse, so the preview is of something that could
156− actually be persisted.
157−- **Do, then look.** `j squash` prints nothing. `j squash && j tree` is the
158− 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.
159177
160178### 1.3 Interrupts
161179
@@ -215,6 +233,16 @@ or a nonempty `NO_COLOR`. Diagnostics use the stderr terminal; displayed
215233values use stdout. Error prefixes are bright red, informational prefixes
216234bright cyan, and interactive successful commands have a green checkmark.
217235Color 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.
218246
219247When both stdout and stderr are capable terminals, repository operations,
220248configuration evaluation, and expression evaluation (including implicit application)
@@ -235,8 +263,10 @@ most 96 ms of total pacing. Its bytes and row order are unchanged. Plain
235263text is written directly. `J_ANIMATIONS=never` (also `off`, `false`, or `0`,
236264case-insensitive) disables all motion while retaining colors. Motion is
237265also disabled by nonempty `NO_COLOR` or `CI`, `TERM=dumb`, or either output
238−stream being redirected. Interactive success messages are omitted in
239−these noninteractive modes; `J_ANIMATIONS` alone does not suppress them.
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.
240270Broken pipes and failed writes keep the behavior in §1.4.
241271
242272---
@@ -573,11 +603,12 @@ as in §4.13.
573603| `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 |
574604| `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) |
575605| `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 |
576−| `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 |
577608| `treeWith o r` | the history rendered as a tree with options record `o`; §7.11 |
578609| `extract S v` | every subvalue of `v` that is an `S`; §4.12 |
579610| `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) |
580−| `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). 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 |
581612
582613### 4.10 Ids: existing and new
583614
@@ -741,6 +772,21 @@ display forms. Any other record is generic.
741772| list of other scalars | `a, b, c` | same, wrapped at the terminal width |
742773| any other list | `n items` | one block per item, separated by a blank line |
743774
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+
744790An empty list of any kind prints `none` (dim) in block form and `0 …` in
745791line form. Author and age in a `Commit` block are read from jj's commit
746792metadata by id;
@@ -764,12 +810,13 @@ Examples:
764810
765811```
766812$ j status
767−changed src/lexer.rs
768− src/parser.rs
769−conflicts src/lexer.rs
770−id wqzt·kpqxmnrvyxskptlmzzzzabcd
771−labels feature
772−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
773820
774821$ j conflicts
775822⊗ wqzt wip feature
@@ -881,6 +928,14 @@ embedded reference `config.j` (§9) to that path as an editable starting point,
881928readable and writable by its owner, and reports its path. An existing file is
882929never overwritten, including when it cannot be read or contains invalid
883930UTF-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.
884939An existing user configuration is not updated when the binary changes. The
885940reference file is also distributed at `<prefix>/share/j/config.j`.
886941
@@ -890,6 +945,23 @@ names read it without evaluation. If it is missing they use the embedded
890945reference in memory without writing a file. The empty-invocation guide does
891946not read or write configuration either.
892947
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.
964+
893965### 6.2 Validation
894966
895967Loading fails (exit 3) if any of these hold:
@@ -913,6 +985,13 @@ not declared is simply unbound, and using it is a runtime crash.
913985
914986Definitions may reference each other in any order.
915987
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+
916995### 6.3 Definitions the interpreter reads
917996
918997Four names in `config.j` are read by the interpreter itself, not only by
@@ -1148,6 +1227,14 @@ directory.
11481227
11491228Reserved commands never snapshot.
11501229
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+
11511238### 7.5 Persistence
11521239
11531240Given the loaded value `old` and the result `new`, one jj operation is
@@ -1287,6 +1374,13 @@ language: `fetch` and `push` are the only things that change them.
12871374 creating it if absent;
12881375 - `{ delete : Text }` — delete the bookmark of that name.
12891376
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+
12901384 Duplicates are merged; two records with the same `name` and different
12911385 effects are a crash. It is refused (exit 1, nothing pushed) if:
12921386 - a `name` is not a valid git branch name, an `id` is not a visible commit
@@ -1330,21 +1424,24 @@ language: `fetch` and `push` are the only things that change them.
13301424 bookmarks, and the refused push does not touch the label. After a
13311425 successful push the labels reflect the new positions. It records one
13321426 operation, even when it sends two git pushes. An empty list sends nothing
1333− 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.
13341431 If the remote accepts some of the updates and rejects others, that
13351432 operation records the accepted ones, whose labels move, and `push` exits 1
13361433 naming the rejected bookmarks and any deletes it did not send.
13371434
13381435Only the remote named `origin` is supported in this version; `fetch` and
1339−`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.
13401437Authentication is git's: the ssh agent for ssh URLs, git credential helpers
13411438for https, as jj-lib's git backend provides them; `j` adds no prompting.
13421439Bringing work up to date is a separate expression, e.g. `rebase trunk`.
13431440
13441441### 7.7 Operations, undo, redo, and the lock
13451442
1346−Every persisting `j` program, and each of `fetch` and `push`, records exactly
1347−one jj operation, described by the expression or command text. A push's
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
13481445description includes its `push ` prefix, so an expression named `init` or
13491446`clone` cannot be mistaken for the operation that created the repository.
13501447Crashes record nothing, except as §7.5 step 7 says.
@@ -1385,8 +1482,10 @@ history crosses from older markers into current ones.
13851482Both refuse, before doing anything, if the working directory holds a change
13861483the focused commit does not, as a snapshot would find it (§7.4; a stale
13871484working copy holds none where the directory holds what was last checked out
1388−or what the commit has): `j: working copy has changes not in @; run \`j id\`
1389−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
13901489out the restored focus, as persistence does (§7.5 step 7): a checkout that
13911490fails leaves the operation recorded, and `redo` or `undo` goes back.
13921491Undoing a `push` restores the recorded labels and leaves the remote untouched.
@@ -1439,7 +1538,9 @@ that state, §7.2 applies.
14391538 it: the last component of `URL`'s path, a final `.git` component skipped,
14401539 minus `.git` (`repo` for `/srv/repo/.git`, `host:repo.git` and
14411540 `https://host/team/repo`); a `URL` that leaves no name, such as `/` or
1442− `..`, 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.
14431544- **`remote URL`** sets the URL of `origin`, creating the remote if it does not
14441545 exist. It does not fetch.
14451546
@@ -1470,7 +1571,16 @@ Commits are authored and committed as `user.name <user.email>` from
14701571`config.j` (§6.3). `j` reads no other configuration; jj-lib's settings object
14711572is constructed by `j` from `user` and fixed defaults. `init`, `clone`, and
14721573persistence exit 3 if `user` is missing or malformed, since that is a
1473−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.
14741584
14751585### 7.10 External diff rendering
14761586
@@ -1491,15 +1601,24 @@ program.
149116014. Return stdout as `Text`. A non-zero exit status is not an error (difftastic
14921602 uses it to signal "differences found" under some options), so what a
14931603 `difft` that fails printed, even nothing, is the result; a failure to
1494− 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.
14951613
14961614All rendering options are difftastic's own, set through its `DFT_*`
14971615environment variables. `j` adds no options of its own. `diff` remains
1498−available as a dependency-free unified diff; the reference `config.j` uses
1499−`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.
15001619
1501−Packaging: difftastic is a runtime dependency; on Nix, wrap `j` with `difft`
1502−on its `PATH`.
1620+Packaging: difftastic is an optional runtime enhancement; on Nix, wrap `j`
1621+with `difft` on its `PATH`.
15031622
15041623### 7.11 Tree rendering
15051624
@@ -1742,6 +1861,10 @@ Drop = { delete : Label } -- delete the remote's bookmark
17421861------------------------------------------------------------------------------
17431862
17441863(.) : (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.
17451868id : a -> a
17461869const : a -> b -> a
17471870crash : Text -> a -- abort the program with a message
@@ -1809,10 +1932,19 @@ splitOn : Text -> Text -> [Text] -- splitOn "/" "a/b" = ["a" "b"]
18091932-- file ./a would, crashes, naming the path (§7.3).
18101933replay : Snapshot -> Change -> Snapshot
18111934unresolved : 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
18121942blob : Text -> Blob -- a resolved regular file
18131943blobHex : Text -> Text -> Blob -- kind (regular/executable/symlink), hex bytes
18141944text : Blob -> Text -- its content (markers if unresolved)
18151945by : 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
18161948subtreeCommits : a -> [Commit] -- every commit of a tree in preorder, in one
18171949 -- pass: what `commits` (below) is
18181950meta : Id -> Meta -- git hash, author, email, committer time;
@@ -1822,9 +1954,11 @@ validate : Repo -> Repo -- the repo unchanged, or the crash p
18221954 -- focus). `tree . validate . rebase trunk` is an
18231955 -- honest dry run.
18241956diff : Blob -> Blob -> Text -- unified diff from the first to the second
1825−difft : Path -> Blob -> Blob -> Text -- difftastic's rendering; needs difft on PATH.
1826− -- Options via DFT_* environment variables.
1827−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 }
18281962 -> Repo -> Text -- the history as a tree; see the spec
18291963
18301964-- Every subvalue of a given shape, however deeply nested. A shape is a type
@@ -1910,6 +2044,9 @@ restrict = \m -> filter (\e -> m e.path)
19102044select : Fileset -> Snapshot -> Snapshot -> Snapshot -- m from a, the rest from b
19112045select = \m a b -> restrict m a ++ restrict (neg m) b
19122046
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`.
19132050conflicted : Snapshot -> [Path]
19142051conflicted = \s -> map (.path) (filter (\e -> unresolved e.content) s)
19152052
@@ -1994,6 +2131,11 @@ minus = \a b repo -> let ys = b repo in filter (\i -> not (member i ys)) (a repo
19942131matching : (Commit -> Bool) -> Revset -> Revset
19952132matching = \p rs repo -> filter (\i -> p (commitAt i repo)) (rs repo)
19962133
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.
19972139conflicts : Revset
19982140conflicts = matching (\c -> not (null (conflicted c.files))) all
19992141
@@ -2002,7 +2144,10 @@ firstOf : [Revset] -> Revset
20022144firstOf = \rss repo -> foldl (\acc rs -> if null acc then rs repo else acc) [] rss
20032145
20042146-- The main line: whichever of these names the remote has, first one wins.
2005−-- 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'.
20062151trunk : Revset
20072152trunk = firstOf [%main %master %trunk]
20082153
@@ -2018,21 +2163,33 @@ immutable = ancestorsOf trunk
20182163-- Focusing. `goto` is the only place a revset must have exactly one member.
20192164------------------------------------------------------------------------------
20202165
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.
20212168goto : Revset -> Edit
20222169goto = \rs repo ->
20232170 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"
20242175 in if length is == 1
20252176 then by (head is) repo
2026− 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.")
20272180
20282181commitAt : Id -> Repo -> Commit
2029−commitAt = \i repo -> (by i repo).root
2182+commitAt = lookupCommit
20302183
20312184prev : Edit -- the parent
20322185prev = goto parents
20332186
2034−next : Edit -- the only child
2035−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
20362193
20372194tip : Edit -- end of the line of only-children
20382195tip = \repo -> tip (next repo) or repo
@@ -2058,6 +2215,8 @@ eachChild = forEach kids
20582215guard : (Repo -> Bool) -> Edit -- crash unless p holds
20592216guard = \p repo -> if p repo then repo else crash "guard"
20602217
2218+-- Whether the focused commit has no unresolved files. This says nothing
2219+-- about unrecorded working-copy edits; inspect those with `j unrecorded`.
20612220clean : Repo -> Bool
20622221clean = \repo -> null (conflicted repo.root.files)
20632222
@@ -2096,11 +2255,28 @@ rewrite = \f repo ->
20962255-- Commands
20972256------------------------------------------------------------------------------
20982257
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+
20992273-- Set the message of the focused commit.
2274+-- Example: j 'describe "fix login"'
21002275describe : Text -> Edit
21012276describe = \m -> mapRoot (\c -> c { message = m })
21022277
21032278-- Start an empty child of the focus and move to it.
2279+-- Examples: j new; j 'describe "next change" . new'
21042280new : Edit
21052281new = \repo -> addChild (newCommit repo.root.files) repo
21062282
@@ -2108,6 +2284,7 @@ new = \repo -> addChild (newCommit repo.root.files) repo
21082284-- new empty commit on that parent becomes the focus (the parent may be
21092285-- immutable, and the focus never is). Refused for a commit the remote has a
21102286-- 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'.
21112288abandon : Edit
21122289abandon = \repo ->
21132290 let c = repo.root
@@ -2117,17 +2294,20 @@ abandon = \repo ->
21172294 else new (remove (eachChild (replayOnto p) repo))
21182295
21192296-- Push the files matching m from the focus down into its parent.
2297+-- Example: j 'contract (ext "rs")'
21202298contract : Fileset -> Edit
21212299contract = \m repo -> let a = repo.root
21222300 in at parents (rewrite (\q -> q { files = select m a.files q.files })) repo
21232301
21242302-- Fold the focus entirely into its parent; a new empty commit becomes the focus.
2303+-- Example: j squash; preview first with j 'preview squash'.
21252304squash : Edit
21262305squash = abandon . contract everything
21272306
21282307-- Push the files matching m from the focus up into a new child, which becomes
21292308-- the focus and inherits the old focus's children. Labels stay with the
21302309-- original commit, since they are the remote's.
2310+-- Example: j 'split (ext "rs")'; use (under ./src) to select a directory.
21312311split : Fileset -> Edit
21322312split = \m repo ->
21332313 let a = repo.root
@@ -2137,6 +2317,7 @@ split = \m repo ->
21372317 in by x.id (repo { root = a', children = [{ root = x, children = repo.children }] })
21382318
21392319-- Move the focused subtree onto another commit. Returns to the moved commit.
2320+-- Example: j 'rebase trunk'; preview with j 'preview (rebase trunk)'.
21402321rebase : Revset -> Edit
21412322rebase = \rs repo ->
21422323 let d = (goto rs repo).root
@@ -2147,6 +2328,7 @@ rebase = \rs repo ->
21472328 in by me (attach x.subtree (by d.id x.rest))
21482329
21492330-- Apply the focused commit's change onto another commit, as a new child there.
2331+-- Example: j 'pick %main'
21502332pick : Revset -> Edit
21512333pick = \rs repo ->
21522334 let ch = changeOf repo
@@ -2154,6 +2336,7 @@ pick = \rs repo ->
21542336 in at rs (addChild (newCommit (replay t.files ch))) repo
21552337
21562338-- A new child of the focus that undoes the change of the given commit.
2339+-- Example: j 'backout %feature'
21572340backout : Revset -> Edit
21582341backout = \rs repo ->
21592342 let ch = changeOf (goto rs repo)
@@ -2199,20 +2382,22 @@ entryAt = \p s -> filter (\e -> e.path == p) s
21992382
22002383-- The content at path p, or an empty file if absent.
22012384contentAt : Path -> Snapshot -> Blob
2202−contentAt = \p s -> (head (entryAt p s)).content or blob ""
2385+contentAt = lookupContent
22032386
22042387-- The paths a change touches.
22052388touched : Change -> [Path]
2206−touched = \ch ->
2207− let paths = map (.path) ch.to ++ filter (\p -> not (member p (map (.path) ch.to))) (map (.path) ch.from)
2208− in filter (\p -> entryAt p ch.to /= entryAt p ch.from) paths
2389+touched = touchedPaths
22092390
2210−-- 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
22112395changed : Repo -> [Path]
22122396changed = \repo -> touched (changeOf repo)
22132397
2214−-- What the focus changes, rendered by difftastic, one record per changed path.
2215−-- 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.
22162401diffs : Repo -> [{ path : Path, diff : Text }]
22172402diffs = \repo ->
22182403 let ch = changeOf repo
@@ -2220,23 +2405,30 @@ diffs = \repo ->
22202405
22212406-- The same, as one page of text. A Text result prints raw, so `j review` is
22222407-- readable in the terminal. A focus that changes nothing reads "no changes";
2223−-- a difft that cannot be run (not on PATH) is a crash saying so, and
2224−-- 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.
22252412review : Repo -> Text
22262413review = \repo ->
22272414 let ds = diffs repo
22282415 in if null ds then "no changes\n" else concat (map (\d -> d.diff) ds)
22292416
2230−-- A summary of the focus.
2231−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] }
22322422status = \repo ->
22332423 { id = repo.root.id
22342424 , message = repo.root.message
22352425 , labels = repo.root.labels
22362426 , changed = changed repo
2237− , conflicts = conflicted repo.root.files }
2427+ , conflicts = conflicted repo.root.files
2428+ , unrecorded = unrecorded repo }
22382429
22392430-- A summary of every commit, top first, in preorder.
2431+-- Example: j log
22402432log : Repo -> [{ id : Id, message : Text, labels : [Label], focus : Bool }]
22412433log = \repo ->
22422434 map (\c -> { id = c.id
@@ -2247,11 +2439,21 @@ log = \repo ->
22472439
22482440-- The history as a tree. Edit these records to change what `j tree` shows.
22492441-- (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.
22502445tree : Repo -> Text
2251−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 })
22522450
22532451treeFull : Repo -> Text
2254−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 })
22552457```
22562458
22572459---
@@ -2297,9 +2499,10 @@ here is out of scope (§12). Nothing here uses the `jj` binary.
22972499**Getting a repository**
22982500
22992501```
2300−$EDITOR ~/.config/j/config.j -- set `user` once
2502+j setup 'Your actual name' you@your-domain.example -- create the full config once
23012503j init -- new repository here, or adopt an existing .git
2302−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
23032506j remote git@host:org/repo.git -- attach a remote to an init'd repo
23042507```
23052508
@@ -2308,9 +2511,9 @@ j remote git@host:org/repo.git -- attach a remote to an init'd repo
23082511```
23092512j tree -- the history as a tree, focus marked
23102513j treeFull -- the same with sizes, ages, authors, changed paths
2311−j status -- id, message, labels, changed paths, conflicts of @
2514+j status -- focus, in-commit changes, unrecorded edits, conflicts
23122515j log -- every commit as a record
2313−j review -- difftastic rendering of @ against its parent
2516+j review -- difftastic or unified diff of @ against its parent
23142517j review . by @kpq -- the same for another commit, without moving
23152518j diffs -- the same, one record per path
23162519j here -- @'s id
@@ -2341,7 +2544,8 @@ j rebase trunk
23412544j guard clean . rebase trunk or id -- only if it comes out clean
23422545j pick @kpq
23432546j 'forEach (descendantsOf here) (describe "wip")'
2344−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
23452549j tree . squash -- dry run: show the result, persist nothing
23462550j tree . validate . squash -- the same, refusing what persistence would refuse
23472551j squash && j tree -- do, then look
@@ -2363,7 +2567,10 @@ j rebase trunk -- bring the stack up to date
23632567A rebase or fetch that conflicts leaves unresolved files in the affected
23642568commits. `j conflicts` lists them; `j by @…` moves there, which checks the
23652569files out with conflict markers; editing the files and running any persisting
2366−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.
23672574
23682575**Mistakes**
23692576
@@ -2378,7 +2585,7 @@ j redo
23782585Any definition added to `config.j` whose value is an `Edit` is a command:
23792586
23802587```haskell
2381−pr = \n -> describe n . squash
2588+pr = \n -> describe n . prev . squash
23822589```
23832590
23842591```
@@ -2395,5 +2602,5 @@ language (blobs are opaque); workspaces other than the default; sparse working
23952602copies (refused; §7.1); remotes other than `origin`; tags; submodules and LFS;
23962603ignore rules beyond `.gitignore`; repairing a repository in which one change
23972604id has two visible commits (refused; §7.2); general command flags beyond the
2398−first-word `--help` and `-h` aliases; static typing. None of
2605+`--help` and `-h` discovery arguments; static typing. None of
23992606these are precluded by the design.