on codex/performance-improvements help sign in
j/docs/base.md
1251 lines 39 kB history raw
1# The `j` base vocabulary
3This is the complete reference for everything `j` knows: every builtin and
4every definition in the reference `config.j`. Nothing else exists — an
5expression can use a name only if `config.j` declares it (a builtin,
6implemented by `j`) or defines it (written in the language).
8Conventions used in this document:
10- **Signatures** are given as `name : Type`. A signature on its own declares a
11 builtin; before a definition it documents and checks that definition.
12- **Types** like `Edit`, `Revset`, `Fileset`, `Snapshot` are aliases defined
13 in §1.3. Lowercase type variables (`a`, `b`, `m`) mean "any type".
14- **Examples** show an expression and its result (`=>`). Pure examples are
15 literal; repository examples are described, since their output depends on
16 the repository.
18How to read the three most common aliases:
20```
21Edit = Repo -> Repo -- a command; may crash. Applied to the repo on the command line.
22Revset = Repo -> [Id] -- a set of commits, computed from a repo
23Fileset = Path -> Bool -- which paths a command applies to
24```
26Most definitions are **Edits** — functions `Repo -> Repo`. On the command line
27a function result is applied to the current repository, and a `Repo` result is
28persisted. `e2 . e1` runs `e1` then `e2`; `e or id` runs `e` and leaves things
29alone if it crashes.
31---
33## 1. Types and shapes
35### 1.1 `user` — who commits
37```
38user : { name : Text, email : Text }
39```
41The identity `j` commits as. `j` reads this definition itself; it is required.
42The reference value is a placeholder you must edit:
44```
45user = { name = "Your Name", email = "you@example.com" }
46```
48This is a plain value, not a function. Its signature is checked once, at load
49(§4.13): if you write `user = 5`, `j` refuses to start (exit 3).
51### 1.2 Paths, labels, entries, snapshots
53**`Path = [Text]`** — a file path as its components. Written `./a/b`, which is
54`["a" "b"]`. A trailing `/` is ignored; a bare `./` is `[]`, the repository
55root. For names the literal cannot spell (spaces, unusual characters), build
56one with `splitOn "/"`.
58```
59./src/lexer.rs => ["src" "lexer.rs"]
60./ => []
61splitOn "/" "a b/c" => ["a b" "c"]
62```
64**`Label = Text`** — a bookmark name on the remote. A label is written `%main`
65in an expression (a `Revset`, see `labelled`); as data it is just text.
67**`Entry = { path : Path, content : Blob }`** — one file in a snapshot: its
68path and its content.
70**`Snapshot = [Entry]`** — the files of one commit. Paths are unique. A
71deleted, resolved path simply has no entry (there is no "deletion" marker).
73```
74[({ path = ./a.txt, content = blob "hello" })]
75 => a snapshot of one file, a.txt, containing "hello"
76```
78**`Change = { from : Snapshot, to : Snapshot }`** — the files before and after
79a change. Used by `replay`, `invert`, `touched`, `changeOf`.
81**`Meta = { hash : Text, author : Text, email : Text, time : Int }`** — what jj
82records about a commit beyond the model: its git hash, author name and email,
83and committer time. Produced by the `meta` builtin.
85### 1.3 Commits, subtrees, and the repository
87**`Commit`** — one commit:
89```
90Commit = { files : Snapshot -- the files
91 , message : Text -- the description
92 , labels : [Label] -- the remote's names for it (read-only)
93 , id : Id } -- its change id (unique)
94```
96`labels` is read-only in the language: only `fetch` and `push` change it.
98**`Subtree = { root : Commit, children : [Subtree] }`** — a commit and,
99recursively, everything below it.
101**`Frame = { parent : Commit, left : [Subtree], right : [Subtree] }`** — one
102ancestor on the path back to the top, with a hole where the focused subtree
103was. `left` and `right` are the siblings on either side (presentation order
104only).
106**`Repo = { root : Commit, children : [Subtree], context : [Frame] }`** — the
107whole history, seen from one commit (the *focus*). `root` is the focused
108commit, `children` its children, `context` the path back up to the top
109(`context = []` at the top of history). A `Repo` is a **zipper**.
111**`Detached = { subtree : Subtree, rest : Repo }`** — the result of `detach`:
112a subtree, and the repository with that subtree removed.
114**`Rebase = { from : Snapshot, onto : Snapshot }`** — the base a subtree was
115written on, and the new base to rewrite it onto. Used by `replayTree`.
117### 1.4 Aliases for functions
119```
120Edit = Repo -> Repo -- a command; may crash
121Fileset = Path -> Bool -- which paths a command applies to
122Revset = Repo -> [Id] -- a set of commits, computed from a repo
123```
125These aliases are unfolded by the contract checker: `at : Revset -> Edit ->
126Edit` checks three arguments and a `Repo` result.
128### 1.5 Push records
130**`Push = { id : Id, name : Label }`** — set the remote's bookmark `name` to
131the commit `id`, creating it if absent.
133**`Drop = { delete : Label }`** — delete the remote's bookmark of that name.
135`j push EXPR` evaluates `EXPR` to a list of `Push` and `Drop` records. Built
136with `label`, `unlabel`, `rename`, `relabel` (§8).
138```
139[({ id = @wqzt, name = "main" })] -- a Push: point main at @wqzt
140[({ delete = "old-feature" })] -- a Drop: delete the old-feature bookmark
141```
143---
145## 2. Functions and control flow
147These are the fundamental combinators. `or` is a keyword, not a builtin.
149### `(.)` — composition
151```
152(.) : (b -> c) -> (a -> b) -> a -> c
153```
155Right-to-left function composition: `(f . g) x = f (g x)`. Right-associative,
156binds very tightly (`infixr 9`). This is how edits are chained.
158```
159((\x -> x + 1) . (\x -> x * 2)) 5
160 => 11 -- (5 * 2) + 1
162describe "wip" . new -- start a new commit, then describe it
163tree . squash -- squash the focus, then render the tree
164```
166### `id` — identity
168```
169id : a -> a
170```
172The identity function. As an edit it does nothing, which makes it the
173universal fallback for `or`.
175```
176id 42 => 42
177(id . id) 7 => 7
178edit or id -- run edit, keep the repository if it crashes
179```
181### `const` — constant function
183```
184const : a -> b -> a
185```
187`const a b = a` — ignores its second argument.
189```
190const 5 99 => 5
191map (const 0) [1 2 3] => [0 0 0]
192everything -- defined as const true
193```
195### `crash` — abort with a message
197```
198crash : Text -> a
199```
201Aborts evaluation with the given message. Every interpreter-detected runtime
202error is also a crash, and all crashes are catchable by `or`.
204```
205crash "something went wrong"
206 -- CRASH: something went wrong
208(crash "boom") or "recovered"
209 => "recovered"
210```
212### `or` — catching crashes (keyword)
214`a or b` evaluates `a`. If `a` crashes, the crash is discarded and `b` is
215returned. Otherwise `a` is the result — unless `a` is a function and `b` is
216too, in which case the result is pointwise: `(f or g) x = f x or g x`.
218```
219head [] or 42 => 42
2201 or head [] => 1 -- head [] never runs
221edit or id -- try edit, fall back to id
222```
224The lifting is what makes `edit or id` meaningful: a function value cannot
225crash until applied, so without pointwise lifting the `or` would catch nothing.
227---
229## 3. Comparison, booleans, integers, `show`
231### Equality: `(==)` and `(/=)`
233```
234(==) : a -> a -> Bool
235(/=) : a -> a -> Bool
236```
238Structural equality on ints, texts, bools, lists, and records; identity on
239`Id`; content-and-mode on `Blob`. `/=` is the negation. **Comparing two
240functions crashes.**
242```
2432 == 2 => true
244"a" == "a" => true
245[1 2] == [1 2] => true
246{ a = 1 } == { a = 1 } => true
2471 /= 2 => true
248```
250### Booleans: `(&&)`, `(||)`, `not`
252```
253(&&) : Bool -> Bool -> Bool -- short-circuit
254(||) : Bool -> Bool -> Bool -- short-circuit
255not : Bool -> Bool
256```
258`&&` and `||` short-circuit (the right operand is not evaluated if the left
259decides the result). Operands must be `Bool`.
261```
2621 == 1 && 2 == 2 => true
2631 == 2 || 3 == 3 => true
264not (1 == 2) => true
265not true => false
266```
268### Integer arithmetic: `(+)`, `(-)`, `(*)`
270```
271(+) : Int -> Int -> Int
272(-) : Int -> Int -> Int
273(*) : Int -> Int -> Int
274```
276Arbitrary-precision: never overflows. There is no division builtin. No unary
277minus — write `0 - 5`.
279```
2802 + 3 * 4 => 14
28120 - 5 - 3 => 12
282999999999999 * 999999999999
283 => 999999999998000000000001
2840 - 5 => (0 - 5)
285```
287### Integer comparison: `(<)`, `(<=)`, `(>)`, `(>=)`
289Defined on `Int` only; crash on anything else. Non-associative (cannot chain:
290`1 < 2 < 3` is a parse error).
292```
2931 < 2 => true
2943 >= 3 => true
295(1 < 2) == true => true -- parenthesised, since < is non-associative
296```
298### `show` — render a value as text
300```
301show : a -> Text
302```
304Renders any value in the language's literal syntax, such that (except for
305closures and unresolved blobs) the text parses and evaluates back to an equal
306value. A top-level `Text` prints raw, so `j show …` is how you inspect values.
307See §5.2 of the spec for the exact rendering.
309```
310show 42 => "42"
311show "hi" => "\"hi\""
312show [1 "a" true] => "[1 \"a\" true]"
313show ({ x = 1 }) => "{ x = 1 }"
314show (describe "wip") => "describe \"wip\""
315show (\x -> x + 1) => "\\x -> x + 1"
316```
318---
320## 4. Lists and text
322### Basic list operations
324```
325(::) : a -> [a] -> [a] -- cons
326map : (a -> b) -> [a] -> [b]
327filter : (a -> Bool) -> [a] -> [a]
328length : [a] -> Int
329null : [a] -> Bool
330head : [a] -> a -- crash on []
331tail : [a] -> [a] -- crash on []
332last : [a] -> a -- crash on []
333nth : Int -> [a] -> a -- 0-based; crash out of range
334take : Int -> [a] -> [a] -- clamp to length
335drop : Int -> [a] -> [a] -- clamp to length
336member : a -> [a] -> Bool -- membership by ==
337range : Int -> Int -> [Int] -- range a b = [a (a+1) … (b-1)]
338foldl : (b -> a -> b) -> b -> [a] -> b
339```
341```
3421 :: [2 3] => [1 2 3]
343map (\x -> x * 2) [1 2 3] => [2 4 6]
344filter (\x -> x > 2) [1 2 3 4] => [3 4]
345length [1 2 3] => 3
346null [] => true
347head [5 6 7] => 5
348tail [5 6 7] => [6 7]
349last [5 6 7] => 7
350nth 1 [10 20 30] => 20
351take 99 [1 2] => [1 2]
352drop 99 [1 2] => []
353member 3 [1 2 3] => true
354range 1 6 => [1 2 3 4 5]
355range 5 1 => []
356foldl (+) 0 [1 2 3 4 5] => 15
357foldl (\a -> \b -> a - b) 0 [1 2 3]
358 => (0 - 6) -- ((0 - 1) - 2) - 3
359```
361`head`, `tail`, `last`, and out-of-range `nth` crash:
363```
364head [] -- CRASH: head: empty list
365nth 5 [1 2 3] -- CRASH: nth: index 5 out of range
366```
368### `(++)` and `concat` — overloaded on lists and text
370```
371(++) : m -> m -> m
372concat : [m] -> m
373```
375These dispatch on the kind of the argument: they concatenate lists and they
376concatenate text. `concat []` crashes (nothing to dispatch on) — supply a
377default with `or`, e.g. `concat xs or []`.
379```
380[1 2] ++ [3 4] => [1 2 3 4]
381"foo" ++ "bar" => "foobar"
382concat [[1 2] [3] []] => [1 2 3]
383concat ["foo" "bar"] => "foobar"
384concat [] or [] => []
385```
387### Text predicates and splitting
389```
390startsWith : Text -> Text -> Bool
391endsWith : Text -> Text -> Bool
392splitOn : Text -> Text -> [Text]
393```
395```
396startsWith "he" "hello" => true
397endsWith "rs" "lexer.rs" => true
398splitOn "," "a,b,c" => ["a" "b" "c"]
399splitOn "/" "src/lexer.rs" => ["src" "lexer.rs"]
400splitOn "/" "" => [""]
401```
403`splitOn` with an empty separator crashes.
405---
407## 5. The repository builtins
409These are the builtins that touch the repository, blobs, and diffs.
411### `replay` — apply a change to a snapshot
413```
414replay : Snapshot -> Change -> Snapshot
415```
417`replay onto ch` computes "`onto`, with the change from `ch.from` to `ch.to`
418applied." Where the change collides with what is already there, the path's
419blob becomes *unresolved* (a conflict) rather than an error. The one
420exception is a collision that would make a path a file on one side and a
421directory on another, as adding `./a/b` onto a file `./a` would: one entry
422cannot list a directory's entries, so `replay` crashes, naming the path, and
423`or` catches it. A conflict already at the path in `onto`, `ch.from` or
424`ch.to` is carried, directory sides and all, so a child that resolves such a
425conflict can still be squashed into it.
427```
428-- onto has "base", change takes "base" -> "theirs": no collision
429replay [({ path = ./f, content = blob "base" })]
430 ({ from = [({ path = ./f, content = blob "base" })],
431 to = [({ path = ./f, content = blob "theirs" })] })
432 => [({ content = blob "theirs", path = ["f"] })]
434-- onto has "onto-version" but the change expects "base": collision -> conflict
435unresolved ((head (replay [({ path = ./f, content = blob "onto" })]
436 ({ from = [({ path = ./f, content = blob "base" })],
437 to = [({ path = ./f, content = blob "theirs" })] }))).content)
438 => true
439```
441Two laws to internalise it: `replay b { from = b, to = x } = x` and
442`replay x { from = b, to = b } = x`.
444### `unresolved`, `blob`, `text` — blob primitives
446```
447unresolved : Blob -> Bool
448blob : Text -> Blob
449text : Blob -> Text
450```
452`blob t` builds a resolved regular-file blob with content `t`. `text b` reads a
453blob's content (its conflict-marker rendering if unresolved). `unresolved b`
454is `true` iff the blob is a conflict.
456```
457blob "content" => blob "content"
458text (blob "content") => "content"
459unresolved (blob "x") => false
460```
462### `by` — focus a commit by id
464```
465by : Id -> Repo -> Repo
466```
468Refocuses the repository on the commit with the given id; crashes if there is
469no such visible commit. Backed by the change-id index. Applied to one argument
470(`by @wqzt`) it is an `Edit`.
472```
473by @wqzt -- an edit: refocus on the commit @wqzt
474```
476### `subtreeCommits` — every commit of a tree
478```
479subtreeCommits : a -> [Commit]
480```
482Every commit of a tree (anything with `root` and `children`, so a `Subtree`
483or a `Repo`'s focused subtree), in preorder, in one pass. It gives exactly
484what `\t -> t.root :: (concat (map subtreeCommits t.children) or [])` gives,
485but in time linear in the size of the tree: written that way, every level
486copies the list below it. The reference config's `commits` is this builtin
487(§6).
489```
490subtreeCommits ({ root = 1, children = [({ root = 2, children = [] })] })
491 => [1 2]
492```
494### `meta` — commit metadata
496```
497meta : Id -> Meta
498```
500The git hash, author name and email, and committer time of the visible commit
501with change id `i`, as a `Meta` record. Crashes for an id with no stored
502commit (one minted in the current program, not yet persisted).
504```
505meta @wqzt => { hash = "74bee64e…", author = "Test User",
506 email = "test@example.com", time = 1789457265 }
507```
509### `validate` — dry-run persistence
511```
512validate : Repo -> Repo
513```
515Returns the repo unchanged if persisting it would succeed; otherwise crashes
516with exactly the error persistence would produce (shape, labels, immutability,
517focus). `tree . validate . rebase trunk` is an honest dry run.
519### `diff` and `difft` — rendering differences
521```
522diff : Blob -> Blob -> Text
523difft : Path -> Blob -> Blob -> Text
524```
526`diff a b` is a unified diff from the text of `a` to the text of `b`: no header
527lines, three lines of context, `""` if equal. Crashes if either is not UTF-8.
529```
530diff (blob "a\nb\n") (blob "a\nc\n")
531 => "@@ -1,2 +1,2 @@\n a\n-b\n+c\n"
532```
534`difft p a b` renders the comparison with **difftastic**, treating the input
535as the file `p` (its last component guides language detection). Needs `difft`
536on `PATH` (the Nix package provides it); options via `DFT_*` environment
537variables. Crashes naming the tool if absent. A `difft` that runs and fails
538is not an error: what it printed, even nothing, is the result, and what it
539says on stderr reaches the terminal.
541### `treeWith` — the history tree
543```
544treeWith : { detail : Int, margin : Bool, elide : Bool, icons : Bool, color : Text }
545 -> Repo -> Text
546```
548Renders the history as a tree with an options record. The reference config
549provides two ready-made settings, `tree` and `treeFull` (§9).
551```
552treeWith ({ detail = 2, margin = true, elide = false, icons = false, color = "auto" })
553 -- a full tree with ages and authors in the margin
554```
556### `extract` — every subvalue of a shape
558```
559extract : Shape -> a -> [b]
560```
562Returns every subvalue of a value that has shape `S`, however deeply nested.
563Walks lists in order and records in ascending field-name order, descending
564into every element and field (not into functions or blobs). A record is an `S`
565if its field set is exactly `S`'s.
567```
568extract Int ({ a = 1, b = 2 })
569 => [1 2]
570extract Int ({ a = 1, b = [2 "x" ({ c = 3 })] })
571 => [1 2 3]
572extract Commit repo -- every commit in the repository
573extract Push ys -- every Push record in a list, however nested
574```
576---
578## 6. The zipper: moving the focus
580A `Repo` is focused on one commit. These primitives move or detach the focus.
581Commands never select a commit by position — they use ids, labels, or
582predicates.
584### `up` — to the parent
586```
587up : Edit
588up = \repo -> …
589```
591Moves the focus to the parent commit, re-attaching the focused subtree among
592its siblings. Crashes at the top of history. This is the raw zipper move;
593`prev` (§7.4) is the friendlier version built on it.
595```
596up -- refocus on the parent (crash if already at the top)
597```
599### `detach` — remove the focused subtree
601```
602detach : Repo -> Detached
603```
605Detaches the focused subtree; the focus moves to the parent. Crashes at the
606top. Returns a `Detached` record (`{ subtree, rest }`). Used to move subtrees
607around (with `attach`).
609### `top` — to the top of history
611```
612top : Edit
613top = \repo -> top (up repo) or repo
614```
616Moves the focus all the way to the top of history (where `context = []`). The
617`or repo` is the base case: when `up` crashes at the top, stop.
619```
620top -- refocus at the very first commit
621commits . top -- every commit, preorder
622```
624### `commits` — every commit of a (sub)tree
626```
627commits : a -> [Commit]
628commits = subtreeCommits
629```
631Every commit of a subtree (or of the focused subtree of a `Repo`), in
632preorder. Takes anything with `root` and `children`, so the signature is left
633open. It is the builtin `subtreeCommits` (§5), which computes
634`\t -> t.root :: (concat (map commits t.children) or [])` in one pass. (There
635the `or []` handles a leaf, whose `children` is empty, so `concat` crashes and
636yields `[]`.) Written in the language, it copies the list below every commit,
637and takes time quadratic in the length of the history.
639```
640length . commits . top -- how many commits are visible?
641commits . top -- every commit, preorder from the top
642```
644---
646## 7. Structure and focusing
648### 7.1 Editing at the focus
650**`mapRoot : (Commit -> Commit) -> Edit`** — apply a function to the focused
651commit.
653```
654mapRoot = \f repo -> repo { root = f repo.root }
655```
657`describe` is `mapRoot` with a message setter (§8.1).
659**`mapChildren : ([Subtree] -> [Subtree]) -> Edit`** — apply a function to the
660focus's children.
662**`attach : Subtree -> Edit`** — attach a subtree as a new child of the focus
663(the focus does not move).
665```
666attach = \t -> mapChildren (++ [t])
667```
669**`remove : Edit`** — remove the focused commit; its children join the parent,
670and the focus moves to the parent.
672**`addChild : Commit -> Edit`** — attach a commit as a new child of the focus
673and move to it.
675**`newCommit : Snapshot -> Commit`** — a new commit with the given files, no
676message, no labels, and a freshly minted id (`@`). Because `@` is evaluated
677per application, each call mints a distinct id.
679```
680newCommit = \files -> { files = files, message = "", labels = [], id = @ }
681```
683### 7.2 Snapshots, changes, and filesets
685**`restrict : Fileset -> Snapshot -> Snapshot`** — the entries matching a
686fileset.
688```
689restrict (ext "rs") snap -- only the .rs files in snap
690```
692**`select : Fileset -> Snapshot -> Snapshot -> Snapshot`** — the entries
693matching `m` from the first snapshot, the rest from the second.
695```
696select = \m a b -> restrict m a ++ restrict (neg m) b
697```
699**`conflicted : Snapshot -> [Path]`** — the paths whose content is unresolved.
701**`changeOf : Repo -> Change`** — what the focus changes: its parent's files
702(`from`) and its own (`to`).
704**`invert : Change -> Change`** — the same change, backwards (`from`/`to`
705swapped). Used by `backout`.
707```
708invert ({ from = [], to = [({ path = ./a, content = blob "1" })] })
709 => { from = [({ content = blob "1", path = ["a"] })], to = [] }
710```
712**Fileset combinators** — filesets are functions `Path -> Bool`, composed
713pointwise:
715```
716everything : Fileset -- const true: every path
717neg : Fileset -> Fileset -- not . m
718both : Fileset -> Fileset -> Fileset -- m p && n p
719either : Fileset -> Fileset -> Fileset -- m p || n p
720under : Path -> Fileset -- inside a directory
721ext : Text -> Fileset -- a file extension
722```
724```
725under ./src ./src/main.rs => true
726under ./src ./README.md => false
727ext "rs" ./src/main.rs => true
728ext "rs" ./src/main.j => false
729both (ext "rs") (under ./src) ./src/main.rs
730 => true
731either (ext "j") (ext "rs") ./x.rs
732 => true
733neg (ext "rs") ./x.j => true
734everything ./anything/at/all => true
735```
737### 7.3 Revsets
739A revset is a function `Repo -> [Id]`: a set of commits, computed from a repo.
740It can be relative to the focus (`parents`, `kids`) or absolute (`all`,
741`%main`).
743```
744here : Revset -- [the focused commit's id]
745parents : Revset -- the focus's parents (crash-safe: [] at the top)
746kids : Revset -- the focus's children
747descendants : Revset -- the focus and everything below it
748ancestors : Revset -- the focus and everything above it
749siblings : Revset -- the other children of the focus's parent
750all : Revset -- every visible commit
751stack : Revset -- the line through the focus (ancestors ∪ descendants)
752labelled : Label -> Revset -- %main is labelled "main"
753trunk : Revset -- the main line: %main, %master, or %trunk, first found
754immutable : Revset -- commits that may never be rewritten (ancestorsOf trunk)
755```
757```
758parents -- the ids of the focus's parents
759kids -- the ids of the focus's children
760labelled "main" -- the commit the remote calls main (== %main)
761```
763**Revset combinators:**
765```
766from : Revset -> Revset -> Revset -- evaluate f starting from each commit of rs
767descendantsOf : Revset -> Revset
768ancestorsOf : Revset -> Revset
769union : Revset -> Revset -> Revset
770intersect : Revset -> Revset -> Revset
771minus : Revset -> Revset -> Revset
772matching : (Commit -> Bool) -> Revset -> Revset -- filter by a predicate
773conflicts : Revset -- commits with unresolved files
774firstOf : [Revset] -> Revset -- the first non-empty revset
775```
777```
778union ancestors descendants -- the stack (this is how `stack` is defined)
779minus all trunk -- everything not on the main line
780matching (\c -> c.message == "") all -- every commit with an empty message
781firstOf [%main %master %trunk] -- this is `trunk`
782ancestorsOf trunk -- this is `immutable`
783conflicts -- the commits that have conflicts
784```
786(These combine revsets, not raw lists — `union [1 2] [3]` is a contract error,
787because a revset is a function.)
789`immutable` is special: `j` evaluates it before persisting anything and refuses
790to change what it names. Merge commits and their ancestors are immutable
791regardless. Because the focus must always be mutable, the way to work on trunk
792is `new . goto trunk`, never `goto trunk` alone.
794### 7.4 Focusing with `goto` and friends
796**`goto : Revset -> Edit`** — refocus on *the one commit* a revset names. This
797is the only place a revset must have exactly one member; otherwise it crashes
798with `expected one revision, got N`.
800```
801goto = \rs repo ->
802 let is = rs repo
803 in if length is == 1 then by (head is) repo
804 else crash ("expected one revision, got " ++ show (length is))
805```
807```
808goto %main -- refocus on main (crash if it names 0 or 2+ commits)
809goto parents -- == prev
810```
812**`commitAt : Id -> Repo -> Commit`** — the commit with a given id.
813The reference definition uses `lookupCommit`, which indexes the repository
814once and retrieves commits without moving the focus. Its result is the same
815as `(by i repo).root`.
817**`prev : Edit`** — to the parent (`goto parents`).
819**`next : Edit`** — to the only child (`goto kids`; crashes if the focus has
820other than exactly one child).
822**`tip : Edit`** — the end of the line of only-children (`tip = \repo -> tip
823(next repo) or repo`).
825**`into : Id -> Edit`** — to a child, by id; crashes if it isn't a child of
826the focus.
828**`child : (Commit -> Bool) -> Edit`** — to the one child satisfying a
829predicate (`goto (matching p kids)`).
831**`at : Revset -> Edit -> Edit`** — run an edit somewhere else, then come back
832(by id, so the edit may move things).
834```
835at %main (describe "released")
836 -- describe main, then return the focus to where it was
837```
839**`forEach : Revset -> Edit -> Edit`** — run an edit at every commit of a
840revset, returning to the focus.
842```
843forEach descendants (describe "wip")
844 -- describe every commit below the focus "wip"
845```
847**`eachChild : Edit -> Edit`** — `forEach kids`: run an edit at every child.
849**`guard : (Repo -> Bool) -> Edit`** — crash unless a predicate holds.
851```
852guard clean -- refuse to continue if the focus has conflicts
853```
855**`clean : Repo -> Bool`** — `true` iff the focus has no unresolved files.
857---
859## 8. Commands
861These are the everyday edits. Each is an `Edit`, applied to the repository on
862the command line.
864### `describe` — set the message
866```
867describe : Text -> Edit
868describe = \m -> mapRoot (\c -> c { message = m })
869```
871Sets the message of the focused commit.
873```
874j 'describe "add the lexer"'
875```
877### `new` — start a new commit
879```
880new : Edit
881new = \repo -> addChild (newCommit repo.root.files) repo
882```
884Starts an empty child of the focus (with the focus's files) and moves to it.
886```
887j 'new'
888j 'describe "wip" . new' -- new commit, then describe it
889```
891### `abandon` — drop a commit
893```
894abandon : Edit
895```
897Removes the focused commit. Its children are rebased onto its parent, and a
898new empty commit on that parent becomes the focus. Refused (crashes) for a
899commit the remote has a name for — push something else under that name first,
900or forget the name.
902```
903j 'abandon' -- drop the focused commit
904```
906### `contract` — push files down into the parent
908```
909contract : Fileset -> Edit
910```
912Pushes the files matching `m` from the focus down into its parent.
914```
915j 'contract (ext "rs")' -- move the .rs changes into the parent commit
916```
918### `squash` — fold into the parent
920```
921squash : Edit
922squash = abandon . contract everything
923```
925Folds the focus entirely into its parent; a new empty commit becomes the
926focus. Defined as `contract everything` followed by `abandon`.
928```
929j 'squash'
930```
932### `split` — push files up into a new child
934```
935split : Fileset -> Edit
936```
938Pushes the files matching `m` from the focus up into a new child, which
939becomes the focus and inherits the old focus's children. Labels stay with the
940original commit (they are the remote's). The inverse of `contract`.
942```
943j 'split (\p -> p == ./a.txt)' -- split off just a.txt into a child commit
944```
946### `rebase` — move a subtree
948```
949rebase : Revset -> Edit
950```
952Moves the focused subtree onto another commit (the one the revset names), and
953returns to the moved commit. Crashes if the destination is inside the subtree
954being moved.
956```
957j 'rebase %main' -- move the current stack onto main
958```
960### `pick` — copy a change elsewhere
962```
963pick : Revset -> Edit
964```
966Applies the focused commit's change onto another commit, as a new child there.
968```
969j 'pick %main' -- copy the focused change onto main
970```
972### `backout` — undo a change
974```
975backout : Revset -> Edit
976```
978Creates a new child of the focus that undoes the change of the given commit
979(by replaying its inverse).
981```
982j 'backout @wqzt' -- a new commit undoing @wqzt's change
983```
985### Rebasing internals
987**`replayTree : Rebase -> Subtree -> Subtree`** — rewrite a subtree as if it
988had been written on `onto` instead of `from`, replaying each commit's change
989onto the new base and its children from old files to new.
991**`replayOnto : Commit -> Edit`** — replay the focused subtree onto a commit,
992in place (the subtree stays attached where it is; only its files change).
993Crashes at the top.
995**`rewrite : (Commit -> Commit) -> Edit`** — replace the focused commit with
996`f` applied to it, replaying its children so they follow.
998---
1000## 9. Pushing
1002`j push EXPR` evaluates `EXPR` to a list of `Push` and `Drop` records. These
1003functions build such lists.
1005**`label : Label -> Revset -> Repo -> [Push]`** — label every commit of a
1006revset.
1008```
1009j 'push (label "feature" here)' -- label the focused commit "feature"
1010```
1012**`unlabel : Label -> [Drop]`** — remove a label on the remote.
1014```
1015j 'push (unlabel "feature")' -- delete the "feature" bookmark
1016```
1018**`rename : Label -> Label -> Repo -> [b]`** — move a label.
1020```
1021j 'push (rename "old" "new")' -- rename the bookmark "old" to "new"
1022```
1024**`relabel : Repo -> [Push]`** — every label the remote already has, at its
1025current local commit. After rewriting a stack, this moves the remote's
1026bookmarks along to the rewritten commits.
1028```
1029j 'push relabel' -- after rewriting, re-point the remote's bookmarks
1030```
1032---
1034## 10. Inspection
1036These return printable values rather than a `Repo`.
1038**`focus : Repo -> Commit`** — the focused commit.
1040**`files : Repo -> Snapshot`** — the focus's files.
1042```
1043j 'files' -- the working-copy commit's files
1044```
1046**`entryAt : Path -> Snapshot -> [Entry]`** — the entry at a path, as a list
1047(`[]` if absent).
1049**`contentAt : Path -> Snapshot -> Blob`** — the content at a path, or an
1050empty file if absent.
1051The reference definition uses `lookupContent`, which shares a path index
1052between repeated lookups in each snapshot. Duplicate paths still select the
1053first entry, as `head (entryAt p s)` does.
1055Existing user configurations keep working with their original definitions.
1056To enable both indexed lookups, add these builtin declarations to your config:
1058```
1059lookupCommit : Id -> Repo -> Commit
1060lookupContent : Path -> Snapshot -> Blob
1061```
1063Then replace the bodies of `commitAt` and `contentAt` with `lookupCommit` and
1064`lookupContent`, respectively, keeping their existing signatures.
1066```
1067contentAt ./a [({ path = ./a, content = blob "x" })]
1068 => blob "x"
1069contentAt ./missing [] => blob ""
1070```
1072**`touched : Change -> [Path]`** — the paths a change touches.
1074**`changed : Repo -> [Path]`** — paths whose content differs between the focus
1075and its parent.
1077**`diffs : Repo -> [{ path : Path, diff : Text }]`** — what the focus changes,
1078rendered by difftastic, one record per changed path. (Replace `difft` with
1079`(\p -> diff)` to use the built-in unified diff instead.)
1081**`review : Repo -> Text`** — the same, as one page of text, or
1082`"no changes\n"` when the focus changes nothing. A `Text` result prints raw,
1083so `j review` is readable in the terminal. Like `diffs`, it crashes when
1084`difft` cannot be run (not on `PATH`); otherwise whatever `difft` prints,
1085even nothing, is the page, and what it says on stderr reaches the terminal.
1087```
1088j 'review' -- the focus's changes, as difftastic renders them
1089```
1091**`status : Repo -> { id : Id, message : Text, labels : [Label], changed : [Path], conflicts : [Path] }`** — a summary of the focus.
1093```
1094j 'status'
1095-- id @wqzt…
1096-- message add the lexer
1097-- labels [feature]
1098-- changed [src/lexer.rs]
1099-- conflicts []
1100```
1102**`log : Repo -> [{ id : Id, message : Text, labels : [Label], focus : Bool }]`**
1103— a summary of every commit, top first, in preorder (`focus` marks the focused
1104commit).
1106**`tree : Repo -> Text`** and **`treeFull : Repo -> Text`** — the history as a
1107tree, built with `treeWith`. `tree` is compact (`detail = 1`, elided);
1108`treeFull` shows ages and authors in the margin (`detail = 2`, `margin =
1109true`). Edit these definitions to change what `j tree` shows.
1111```
1112tree = treeWith ({ detail = 1, margin = false, elide = true, icons = false, color = "auto" })
1113treeFull = treeWith ({ detail = 2, margin = true, elide = false, icons = false, color = "auto" })
1114```
1116```
1117j 'tree'
1118-- ⌂ zzzz
1119-- └─ ● rpmt ▂
1120-- ▶ └─ ◌ lvpl add the lexer
1121```
1123---
1125## 11. Alphabetical index
1127| name | type | section |
1128|---|---|---|
1129| `(.)` | `(b -> c) -> (a -> b) -> a -> c` | §2 |
1130| `(==)` `(/=)` | `a -> a -> Bool` | §3 |
1131| `(&&)` `(\|\|)` `not` | booleans | §3 |
1132| `(+)` `(-)` `(*)` | `Int -> Int -> Int` | §3 |
1133| `(<)` `(<=)` `(>)` `(>=)` | `Int -> Int -> Bool` | §3 |
1134| `(++)` `concat` | lists & text | §4 |
1135| `(::)` | `a -> [a] -> [a]` | §4 |
1136| `abandon` | `Edit` | §8 |
1137| `addChild` | `Commit -> Edit` | §7.1 |
1138| `all` | `Revset` | §7.3 |
1139| `ancestors` | `Revset` | §7.3 |
1140| `ancestorsOf` | `Revset -> Revset` | §7.3 |
1141| `at` | `Revset -> Edit -> Edit` | §7.4 |
1142| `attach` | `Subtree -> Edit` | §7.1 |
1143| `backout` | `Revset -> Edit` | §8 |
1144| `blob` | `Text -> Blob` | §5 |
1145| `both` | `Fileset -> Fileset -> Fileset` | §7.2 |
1146| `by` | `Id -> Repo -> Repo` | §5 |
1147| `changeOf` | `Repo -> Change` | §7.2 |
1148| `changed` | `Repo -> [Path]` | §10 |
1149| `child` | `(Commit -> Bool) -> Edit` | §7.4 |
1150| `clean` | `Repo -> Bool` | §7.4 |
1151| `commitAt` | `Id -> Repo -> Commit` | §7.4 |
1152| `commits` | `a -> [Commit]` | §6 |
1153| `concat` | `[m] -> m` | §4 |
1154| `conflicted` | `Snapshot -> [Path]` | §7.2 |
1155| `conflicts` | `Revset` | §7.3 |
1156| `const` | `a -> b -> a` | §2 |
1157| `contentAt` | `Path -> Snapshot -> Blob` | §10 |
1158| `contract` | `Fileset -> Edit` | §8 |
1159| `crash` | `Text -> a` | §2 |
1160| `describe` | `Text -> Edit` | §8 |
1161| `descendants` | `Revset` | §7.3 |
1162| `descendantsOf` | `Revset -> Revset` | §7.3 |
1163| `detach` | `Repo -> Detached` | §6 |
1164| `diff` | `Blob -> Blob -> Text` | §5 |
1165| `diffs` | `Repo -> [{path, diff}]` | §10 |
1166| `difft` | `Path -> Blob -> Blob -> Text` | §5 |
1167| `drop` | `Int -> [a] -> [a]` | §4 |
1168| `eachChild` | `Edit -> Edit` | §7.4 |
1169| `either` | `Fileset -> Fileset -> Fileset` | §7.2 |
1170| `endsWith` | `Text -> Text -> Bool` | §4 |
1171| `entryAt` | `Path -> Snapshot -> [Entry]` | §10 |
1172| `everything` | `Fileset` | §7.2 |
1173| `ext` | `Text -> Fileset` | §7.2 |
1174| `extract` | `Shape -> a -> [b]` | §5 |
1175| `files` | `Repo -> Snapshot` | §10 |
1176| `filter` | `(a -> Bool) -> [a] -> [a]` | §4 |
1177| `firstOf` | `[Revset] -> Revset` | §7.3 |
1178| `focus` | `Repo -> Commit` | §10 |
1179| `foldl` | `(b -> a -> b) -> b -> [a] -> b` | §4 |
1180| `forEach` | `Revset -> Edit -> Edit` | §7.4 |
1181| `from` | `Revset -> Revset -> Revset` | §7.3 |
1182| `goto` | `Revset -> Edit` | §7.4 |
1183| `guard` | `(Repo -> Bool) -> Edit` | §7.4 |
1184| `head` | `[a] -> a` | §4 |
1185| `here` | `Revset` | §7.3 |
1186| `id` | `a -> a` | §2 |
1187| `immutable` | `Revset` | §7.3 |
1188| `intersect` | `Revset -> Revset -> Revset` | §7.3 |
1189| `into` | `Id -> Edit` | §7.4 |
1190| `invert` | `Change -> Change` | §7.2 |
1191| `kids` | `Revset` | §7.3 |
1192| `label` | `Label -> Revset -> Repo -> [Push]` | §9 |
1193| `labelled` | `Label -> Revset` | §7.3 |
1194| `last` | `[a] -> a` | §4 |
1195| `length` | `[a] -> Int` | §4 |
1196| `log` | `Repo -> […]` | §10 |
1197| `map` | `(a -> b) -> [a] -> [b]` | §4 |
1198| `mapChildren` | `([Subtree] -> [Subtree]) -> Edit` | §7.1 |
1199| `mapRoot` | `(Commit -> Commit) -> Edit` | §7.1 |
1200| `matching` | `(Commit -> Bool) -> Revset -> Revset` | §7.3 |
1201| `member` | `a -> [a] -> Bool` | §4 |
1202| `meta` | `Id -> Meta` | §5 |
1203| `minus` | `Revset -> Revset -> Revset` | §7.3 |
1204| `neg` | `Fileset -> Fileset` | §7.2 |
1205| `new` | `Edit` | §8 |
1206| `newCommit` | `Snapshot -> Commit` | §7.1 |
1207| `next` | `Edit` | §7.4 |
1208| `not` | `Bool -> Bool` | §3 |
1209| `nth` | `Int -> [a] -> a` | §4 |
1210| `null` | `[a] -> Bool` | §4 |
1211| `parents` | `Revset` | §7.3 |
1212| `pick` | `Revset -> Edit` | §8 |
1213| `prev` | `Edit` | §7.4 |
1214| `range` | `Int -> Int -> [Int]` | §4 |
1215| `rebase` | `Revset -> Edit` | §8 |
1216| `relabel` | `Repo -> [Push]` | §9 |
1217| `remove` | `Edit` | §7.1 |
1218| `rename` | `Label -> Label -> Repo -> [b]` | §9 |
1219| `replay` | `Snapshot -> Change -> Snapshot` | §5 |
1220| `replayOnto` | `Commit -> Edit` | §8 |
1221| `replayTree` | `Rebase -> Subtree -> Subtree` | §8 |
1222| `restrict` | `Fileset -> Snapshot -> Snapshot` | §7.2 |
1223| `review` | `Repo -> Text` | §10 |
1224| `rewrite` | `(Commit -> Commit) -> Edit` | §8 |
1225| `select` | `Fileset -> Snapshot -> Snapshot -> Snapshot` | §7.2 |
1226| `show` | `a -> Text` | §3 |
1227| `siblings` | `Revset` | §7.3 |
1228| `split` | `Fileset -> Edit` | §8 |
1229| `splitOn` | `Text -> Text -> [Text]` | §4 |
1230| `squash` | `Edit` | §8 |
1231| `stack` | `Revset` | §7.3 |
1232| `startsWith` | `Text -> Text -> Bool` | §4 |
1233| `status` | `Repo -> {…}` | §10 |
1234| `subtreeCommits` | `a -> [Commit]` | §5 |
1235| `tail` | `[a] -> [a]` | §4 |
1236| `take` | `Int -> [a] -> [a]` | §4 |
1237| `text` | `Blob -> Text` | §5 |
1238| `tip` | `Edit` | §7.4 |
1239| `top` | `Edit` | §6 |
1240| `touched` | `Change -> [Path]` | §10 |
1241| `tree` | `Repo -> Text` | §10 |
1242| `treeFull` | `Repo -> Text` | §10 |
1243| `treeWith` | `{…} -> Repo -> Text` | §5 |
1244| `trunk` | `Revset` | §7.3 |
1245| `under` | `Path -> Fileset` | §7.2 |
1246| `union` | `Revset -> Revset -> Revset` | §7.3 |
1247| `unlabel` | `Label -> [Drop]` | §9 |
1248| `unresolved` | `Blob -> Bool` | §5 |
1249| `up` | `Edit` | §6 |
1250| `user` | `{ name : Text, email : Text }` | §1.1 |
1251| `validate` | `Repo -> Repo` | §5 |