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
18How to read the three most common aliases:
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
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
35### 1.1 `user` — who commits
38user : { name : Text, email : Text }
41The identity `j` commits as. `j` reads this definition itself; it is required.
42The reference value is a placeholder you must edit:
45user = { name = "Your Name", email = "you@example.com" }
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).
50Creating a repository or saving changes also rejects empty fields and the
51reference placeholders. Use your own name and email before `j init`,
52`j clone`, or an edit that saves changes. `j help config` shows your actual
53config path and an example, even if the config is broken.
54For a new installation, `j setup 'Your actual name' you@your-domain.example`
55creates the complete config with that identity. It never overwrites an
58### 1.2 Paths, labels, entries, snapshots
60**`Path = [Text]`** — a file path as its components. Written `./a/b`, which is
61`["a" "b"]`. A trailing `/` is ignored; a bare `./` is `[]`, the repository
62root. For names the literal cannot spell (spaces, unusual characters), build
63one with `splitOn "/"`.
66./src/lexer.rs => ["src" "lexer.rs"]
68splitOn "/" "a b/c" => ["a b" "c"]
71**`Label = Text`** — a bookmark name on the remote. A label is written `%main`
72in an expression (a `Revset`, see `labelled`); as data it is just text.
74**`Entry = { path : Path, content : Blob }`** — one file in a snapshot: its
77**`Snapshot = [Entry]`** — the files of one commit. Paths are unique. A
78deleted, resolved path simply has no entry (there is no "deletion" marker).
81[({ path = ./a.txt, content = blob "hello" })]
82 => a snapshot of one file, a.txt, containing "hello"
85**`Change = { from : Snapshot, to : Snapshot }`** — the files before and after
86a change. Used by `replay`, `invert`, `touched`, `changeOf`.
88**`Meta = { hash : Text, author : Text, email : Text, time : Int }`** — what jj
89records about a commit beyond the model: its git hash, author name and email,
90and committer time. Produced by the `meta` builtin.
92### 1.3 Commits, subtrees, and the repository
94**`Commit`** — one commit:
97Commit = { files : Snapshot -- the files
98 , message : Text -- the description
99 , labels : [Label] -- the remote's names for it (read-only)
100 , id : Id } -- its change id (unique)
103`labels` is read-only in the language: only `fetch` and `push` change it.
105**`Subtree = { root : Commit, children : [Subtree] }`** — a commit and,
106recursively, everything below it.
108**`Frame = { parent : Commit, left : [Subtree], right : [Subtree] }`** — one
109ancestor on the path back to the top, with a hole where the focused subtree
110was. `left` and `right` are the siblings on either side (presentation order
113**`Repo = { root : Commit, children : [Subtree], context : [Frame] }`** — the
114whole history, seen from one commit (the *focus*). `root` is the focused
115commit, `children` its children, `context` the path back up to the top
116(`context = []` at the top of history). A `Repo` is a **zipper**.
118**`Detached = { subtree : Subtree, rest : Repo }`** — the result of `detach`:
119a subtree, and the repository with that subtree removed.
121**`Rebase = { from : Snapshot, onto : Snapshot }`** — the base a subtree was
122written on, and the new base to rewrite it onto. Used by `replayTree`.
124### 1.4 Aliases for functions
127Edit = Repo -> Repo -- a command; may crash
128Fileset = Path -> Bool -- which paths a command applies to
129Revset = Repo -> [Id] -- a set of commits, computed from a repo
132These aliases are unfolded by the contract checker: `at : Revset -> Edit ->
133Edit` checks three arguments and a `Repo` result.
137**`Push = { id : Id, name : Label }`** — set the remote's bookmark `name` to
138the commit `id`, creating it if absent.
140**`Drop = { delete : Label }`** — delete the remote's bookmark of that name.
142`j push EXPR` evaluates `EXPR` to a list of `Push` and `Drop` records. Built
143with `label`, `unlabel`, `rename`, `relabel` (§8).
146[({ id = @wqzt, name = "main" })] -- a Push: point main at @wqzt
147[({ delete = "old-feature" })] -- a Drop: delete the old-feature bookmark
152## 2. Functions and control flow
154These are the fundamental combinators. `or` is a keyword, not a builtin.
156### `(.)` — composition
159(.) : (b -> c) -> (a -> b) -> a -> c
162Right-to-left function composition: `(f . g) x = f (g x)`. Right-associative,
163binds very tightly (`infixr 9`). This is how edits are chained.
166((\x -> x + 1) . (\x -> x * 2)) 5
169describe "wip" . new -- start a new commit, then describe it
170tree . squash -- squash the focus, then render the tree
179The identity function. As an edit it does nothing, which makes it the
180universal fallback for `or`.
185edit or id -- run edit, keep the repository if it crashes
188### `save` — record file edits
194The identity edit, with a name for the everyday action: `j save` records the
195working-directory snapshot without changing the commit's description or
196focus. It creates no operation when nothing changed. `j id` does the same.
198### `preview` — validate an edit without saving
201preview : Edit -> Repo -> Text
204Apply the edit, validate its result, and render the resulting tree. Because
205the final value is text, no edit or working-directory snapshot is recorded.
206Validation errors report why the edit could not be saved.
210j 'preview (rebase trunk)'
213This is the convenient form of `tree . validate . EDIT`. `validate . EDIT`
214alone returns a repository and saves it.
216### `const` — constant function
222`const a b = a` — ignores its second argument.
226map (const 0) [1 2 3] => [0 0 0]
227everything -- defined as const true
230### `crash` — abort with a message
236Aborts evaluation with the given message. Every interpreter-detected runtime
237error is also a crash, and all crashes are catchable by `or`.
240crash "something went wrong"
241 -- CRASH: something went wrong
243(crash "boom") or "recovered"
247### `or` — catching crashes (keyword)
249`a or b` evaluates `a`. If `a` crashes, the crash is discarded and `b` is
250returned. Otherwise `a` is the result — unless `a` is a function and `b` is
251too, in which case the result is pointwise: `(f or g) x = f x or g x`.
2551 or head [] => 1 -- head [] never runs
256edit or id -- try edit, fall back to id
259The lifting is what makes `edit or id` meaningful: a function value cannot
260crash until applied, so without pointwise lifting the `or` would catch nothing.
264## 3. Comparison, booleans, integers, `show`
266### Equality: `(==)` and `(/=)`
273Structural equality on ints, texts, bools, lists, and records; identity on
274`Id`; content-and-mode on `Blob`. `/=` is the negation. **Comparing two
280[1 2] == [1 2] => true
281{ a = 1 } == { a = 1 } => true
285### Booleans: `(&&)`, `(||)`, `not`
288(&&) : Bool -> Bool -> Bool -- short-circuit
289(||) : Bool -> Bool -> Bool -- short-circuit
293`&&` and `||` short-circuit (the right operand is not evaluated if the left
294decides the result). Operands must be `Bool`. Infix use requires their
295declarations in `config.j` and checks the contracts of evaluated operands
296and the result; a skipped right operand is not checked.
2991 == 1 && 2 == 2 => true
3001 == 2 || 3 == 3 => true
305### Integer arithmetic: `(+)`, `(-)`, `(*)`
308(+) : Int -> Int -> Int
309(-) : Int -> Int -> Int
310(*) : Int -> Int -> Int
313Arbitrary-precision: never overflows. There is no division builtin. No unary
314minus — write `0 - 5`.
319999999999999 * 999999999999
320 => 999999999998000000000001
324### Integer comparison: `(<)`, `(<=)`, `(>)`, `(>=)`
326Defined on `Int` only; crash on anything else. Non-associative (cannot chain:
327`1 < 2 < 3` is a parse error).
332(1 < 2) == true => true -- parenthesised, since < is non-associative
335### `show` — render a value as text
341When it succeeds, `show` renders a value in the language's literal syntax.
342Except for closures and unresolved blobs, the text parses and evaluates back
343to an equal value. A top-level `Text` prints raw, so `j show …` is how you inspect values.
344See §5.2 of the spec for the exact rendering.
346Regular UTF-8 blobs render as `blob "…"`; binary blobs, executables, and
347symlinks render as `blobHex "kind" "…"`, preserving bytes and file type with
348lowercase hexadecimal content. An older custom config without
349`blobHex : Text -> Text -> Blob` must add that declaration before showing
350these values; otherwise `show` crashes with instructions to do so.
355show [1 "a" true] => "[1 \"a\" true]"
356show ({ x = 1 }) => "{ x = 1 }"
357show (describe "wip") => "describe \"wip\""
358show (\x -> x + 1) => "\\x -> x + 1"
365### Basic list operations
368(::) : a -> [a] -> [a] -- cons
369map : (a -> b) -> [a] -> [b]
370filter : (a -> Bool) -> [a] -> [a]
373head : [a] -> a -- crash on []
374tail : [a] -> [a] -- crash on []
375last : [a] -> a -- crash on []
376nth : Int -> [a] -> a -- 0-based; crash out of range
377take : Int -> [a] -> [a] -- clamp to length
378drop : Int -> [a] -> [a] -- clamp to length
379member : a -> [a] -> Bool -- membership by ==
380range : Int -> Int -> [Int] -- range a b = [a (a+1) … (b-1)]
381foldl : (b -> a -> b) -> b -> [a] -> b
386map (\x -> x * 2) [1 2 3] => [2 4 6]
387filter (\x -> x > 2) [1 2 3 4] => [3 4]
393nth 1 [10 20 30] => 20
394take 99 [1 2] => [1 2]
396member 3 [1 2 3] => true
397range 1 6 => [1 2 3 4 5]
399foldl (+) 0 [1 2 3 4 5] => 15
400foldl (\a -> \b -> a - b) 0 [1 2 3]
401 => (0 - 6) -- ((0 - 1) - 2) - 3
404`head`, `tail`, `last`, and out-of-range `nth` crash:
407head [] -- CRASH: head: empty list
408nth 5 [1 2 3] -- CRASH: nth: index 5 out of range
411### `(++)` and `concat` — overloaded on lists and text
418These dispatch on the kind of the argument: they concatenate lists and they
419concatenate text. `concat []` crashes (nothing to dispatch on) — supply a
420default with `or`, e.g. `concat xs or []`.
423[1 2] ++ [3 4] => [1 2 3 4]
424"foo" ++ "bar" => "foobar"
425concat [[1 2] [3] []] => [1 2 3]
426concat ["foo" "bar"] => "foobar"
430### Text predicates and splitting
433startsWith : Text -> Text -> Bool
434endsWith : Text -> Text -> Bool
435splitOn : Text -> Text -> [Text]
439startsWith "he" "hello" => true
440endsWith "rs" "lexer.rs" => true
441splitOn "," "a,b,c" => ["a" "b" "c"]
442splitOn "/" "src/lexer.rs" => ["src" "lexer.rs"]
443splitOn "/" "" => [""]
446`splitOn` with an empty separator crashes.
450## 5. The repository builtins
452These are the builtins that touch the repository, blobs, and diffs.
454### `replay` — apply a change to a snapshot
457replay : Snapshot -> Change -> Snapshot
460`replay onto ch` computes "`onto`, with the change from `ch.from` to `ch.to`
461applied." Where the change collides with what is already there, the path's
462blob becomes *unresolved* (a conflict) rather than an error. The one
463exception is a collision that would make a path a file on one side and a
464directory on another, as adding `./a/b` onto a file `./a` would: one entry
465cannot list a directory's entries, so `replay` crashes, naming the path, and
466`or` catches it. A conflict already at the path in `onto`, `ch.from` or
467`ch.to` is carried, directory sides and all, so a child that resolves such a
468conflict can still be squashed into it.
471-- onto has "base", change takes "base" -> "theirs": no collision
472replay [({ path = ./f, content = blob "base" })]
473 ({ from = [({ path = ./f, content = blob "base" })],
474 to = [({ path = ./f, content = blob "theirs" })] })
475 => [({ content = blob "theirs", path = ["f"] })]
477-- onto has "onto-version" but the change expects "base": collision -> conflict
478unresolved ((head (replay [({ path = ./f, content = blob "onto" })]
479 ({ from = [({ path = ./f, content = blob "base" })],
480 to = [({ path = ./f, content = blob "theirs" })] }))).content)
484Two laws to internalise it: `replay b { from = b, to = x } = x` and
485`replay x { from = b, to = b } = x`.
487### `unresolved`, `blob`, `blobHex`, `text` — blob primitives
490unresolved : Blob -> Bool
492blobHex : Text -> Text -> Blob
496`blob t` builds a resolved regular-file blob with content `t`. `text b` reads a
497blob's content (its conflict-marker rendering if unresolved). `unresolved b`
498is `true` iff the blob is a conflict.
500`blobHex kind hex` constructs a resolved blob from its file kind and bytes.
501The kind must be `"regular"`, `"executable"`, or `"symlink"`; the bytes must
502be an even-length hexadecimal text, using uppercase or lowercase letters.
503An unknown kind or malformed hex crashes. `text` crashes on non-UTF-8 bytes;
504persisting a symlink also requires a UTF-8 target.
507blob "content" => blob "content"
508text (blob "content") => "content"
509text (blobHex "regular" "6869") => "hi"
510unresolved (blob "x") => false
513### `by` — focus a commit by id
516by : Id -> Repo -> Repo
519Refocuses the repository on the commit with the given id; crashes if there is
520no such visible commit. Backed by the change-id index. Applied to one argument
521(`by @wqzt`) it is an `Edit`.
524by @wqzt -- an edit: refocus on the commit @wqzt
527### `subtreeCommits` — every commit of a tree
530subtreeCommits : a -> [Commit]
533Every commit of a tree (anything with `root` and `children`, so a `Subtree`
534or a `Repo`'s focused subtree), in preorder, in one pass. It gives exactly
535what `\t -> t.root :: (concat (map subtreeCommits t.children) or [])` gives,
536but in time linear in the size of the tree: written that way, every level
537copies the list below it. The reference config's `commits` is this builtin
541subtreeCommits ({ root = 1, children = [({ root = 2, children = [] })] })
545### `meta` — commit metadata
551The git hash, author name and email, and committer time of the visible commit
552with change id `i`, as a `Meta` record. Crashes for an id with no stored
553commit (one minted in the current program, not yet persisted).
556meta @wqzt => { hash = "74bee64e…", author = "Test User",
557 email = "test@example.com", time = 1789457265 }
560### `validate` — check whether persistence would succeed
563validate : Repo -> Repo
566Returns the repo unchanged if persisting it would succeed; otherwise crashes
567with exactly the error persistence would produce (shape, root, labels, immutability,
568focus). A changed repository also checks that the author identity is configured;
569an unchanged repository can still be inspected with the default identity.
570`tree . validate . rebase trunk` is an honest dry run.
571`validate . rebase trunk` alone saves the result: `validate` returns the same
572repository it was given, not the repository from before the edit. Prefer
573`preview (rebase trunk)` for the complete safe-preview expression.
575### `diff` and `difft` — rendering differences
578diff : Blob -> Blob -> Text
579difft : Path -> Blob -> Blob -> Text
582`diff a b` is a unified diff from the text of `a` to the text of `b`: no header
583lines, three lines of context, `""` if equal. Crashes if either is not UTF-8.
586diff (blob "a\nb\n") (blob "a\nc\n")
587 => "@@ -1,2 +1,2 @@\n a\n-b\n+c\n"
590`difft p a b` renders the comparison with **difftastic**, treating the input
591as the file `p` (its last component guides language detection), using options
592from `DFT_*` environment variables. The Nix package provides `difft`.
593When the executable is missing, the builtin returns a unified diff with
594`a/` and `b/` path headers and three lines of context; binary content receives
595a difference summary. Other launch failures are crashes naming the tool and
596OS error. A `difft` that runs and fails is not an error: what it printed,
597even nothing, is the result, and what it says on stderr reaches the terminal.
599### `treeWith` — the history tree
602treeWith : { detail : Int, margin : Bool, elide : Bool, icons : Bool, color : Text,
603 lanes : Int, author : Bool, date : Bool, files : Bool }
607Renders the history as a tree with an options record. The reference config
608provides ready-made settings, including `tree` and `treeFull` (§10).
611treeWith ({ detail = 2, margin = true, elide = false, icons = false, color = "auto", lanes = 4, author = false, date = false, files = false })
612 -- a full tree with ages and authors in the margin
615Branches beyond the `lanes` limit retain their state marker beside `»`.
616Increase `lanes` to display more rails. Long file paths in detail rows wrap
617to the available terminal width.
619### `extract` — every subvalue of a shape
622extract : Shape -> a -> [b]
625Returns every subvalue of a value that has shape `S`, however deeply nested.
626Walks lists in order and records in ascending field-name order, descending
627into every element and field (not into functions or blobs). A record is an `S`
628if its field set is exactly `S`'s.
631extract Int ({ a = 1, b = 2 })
633extract Int ({ a = 1, b = [2 "x" ({ c = 3 })] })
635extract Commit repo -- every commit in the repository
636extract Push ys -- every Push record in a list, however nested
641## 6. The zipper: moving the focus
643A `Repo` is focused on one commit. These primitives move or detach the focus.
644Commands never select a commit by position — they use ids, labels, or
647### `up` — to the parent
654Moves the focus to the parent commit, re-attaching the focused subtree among
655its siblings. Crashes at the top of history. This is the raw zipper move;
656`prev` (§7.4) is the friendlier version built on it.
659up -- refocus on the parent (crash if already at the top)
662### `detach` — remove the focused subtree
665detach : Repo -> Detached
668Detaches the focused subtree; the focus moves to the parent. Crashes at the
669top. Returns a `Detached` record (`{ subtree, rest }`). Used to move subtrees
670around (with `attach`).
672### `top` — to the top of history
676top = \repo -> top (up repo) or repo
679Moves the focus all the way to the top of history (where `context = []`). The
680`or repo` is the base case: when `up` crashes at the top, stop. Use it inside
681an inspection such as `commits . top` or `tree . top`; `j top` alone cannot
682save a focus on the protected root commit.
685top -- refocus at the very first commit
686commits . top -- every commit, preorder
689### `commits` — every commit of a (sub)tree
692commits : a -> [Commit]
693commits = subtreeCommits
696Every commit of a subtree (or of the focused subtree of a `Repo`), in
697preorder. Takes anything with `root` and `children`, so the signature is left
698open. It is the builtin `subtreeCommits` (§5), which computes
699`\t -> t.root :: (concat (map commits t.children) or [])` in one pass. (There
700the `or []` handles a leaf, whose `children` is empty, so `concat` crashes and
701yields `[]`.) Written in the language, it copies the list below every commit,
702and takes time quadratic in the length of the history.
705length . commits . top -- how many commits are visible?
706commits . top -- every commit, preorder from the top
711## 7. Structure and focusing
713### 7.1 Editing at the focus
715**`mapRoot : (Commit -> Commit) -> Edit`** — apply a function to the focused
719mapRoot = \f repo -> repo { root = f repo.root }
722`describe` is `mapRoot` with a message setter (§8.1).
724**`mapChildren : ([Subtree] -> [Subtree]) -> Edit`** — apply a function to the
727**`attach : Subtree -> Edit`** — attach a subtree as a new child of the focus
728(the focus does not move).
731attach = \t -> mapChildren (++ [t])
734**`remove : Edit`** — remove the focused commit; its children join the parent,
735and the focus moves to the parent.
737**`addChild : Commit -> Edit`** — attach a commit as a new child of the focus
740**`newCommit : Snapshot -> Commit`** — a new commit with the given files, no
741message, no labels, and a freshly minted id (`@`). Because `@` is evaluated
742per application, each call mints a distinct id.
745newCommit = \files -> { files = files, message = "", labels = [], id = @ }
748### 7.2 Snapshots, changes, and filesets
750**`restrict : Fileset -> Snapshot -> Snapshot`** — the entries matching a
754restrict (ext "rs") snap -- only the .rs files in snap
757**`select : Fileset -> Snapshot -> Snapshot -> Snapshot`** — the entries
758matching `m` from the first snapshot, the rest from the second.
761select = \m a b -> restrict m a ++ restrict (neg m) b
764**`conflicted : Snapshot -> [Path]`** — the paths whose content is unresolved.
766**`changeOf : Repo -> Change`** — what the focus changes: its parent's files
767(`from`) and its own (`to`).
769**`invert : Change -> Change`** — the same change, backwards (`from`/`to`
770swapped). Used by `backout`.
773invert ({ from = [], to = [({ path = ./a, content = blob "1" })] })
774 => { from = [({ content = blob "1", path = ["a"] })], to = [] }
777**Fileset combinators** — filesets are functions `Path -> Bool`, composed
781everything : Fileset -- const true: every path
782neg : Fileset -> Fileset -- not . m
783both : Fileset -> Fileset -> Fileset -- m p && n p
784either : Fileset -> Fileset -> Fileset -- m p || n p
785under : Path -> Fileset -- inside a directory
786ext : Text -> Fileset -- a file extension
790under ./src ./src/main.rs => true
791under ./src ./README.md => false
792ext "rs" ./src/main.rs => true
793ext "rs" ./src/main.j => false
794both (ext "rs") (under ./src) ./src/main.rs
796either (ext "j") (ext "rs") ./x.rs
798neg (ext "rs") ./x.j => true
799everything ./anything/at/all => true
804A revset is a function `Repo -> [Id]`: a set of commits, computed from a repo.
805It can be relative to the focus (`parents`, `kids`) or absolute (`all`,
809here : Revset -- [the focused commit's id]
810parents : Revset -- the focus's parents (crash-safe: [] at the top)
811kids : Revset -- the focus's children
812descendants : Revset -- the focus and everything below it
813ancestors : Revset -- the focus and everything above it
814siblings : Revset -- the other children of the focus's parent
815all : Revset -- every visible commit
816stack : Revset -- the line through the focus (ancestors ∪ descendants)
817labelled : Label -> Revset -- %main is labelled "main"
818trunk : Revset -- the main line: %main, %master, or %trunk, first found
819immutable : Revset -- commits that may never be rewritten (ancestorsOf trunk)
823parents -- the ids of the focus's parents
824kids -- the ids of the focus's children
825labelled "main" -- the commit the remote calls main (== %main)
828**Revset combinators:**
831from : Revset -> Revset -> Revset -- evaluate f starting from each commit of rs
832descendantsOf : Revset -> Revset
833ancestorsOf : Revset -> Revset
834union : Revset -> Revset -> Revset
835intersect : Revset -> Revset -> Revset
836minus : Revset -> Revset -> Revset
837matching : (Commit -> Bool) -> Revset -> Revset -- filter by a predicate
838conflicts : Revset -- commits with unresolved files
839firstOf : [Revset] -> Revset -- the first non-empty revset
843union ancestors descendants -- the stack (this is how `stack` is defined)
844minus all trunk -- everything not on the main line
845matching (\c -> c.message == "") all -- every commit with an empty message
846firstOf [%main %master %trunk] -- this is `trunk`
847ancestorsOf trunk -- this is `immutable`
848conflicts -- the commits that have conflicts
851(These combine revsets, not raw lists — `union [1 2] [3]` is a contract error,
852because a revset is a function.)
854`immutable` is special: `j` evaluates it before persisting anything and refuses
855to change what it names. Merge commits and their ancestors are immutable
856regardless. Because the focus must always be mutable, the way to work on trunk
857is `new . goto trunk`, never `goto trunk` alone.
859### 7.4 Focusing with `goto` and friends
861**`goto : Revset -> Edit`** — refocus on *the one commit* a revset names. This
862is the only place a revset must have exactly one member; otherwise it crashes
863with `expected one revision, got N` and recovery guidance. An empty selector
864points to `j tree` and remote/fetch setup; an ambiguous selector shows up to
865eight candidate ids and descriptions, with commands to select a mutable commit
866or create a child. Candidate ids are complete copyable id literals.
869new . goto %main -- start a mutable child on main
870tree . goto %main -- inspect main without moving the working copy
871goto parents -- == prev
874**`commitAt : Id -> Repo -> Commit`** — the commit with a given id.
875The reference definition uses `lookupCommit`, which indexes the repository
876once and retrieves commits without moving the focus. Its result is the same
877as `(by i repo).root`.
879**`prev : Edit`** — to the parent (`goto parents`). Like every navigation edit,
880it checks out the selected commit. A protected parent cannot remain focused;
881inspect it with `tree . prev` or start a new child instead.
883**`next : Edit`** — to the only child. At a tip it suggests `j new`; at a fork
884it lists candidates to choose with `by`.
886**`tip : Edit`** — the end of the line of only-children (`tip = \repo -> tip
887(next repo) or repo`).
889**`into : Id -> Edit`** — to a child, by id; crashes if it isn't a child of
892**`child : (Commit -> Bool) -> Edit`** — to the one child satisfying a
893predicate (`goto (matching p kids)`).
895**`at : Revset -> Edit -> Edit`** — run an edit somewhere else, then come back
896(by id, so the edit may move things).
899at %main (describe "released")
900 -- describe main, then return the focus to where it was
903**`forEach : Revset -> Edit -> Edit`** — run an edit at every commit of a
904revset, returning to the focus.
907forEach descendants (describe "wip")
908 -- describe every commit below the focus "wip"
911**`eachChild : Edit -> Edit`** — `forEach kids`: run an edit at every child.
913**`guard : (Repo -> Bool) -> Edit`** — crash unless a predicate holds.
916guard clean -- refuse to continue if the focus has conflicts
919**`clean : Repo -> Bool`** — `true` iff the focus has no unresolved files.
925These are the everyday edits. Each is an `Edit`, applied to the repository on
928### `describe` — set the message
931describe : Text -> Edit
932describe = \m -> mapRoot (\c -> c { message = m })
935Sets the message of the focused commit.
938j 'describe "add the lexer"'
941### `new` — start a new commit
945new = \repo -> addChild (newCommit repo.root.files) repo
948Starts an empty child of the focus (with the focus's files) and moves to it.
952j 'describe "wip" . new' -- new commit, then describe it
955### `abandon` — drop a commit
961Removes the focused commit. Its children are rebased onto its parent, and a
962new empty commit on that parent becomes the focus. Refused (crashes) for a
963commit the remote has a name for — push something else under that name first,
967j 'abandon' -- drop the focused commit
970### `contract` — push files down into the parent
973contract : Fileset -> Edit
976Pushes the files matching `m` from the focus down into its parent.
979j 'contract (ext "rs")' -- move the .rs changes into the parent commit
982### `squash` — fold into the parent
986squash = abandon . contract everything
989Folds the focus entirely into its parent; a new empty commit becomes the
990focus. Defined as `contract everything` followed by `abandon`.
996### `split` — push files up into a new child
999split : Fileset -> Edit
1002Pushes the files matching `m` from the focus up into a new child, which
1003becomes the focus and inherits the old focus's children. Labels stay with the
1004original commit (they are the remote's). The inverse of `contract`.
1007j 'split (\p -> p == ./a.txt)' -- split off just a.txt into a child commit
1010### `rebase` — move a subtree
1013rebase : Revset -> Edit
1016Moves the focused subtree onto another commit (the one the revset names), and
1017returns to the moved commit. Crashes if the destination is inside the subtree
1021j 'rebase %main' -- move the current stack onto main
1024### `pick` — copy a change elsewhere
1027pick : Revset -> Edit
1030Applies the focused commit's change onto another commit, as a new child there.
1033j 'pick %main' -- copy the focused change onto main
1036### `backout` — undo a change
1039backout : Revset -> Edit
1042Creates a new child of the focus that undoes the change of the given commit
1043(by replaying its inverse).
1046j 'backout @wqzt' -- a new commit undoing @wqzt's change
1049### Rebasing internals
1051**`replayTree : Rebase -> Subtree -> Subtree`** — rewrite a subtree as if it
1052had been written on `onto` instead of `from`, replaying each commit's change
1053onto the new base and its children from old files to new.
1055**`replayOnto : Commit -> Edit`** — replay the focused subtree onto a commit,
1056in place (the subtree stays attached where it is; only its files change).
1059**`rewrite : (Commit -> Commit) -> Edit`** — replace the focused commit with
1060`f` applied to it, replaying its children so they follow.
1066`j push EXPR` evaluates `EXPR` to a list of `Push` and `Drop` records. These
1067functions build such lists.
1068Push sends recorded commits. Unrecorded file edits produce a warning before
1069the recorded content is sent. Run `j id`, then push again to include those
1070edits. Inspection commands such as `j status` do not record them.
1072**`label : Label -> Revset -> Repo -> [Push]`** — label every commit of a
1076j 'push (label "feature" here)' -- label the focused commit "feature"
1079**`unlabel : Label -> [Drop]`** — remove a label on the remote.
1082j 'push (unlabel "feature")' -- delete the "feature" bookmark
1085**`rename : Label -> Label -> Repo -> [b]`** — move a label.
1088j 'push (rename "old" "new")' -- rename the bookmark "old" to "new"
1091**`relabel : Repo -> [Push]`** — every label the remote already has, at its
1092current local commit. After rewriting a stack, this moves the remote's
1093bookmarks along to the rewritten commits.
1096j 'push relabel' -- after rewriting, re-point the remote's bookmarks
1103These return printable values rather than a `Repo`. They include current
1104file edits in the displayed snapshot without recording those edits.
1106**`focus : Repo -> Commit`** — the focused commit.
1108**`files : Repo -> Snapshot`** — the focus's files.
1111j 'files' -- the working-copy commit's files
1114**`entryAt : Path -> Snapshot -> [Entry]`** — the entry at a path, as a list
1117**`contentAt : Path -> Snapshot -> Blob`** — the content at a path, or an
1118empty file if absent.
1119The reference definition uses `lookupContent`, which shares a path index
1120between repeated lookups in each snapshot. Duplicate paths still select the
1121first entry, as `head (entryAt p s)` does.
1123Existing user configurations keep working with their original definitions.
1124To enable both indexed lookups, add these builtin declarations to your config:
1127lookupCommit : Id -> Repo -> Commit
1128lookupContent : Path -> Snapshot -> Blob
1131Then replace the bodies of `commitAt` and `contentAt` with `lookupCommit` and
1132`lookupContent`, respectively, keeping their existing signatures.
1135contentAt ./a [({ path = ./a, content = blob "x" })]
1137contentAt ./missing [] => blob ""
1140**`touched : Change -> [Path]`** — the paths a change touches.
1142**`changed : Repo -> [Path]`** — paths whose content differs between the focus
1145**`unrecorded : Repo -> [Path]`** — paths changed in the working directory
1146since the recorded snapshot at the start of this invocation. This builtin
1147compares the loaded and snapshotted focus files, independently of edits to
1148the repository value later in the expression. It returns `[]` when no
1149snapshot context exists, including in a push expression.
1151**`diffs : Repo -> [{ path : Path, diff : Text }]`** — what the focus changes,
1152rendered by difftastic, one record per changed path, with unified diffs as
1153the fallback when difftastic is missing.
1155**`review : Repo -> Text`** — the same, as one page of text, or
1156`"no changes\n"` when the focus changes nothing. A `Text` result prints raw,
1157so `j review` is readable in the terminal even without difftastic. When
1158`difft` runs, whatever it prints, even nothing, is the page, and what it says
1159on stderr reaches the terminal. Other launch errors still fail the review.
1162j 'review' -- the focus's changes, as difftastic renders them
1165**`status : Repo -> { id : Id, message : Text, labels : [Label], changed : [Path], unrecorded : [Path], conflicts : [Path] }`** — a summary of the focus and unsaved file edits.
1170-- message add the lexer
1172-- in commit [src/lexer.rs]
1173-- conflicts No conflicts
1174-- unrecorded All edits recorded
1177Empty changed paths, labels, and descriptions read `No changes`, `No labels`,
1178and `No description` respectively. The display calls `changed` **in commit**;
1179the underlying field keeps its name. It compares the focus with its
1180parent. `unrecorded` lists paths that still need saving and reads
1181`All edits recorded` when empty. Run `j save` to record them; `changed`
1182continues to show the commit's changes after saving.
1184**`log : Repo -> [{ id : Id, message : Text, labels : [Label], focus : Bool }]`**
1185— a summary of every commit, top first, in preorder (`focus` marks the focused
1186commit). The display shows short ids and label names, fits columns to the
1187terminal, and switches to fields on separate lines when necessary. Redirected
1188output preserves full cell text.
1190**`tree : Repo -> Text`** and **`treeFull : Repo -> Text`** — the history as a
1191tree, built with `treeWith`. `tree` shows every commit (`detail = 1`);
1192`treeFull` shows ages and authors in the margin (`detail = 2`, `margin =
1193true`). Edit these definitions to change what `j tree` shows.
1196tree = treeWith ({ detail = 1, margin = false, elide = false, icons = false, color = "auto", lanes = 4, author = false, date = false, files = false })
1197treeFull = treeWith ({ detail = 2, margin = true, elide = false, icons = false, color = "auto", lanes = 4, author = false, date = false, files = false })
1200Use `treeCompact` to fold uninteresting runs and distant subtrees, or
1201`treeData` for extra date, files-changed, and author columns.
1207-- ▶ └─ ◌ lvpl add the lexer
1212## 11. Alphabetical index
1214| name | type | section |
1216| `(.)` | `(b -> c) -> (a -> b) -> a -> c` | §2 |
1217| `(==)` `(/=)` | `a -> a -> Bool` | §3 |
1218| `(&&)` `(\|\|)` `not` | booleans | §3 |
1219| `(+)` `(-)` `(*)` | `Int -> Int -> Int` | §3 |
1220| `(<)` `(<=)` `(>)` `(>=)` | `Int -> Int -> Bool` | §3 |
1221| `(++)` `concat` | lists & text | §4 |
1222| `(::)` | `a -> [a] -> [a]` | §4 |
1223| `abandon` | `Edit` | §8 |
1224| `addChild` | `Commit -> Edit` | §7.1 |
1225| `all` | `Revset` | §7.3 |
1226| `ancestors` | `Revset` | §7.3 |
1227| `ancestorsOf` | `Revset -> Revset` | §7.3 |
1228| `at` | `Revset -> Edit -> Edit` | §7.4 |
1229| `attach` | `Subtree -> Edit` | §7.1 |
1230| `backout` | `Revset -> Edit` | §8 |
1231| `blob` | `Text -> Blob` | §5 |
1232| `blobHex` | `Text -> Text -> Blob` | §5 |
1233| `both` | `Fileset -> Fileset -> Fileset` | §7.2 |
1234| `by` | `Id -> Repo -> Repo` | §5 |
1235| `changeOf` | `Repo -> Change` | §7.2 |
1236| `changed` | `Repo -> [Path]` | §10 |
1237| `child` | `(Commit -> Bool) -> Edit` | §7.4 |
1238| `clean` | `Repo -> Bool` | §7.4 |
1239| `commitAt` | `Id -> Repo -> Commit` | §7.4 |
1240| `commits` | `a -> [Commit]` | §6 |
1241| `concat` | `[m] -> m` | §4 |
1242| `conflicted` | `Snapshot -> [Path]` | §7.2 |
1243| `conflicts` | `Revset` | §7.3 |
1244| `const` | `a -> b -> a` | §2 |
1245| `contentAt` | `Path -> Snapshot -> Blob` | §10 |
1246| `contract` | `Fileset -> Edit` | §8 |
1247| `crash` | `Text -> a` | §2 |
1248| `describe` | `Text -> Edit` | §8 |
1249| `descendants` | `Revset` | §7.3 |
1250| `descendantsOf` | `Revset -> Revset` | §7.3 |
1251| `detach` | `Repo -> Detached` | §6 |
1252| `diff` | `Blob -> Blob -> Text` | §5 |
1253| `diffs` | `Repo -> [{path, diff}]` | §10 |
1254| `difft` | `Path -> Blob -> Blob -> Text` | §5 |
1255| `drop` | `Int -> [a] -> [a]` | §4 |
1256| `eachChild` | `Edit -> Edit` | §7.4 |
1257| `either` | `Fileset -> Fileset -> Fileset` | §7.2 |
1258| `endsWith` | `Text -> Text -> Bool` | §4 |
1259| `entryAt` | `Path -> Snapshot -> [Entry]` | §10 |
1260| `everything` | `Fileset` | §7.2 |
1261| `ext` | `Text -> Fileset` | §7.2 |
1262| `extract` | `Shape -> a -> [b]` | §5 |
1263| `files` | `Repo -> Snapshot` | §10 |
1264| `filter` | `(a -> Bool) -> [a] -> [a]` | §4 |
1265| `firstOf` | `[Revset] -> Revset` | §7.3 |
1266| `focus` | `Repo -> Commit` | §10 |
1267| `foldl` | `(b -> a -> b) -> b -> [a] -> b` | §4 |
1268| `forEach` | `Revset -> Edit -> Edit` | §7.4 |
1269| `from` | `Revset -> Revset -> Revset` | §7.3 |
1270| `goto` | `Revset -> Edit` | §7.4 |
1271| `guard` | `(Repo -> Bool) -> Edit` | §7.4 |
1272| `head` | `[a] -> a` | §4 |
1273| `here` | `Revset` | §7.3 |
1274| `id` | `a -> a` | §2 |
1275| `immutable` | `Revset` | §7.3 |
1276| `intersect` | `Revset -> Revset -> Revset` | §7.3 |
1277| `into` | `Id -> Edit` | §7.4 |
1278| `invert` | `Change -> Change` | §7.2 |
1279| `kids` | `Revset` | §7.3 |
1280| `label` | `Label -> Revset -> Repo -> [Push]` | §9 |
1281| `labelled` | `Label -> Revset` | §7.3 |
1282| `last` | `[a] -> a` | §4 |
1283| `length` | `[a] -> Int` | §4 |
1284| `log` | `Repo -> […]` | §10 |
1285| `map` | `(a -> b) -> [a] -> [b]` | §4 |
1286| `mapChildren` | `([Subtree] -> [Subtree]) -> Edit` | §7.1 |
1287| `mapRoot` | `(Commit -> Commit) -> Edit` | §7.1 |
1288| `matching` | `(Commit -> Bool) -> Revset -> Revset` | §7.3 |
1289| `member` | `a -> [a] -> Bool` | §4 |
1290| `meta` | `Id -> Meta` | §5 |
1291| `minus` | `Revset -> Revset -> Revset` | §7.3 |
1292| `neg` | `Fileset -> Fileset` | §7.2 |
1293| `new` | `Edit` | §8 |
1294| `newCommit` | `Snapshot -> Commit` | §7.1 |
1295| `next` | `Edit` | §7.4 |
1296| `not` | `Bool -> Bool` | §3 |
1297| `nth` | `Int -> [a] -> a` | §4 |
1298| `null` | `[a] -> Bool` | §4 |
1299| `parents` | `Revset` | §7.3 |
1300| `pick` | `Revset -> Edit` | §8 |
1301| `prev` | `Edit` | §7.4 |
1302| `preview` | `Edit -> Repo -> Text` | §2 |
1303| `range` | `Int -> Int -> [Int]` | §4 |
1304| `rebase` | `Revset -> Edit` | §8 |
1305| `relabel` | `Repo -> [Push]` | §9 |
1306| `remove` | `Edit` | §7.1 |
1307| `rename` | `Label -> Label -> Repo -> [b]` | §9 |
1308| `replay` | `Snapshot -> Change -> Snapshot` | §5 |
1309| `replayOnto` | `Commit -> Edit` | §8 |
1310| `replayTree` | `Rebase -> Subtree -> Subtree` | §8 |
1311| `restrict` | `Fileset -> Snapshot -> Snapshot` | §7.2 |
1312| `review` | `Repo -> Text` | §10 |
1313| `rewrite` | `(Commit -> Commit) -> Edit` | §8 |
1314| `save` | `Edit` | §2 |
1315| `select` | `Fileset -> Snapshot -> Snapshot -> Snapshot` | §7.2 |
1316| `show` | `a -> Text` | §3 |
1317| `siblings` | `Revset` | §7.3 |
1318| `split` | `Fileset -> Edit` | §8 |
1319| `splitOn` | `Text -> Text -> [Text]` | §4 |
1320| `squash` | `Edit` | §8 |
1321| `stack` | `Revset` | §7.3 |
1322| `startsWith` | `Text -> Text -> Bool` | §4 |
1323| `status` | `Repo -> {…}` | §10 |
1324| `subtreeCommits` | `a -> [Commit]` | §5 |
1325| `tail` | `[a] -> [a]` | §4 |
1326| `take` | `Int -> [a] -> [a]` | §4 |
1327| `text` | `Blob -> Text` | §5 |
1328| `tip` | `Edit` | §7.4 |
1329| `top` | `Edit` | §6 |
1330| `touched` | `Change -> [Path]` | §10 |
1331| `tree` | `Repo -> Text` | §10 |
1332| `treeFull` | `Repo -> Text` | §10 |
1333| `treeWith` | `{…} -> Repo -> Text` | §5 |
1334| `trunk` | `Revset` | §7.3 |
1335| `under` | `Path -> Fileset` | §7.2 |
1336| `union` | `Revset -> Revset -> Revset` | §7.3 |
1337| `unlabel` | `Label -> [Drop]` | §9 |
1338| `unrecorded` | `Repo -> [Path]` | §10 |
1339| `unresolved` | `Blob -> Bool` | §5 |
1340| `up` | `Edit` | §6 |
1341| `user` | `{ name : Text, email : Text }` | §1.1 |
1342| `validate` | `Repo -> Repo` | §5 |
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list