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).
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 "/"`.
59./src/lexer.rs => ["src" "lexer.rs"]
61splitOn "/" "a b/c" => ["a b" "c"]
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
70**`Snapshot = [Entry]`** — the files of one commit. Paths are unique. A
71deleted, resolved path simply has no entry (there is no "deletion" marker).
74[({ path = ./a.txt, content = blob "hello" })]
75 => a snapshot of one file, a.txt, containing "hello"
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:
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)
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
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
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
125These aliases are unfolded by the contract checker: `at : Revset -> Edit ->
126Edit` checks three arguments and a `Repo` result.
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).
139[({ id = @wqzt, name = "main" })] -- a Push: point main at @wqzt
140[({ delete = "old-feature" })] -- a Drop: delete the old-feature bookmark
145## 2. Functions and control flow
147These are the fundamental combinators. `or` is a keyword, not a builtin.
149### `(.)` — composition
152(.) : (b -> c) -> (a -> b) -> a -> c
155Right-to-left function composition: `(f . g) x = f (g x)`. Right-associative,
156binds very tightly (`infixr 9`). This is how edits are chained.
159((\x -> x + 1) . (\x -> x * 2)) 5
162describe "wip" . new -- start a new commit, then describe it
163tree . squash -- squash the focus, then render the tree
172The identity function. As an edit it does nothing, which makes it the
173universal fallback for `or`.
178edit or id -- run edit, keep the repository if it crashes
181### `const` — constant function
187`const a b = a` — ignores its second argument.
191map (const 0) [1 2 3] => [0 0 0]
192everything -- defined as const true
195### `crash` — abort with a message
201Aborts evaluation with the given message. Every interpreter-detected runtime
202error is also a crash, and all crashes are catchable by `or`.
205crash "something went wrong"
206 -- CRASH: something went wrong
208(crash "boom") or "recovered"
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`.
2201 or head [] => 1 -- head [] never runs
221edit or id -- try edit, fall back to id
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.
229## 3. Comparison, booleans, integers, `show`
231### Equality: `(==)` and `(/=)`
238Structural equality on ints, texts, bools, lists, and records; identity on
239`Id`; content-and-mode on `Blob`. `/=` is the negation. **Comparing two
245[1 2] == [1 2] => true
246{ a = 1 } == { a = 1 } => true
250### Booleans: `(&&)`, `(||)`, `not`
253(&&) : Bool -> Bool -> Bool -- short-circuit
254(||) : Bool -> Bool -> Bool -- short-circuit
258`&&` and `||` short-circuit (the right operand is not evaluated if the left
259decides the result). Operands must be `Bool`.
2621 == 1 && 2 == 2 => true
2631 == 2 || 3 == 3 => true
268### Integer arithmetic: `(+)`, `(-)`, `(*)`
271(+) : Int -> Int -> Int
272(-) : Int -> Int -> Int
273(*) : Int -> Int -> Int
276Arbitrary-precision: never overflows. There is no division builtin. No unary
277minus — write `0 - 5`.
282999999999999 * 999999999999
283 => 999999999998000000000001
287### Integer comparison: `(<)`, `(<=)`, `(>)`, `(>=)`
289Defined on `Int` only; crash on anything else. Non-associative (cannot chain:
290`1 < 2 < 3` is a parse error).
295(1 < 2) == true => true -- parenthesised, since < is non-associative
298### `show` — render a value as text
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.
312show [1 "a" true] => "[1 \"a\" true]"
313show ({ x = 1 }) => "{ x = 1 }"
314show (describe "wip") => "describe \"wip\""
315show (\x -> x + 1) => "\\x -> x + 1"
322### Basic list operations
325(::) : a -> [a] -> [a] -- cons
326map : (a -> b) -> [a] -> [b]
327filter : (a -> Bool) -> [a] -> [a]
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
343map (\x -> x * 2) [1 2 3] => [2 4 6]
344filter (\x -> x > 2) [1 2 3 4] => [3 4]
350nth 1 [10 20 30] => 20
351take 99 [1 2] => [1 2]
353member 3 [1 2 3] => true
354range 1 6 => [1 2 3 4 5]
356foldl (+) 0 [1 2 3 4 5] => 15
357foldl (\a -> \b -> a - b) 0 [1 2 3]
358 => (0 - 6) -- ((0 - 1) - 2) - 3
361`head`, `tail`, `last`, and out-of-range `nth` crash:
364head [] -- CRASH: head: empty list
365nth 5 [1 2 3] -- CRASH: nth: index 5 out of range
368### `(++)` and `concat` — overloaded on lists and text
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 []`.
380[1 2] ++ [3 4] => [1 2 3 4]
381"foo" ++ "bar" => "foobar"
382concat [[1 2] [3] []] => [1 2 3]
383concat ["foo" "bar"] => "foobar"
387### Text predicates and splitting
390startsWith : Text -> Text -> Bool
391endsWith : Text -> Text -> Bool
392splitOn : Text -> Text -> [Text]
396startsWith "he" "hello" => true
397endsWith "rs" "lexer.rs" => true
398splitOn "," "a,b,c" => ["a" "b" "c"]
399splitOn "/" "src/lexer.rs" => ["src" "lexer.rs"]
400splitOn "/" "" => [""]
403`splitOn` with an empty separator crashes.
407## 5. The repository builtins
409These are the builtins that touch the repository, blobs, and diffs.
411### `replay` — apply a change to a snapshot
414replay : Snapshot -> Change -> Snapshot
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.
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)
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
447unresolved : Blob -> Bool
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.
457blob "content" => blob "content"
458text (blob "content") => "content"
459unresolved (blob "x") => false
462### `by` — focus a commit by id
465by : Id -> Repo -> Repo
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`.
473by @wqzt -- an edit: refocus on the commit @wqzt
476### `subtreeCommits` — every commit of a tree
479subtreeCommits : a -> [Commit]
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
490subtreeCommits ({ root = 1, children = [({ root = 2, children = [] })] })
494### `meta` — commit metadata
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).
505meta @wqzt => { hash = "74bee64e…", author = "Test User",
506 email = "test@example.com", time = 1789457265 }
509### `validate` — dry-run persistence
512validate : Repo -> Repo
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
522diff : Blob -> Blob -> Text
523difft : Path -> Blob -> Blob -> Text
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.
530diff (blob "a\nb\n") (blob "a\nc\n")
531 => "@@ -1,2 +1,2 @@\n a\n-b\n+c\n"
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
544treeWith : { detail : Int, margin : Bool, elide : Bool, icons : Bool, color : Text }
548Renders the history as a tree with an options record. The reference config
549provides two ready-made settings, `tree` and `treeFull` (§9).
552treeWith ({ detail = 2, margin = true, elide = false, icons = false, color = "auto" })
553 -- a full tree with ages and authors in the margin
556### `extract` — every subvalue of a shape
559extract : Shape -> a -> [b]
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.
568extract Int ({ a = 1, b = 2 })
570extract Int ({ a = 1, b = [2 "x" ({ c = 3 })] })
572extract Commit repo -- every commit in the repository
573extract Push ys -- every Push record in a list, however nested
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
584### `up` — to the parent
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.
596up -- refocus on the parent (crash if already at the top)
599### `detach` — remove the focused subtree
602detach : Repo -> Detached
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
613top = \repo -> top (up repo) or repo
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.
620top -- refocus at the very first commit
621commits . top -- every commit, preorder
624### `commits` — every commit of a (sub)tree
627commits : a -> [Commit]
628commits = subtreeCommits
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.
640length . commits . top -- how many commits are visible?
641commits . top -- every commit, preorder from the top
646## 7. Structure and focusing
648### 7.1 Editing at the focus
650**`mapRoot : (Commit -> Commit) -> Edit`** — apply a function to the focused
654mapRoot = \f repo -> repo { root = f repo.root }
657`describe` is `mapRoot` with a message setter (§8.1).
659**`mapChildren : ([Subtree] -> [Subtree]) -> Edit`** — apply a function to the
662**`attach : Subtree -> Edit`** — attach a subtree as a new child of the focus
663(the focus does not move).
666attach = \t -> mapChildren (++ [t])
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
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.
680newCommit = \files -> { files = files, message = "", labels = [], id = @ }
683### 7.2 Snapshots, changes, and filesets
685**`restrict : Fileset -> Snapshot -> Snapshot`** — the entries matching a
689restrict (ext "rs") snap -- only the .rs files in snap
692**`select : Fileset -> Snapshot -> Snapshot -> Snapshot`** — the entries
693matching `m` from the first snapshot, the rest from the second.
696select = \m a b -> restrict m a ++ restrict (neg m) b
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`.
708invert ({ from = [], to = [({ path = ./a, content = blob "1" })] })
709 => { from = [({ content = blob "1", path = ["a"] })], to = [] }
712**Fileset combinators** — filesets are functions `Path -> Bool`, composed
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
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
731either (ext "j") (ext "rs") ./x.rs
733neg (ext "rs") ./x.j => true
734everything ./anything/at/all => true
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`,
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)
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)
763**Revset combinators:**
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
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
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`.
803 in if length is == 1 then by (head is) repo
804 else crash ("expected one revision, got " ++ show (length is))
808goto %main -- refocus on main (crash if it names 0 or 2+ commits)
809goto parents -- == prev
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
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).
835at %main (describe "released")
836 -- describe main, then return the focus to where it was
839**`forEach : Revset -> Edit -> Edit`** — run an edit at every commit of a
840revset, returning to the focus.
843forEach descendants (describe "wip")
844 -- describe every commit below the focus "wip"
847**`eachChild : Edit -> Edit`** — `forEach kids`: run an edit at every child.
849**`guard : (Repo -> Bool) -> Edit`** — crash unless a predicate holds.
852guard clean -- refuse to continue if the focus has conflicts
855**`clean : Repo -> Bool`** — `true` iff the focus has no unresolved files.
861These are the everyday edits. Each is an `Edit`, applied to the repository on
864### `describe` — set the message
867describe : Text -> Edit
868describe = \m -> mapRoot (\c -> c { message = m })
871Sets the message of the focused commit.
874j 'describe "add the lexer"'
877### `new` — start a new commit
881new = \repo -> addChild (newCommit repo.root.files) repo
884Starts an empty child of the focus (with the focus's files) and moves to it.
888j 'describe "wip" . new' -- new commit, then describe it
891### `abandon` — drop a commit
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,
903j 'abandon' -- drop the focused commit
906### `contract` — push files down into the parent
909contract : Fileset -> Edit
912Pushes the files matching `m` from the focus down into its parent.
915j 'contract (ext "rs")' -- move the .rs changes into the parent commit
918### `squash` — fold into the parent
922squash = abandon . contract everything
925Folds the focus entirely into its parent; a new empty commit becomes the
926focus. Defined as `contract everything` followed by `abandon`.
932### `split` — push files up into a new child
935split : Fileset -> Edit
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`.
943j 'split (\p -> p == ./a.txt)' -- split off just a.txt into a child commit
946### `rebase` — move a subtree
949rebase : Revset -> Edit
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
957j 'rebase %main' -- move the current stack onto main
960### `pick` — copy a change elsewhere
966Applies the focused commit's change onto another commit, as a new child there.
969j 'pick %main' -- copy the focused change onto main
972### `backout` — undo a change
975backout : Revset -> Edit
978Creates a new child of the focus that undoes the change of the given commit
979(by replaying its inverse).
982j 'backout @wqzt' -- a new commit undoing @wqzt's change
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).
995**`rewrite : (Commit -> Commit) -> Edit`** — replace the focused commit with
996`f` applied to it, replaying its children so they follow.
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
1009j 'push (label "feature" here)' -- label the focused commit "feature"
1012**`unlabel : Label -> [Drop]`** — remove a label on the remote.
1015j 'push (unlabel "feature")' -- delete the "feature" bookmark
1018**`rename : Label -> Label -> Repo -> [b]`** — move a label.
1021j 'push (rename "old" "new")' -- rename the bookmark "old" to "new"
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.
1029j 'push relabel' -- after rewriting, re-point the remote's bookmarks
1036These return printable values rather than a `Repo`.
1038**`focus : Repo -> Commit`** — the focused commit.
1040**`files : Repo -> Snapshot`** — the focus's files.
1043j 'files' -- the working-copy commit's files
1046**`entryAt : Path -> Snapshot -> [Entry]`** — the entry at a path, as a list
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:
1059lookupCommit : Id -> Repo -> Commit
1060lookupContent : Path -> Snapshot -> Blob
1063Then replace the bodies of `commitAt` and `contentAt` with `lookupCommit` and
1064`lookupContent`, respectively, keeping their existing signatures.
1067contentAt ./a [({ path = ./a, content = blob "x" })]
1069contentAt ./missing [] => blob ""
1072**`touched : Change -> [Path]`** — the paths a change touches.
1074**`changed : Repo -> [Path]`** — paths whose content differs between the focus
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.
1088j 'review' -- the focus's changes, as difftastic renders them
1091**`status : Repo -> { id : Id, message : Text, labels : [Label], changed : [Path], conflicts : [Path] }`** — a summary of the focus.
1096-- message add the lexer
1098-- changed [src/lexer.rs]
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
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.
1112tree = treeWith ({ detail = 1, margin = false, elide = true, icons = false, color = "auto" })
1113treeFull = treeWith ({ detail = 2, margin = true, elide = false, icons = false, color = "auto" })
1120-- ▶ └─ ◌ lvpl add the lexer
1125## 11. Alphabetical index
1127| name | type | section |
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 |
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