···11+22+# Templates
33+44+Jujutsu supports a functional language to customize output of commands.
55+The language consists of literals, keywords, operators, functions, and
66+methods.
77+88+A couple of `jj` commands accept a template via `-T`/`--template` option.
99+1010+## Keywords
1111+1212+Keywords represent objects of different types; the types are described in
1313+a follow-up section. In addition to context-specific keywords, the top-level
1414+object can be referenced as `self`.
1515+1616+### Commit keywords
1717+1818+In `jj log` templates, all 0-argument methods of [the `Commit`
1919+type](#commit-type) are available as keywords. For example, `commit_id` is
2020+equivalent to `self.commit_id()`.
2121+2222+### Operation keywords
2323+2424+In `jj op log` templates, all 0-argument methods of [the `Operation`
2525+type](#operation-type) are available as keywords. For example,
2626+`current_operation` is equivalent to `self.current_operation()`.
2727+2828+## Operators
2929+3030+The following operators are supported.
3131+3232+* `x.f()`: Method call.
3333+* `-x`: Negate integer value.
3434+* `!x`: Logical not.
3535+* `x * y`, `x / y`, `x % y`: Multiplication/division/remainder. Operands must
3636+ be `Integer`s.
3737+* `x + y`, `x - y`: Addition/subtraction. Operands must be `Integer`s.
3838+* `x >= y`, `x > y`, `x <= y`, `x < y`: Greater than or equal/greater than/
3939+ lesser than or equal/lesser than. Operands must be `Integer`s.
4040+* `x == y`, `x != y`: Equal/not equal. Operands must be either `Boolean`,
4141+ `Integer`, or `String`.
4242+* `x && y`: Logical and, short-circuiting.
4343+* `x || y`: Logical or, short-circuiting.
4444+* `x ++ y`: Concatenate `x` and `y` templates.
4545+4646+(listed in order of binding strengths)
4747+4848+## Global functions
4949+5050+The following functions are defined.
5151+5252+* `fill(width: Integer, content: Template) -> Template`: Fill lines at
5353+ the given `width`.
5454+* `indent(prefix: Template, content: Template) -> Template`: Indent
5555+ non-empty lines by the given `prefix`.
5656+* `pad_start(width: Integer, content: Template, [fill_char: Template])`: Pad (or
5757+ right-justify) content by adding leading fill characters. The `content`
5858+ shouldn't have newline character.
5959+* `pad_end(width: Integer, content: Template, [fill_char: Template])`: Pad (or
6060+ left-justify) content by adding trailing fill characters. The `content`
6161+ shouldn't have newline character.
6262+* `pad_centered(width: Integer, content: Template, [fill_char: Template])`: Pad
6363+ content by adding both leading and trailing fill characters. If an odd number
6464+ of fill characters are needed, the trailing fill will be one longer than the
6565+ leading fill. The `content` shouldn't have newline characters.
6666+* `truncate_start(width: Integer, content: Template, [ellipsis: Template])`:
6767+ Truncate `content` by removing leading characters. The `content` shouldn't
6868+ have newline character. If `ellipsis` is provided and `content` was truncated,
6969+ prepend the `ellipsis` to the result.
7070+* `truncate_end(width: Integer, content: Template, [ellipsis: Template])`:
7171+ Truncate `content` by removing trailing characters. The `content` shouldn't
7272+ have newline character. If `ellipsis` is provided and `content` was truncated,
7373+ append the `ellipsis` to the result.
7474+* `hash(content: Stringify) -> String`:
7575+ Hash the input and return a hexadecimal string representation of the digest.
7676+* `label(label: Stringify, content: Template) -> Template`: Apply a custom
7777+ [color label](#color-labels) to the content. The `label` is evaluated as a
7878+ space-separated string.
7979+* `raw_escape_sequence(content: Template) -> Template`: Preserves any escape
8080+ sequences in `content` (i.e., bypasses sanitization) and strips labels.
8181+ Note: This function is intended for escape sequences and as such, its output
8282+ is expected to be invisible / of no display width. Outputting content with
8383+ nonzero display width may break wrapping, indentation etc.
8484+* `stringify(content: Stringify) -> String`: Format `content` to string. This
8585+ effectively removes color labels.
8686+* `json(value: Serialize) -> String`: Serialize `value` in JSON format.
8787+* `if(condition: Boolean, then: Template, [else: Template]) -> Template`:
8888+ Conditionally evaluate `then`/`else` template content.
8989+* `coalesce(content: Template...) -> Template`: Returns the first **non-empty**
9090+ content.
9191+* `concat(content: Template...) -> Template`:
9292+ Same as `content_1 ++ ... ++ content_n`.
9393+* `join(separator: Template, content: Template...) -> Template`: Insert
9494+ `separator` between `content`s.
9595+* `separate(separator: Template, content: Template...) -> Template`: Insert
9696+ `separator` between **non-empty** `content`s.
9797+* `surround(prefix: Template, suffix: Template, content: Template) -> Template`:
9898+ Surround **non-empty** content with texts such as parentheses.
9999+* `config(name: StringLiteral) -> ConfigValue`: Look up configuration value by `name`.
100100+101101+## Built-in Aliases
102102+103103+* `hyperlink(url, text)`: Creates a clickable hyperlink using [OSC8 escape sequences](https://github.com/Alhadis/OSC8-Adoption).
104104+ The `text` will be displayed and clickable, linking to the given `url` in
105105+ terminals that support OSC8 hyperlinks.
106106+107107+## Types
108108+109109+### `AnnotationLine` type
110110+111111+_Conversion: `Boolean`: no, `Serialize`: no, `Template`: no_
112112+113113+The following methods are defined.
114114+115115+* `.commit() -> Commit`: Commit responsible for changing the relevant line.
116116+* `.content() -> Template`: Line content including newline character.
117117+* `.line_number() -> Integer`: 1-based line number.
118118+* `.original_line_number() -> Integer`: 1-based line number in the original commit.
119119+* `.first_line_in_hunk() -> Boolean`: False when the directly preceding line
120120+ references the same commit.
121121+122122+### `Boolean` type
123123+124124+_Conversion: `Boolean`: yes, `Serialize`: yes, `Template`: yes_
125125+126126+No methods are defined. Can be constructed with `false` or `true` literal.
127127+128128+### `Commit` type
129129+130130+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: no_
131131+132132+This type cannot be printed. The following methods are defined.
133133+134134+* `.description() -> String`
135135+* `.trailers() -> List<Trailer>`: The trailers at the end of the commit
136136+ description that are formatted as `<key>: <value>`. These are returned in the
137137+ same order as they appear in the description, and there may be multiple
138138+ `Trailer`s with the same key.
139139+* `.change_id() -> ChangeId`
140140+* `.commit_id() -> CommitId`
141141+* `.parents() -> List<Commit>`
142142+* `.author() -> Signature`
143143+* `.committer() -> Signature`
144144+* `.signature() -> Option<CryptographicSignature>`: Cryptographic signature if
145145+ the commit was signed.
146146+* `.mine() -> Boolean`: Commits where the author's email matches the email of
147147+ the current user.
148148+* `.working_copies() -> List<WorkspaceRef>`: For multi-workspace repositories,
149149+ returns a list of workspace references for each workspace whose working-copy
150150+ commit matches the current commit.
151151+* `.current_working_copy() -> Boolean`: True for the working-copy commit of the
152152+ current workspace.
153153+* `.bookmarks() -> List<CommitRef>`: Local and remote bookmarks pointing to the
154154+ commit. A tracked remote bookmark will be included only if its target is
155155+ different from the local one.
156156+* `.local_bookmarks() -> List<CommitRef>`: All local bookmarks pointing to the
157157+ commit.
158158+* `.remote_bookmarks() -> List<CommitRef>`: All remote bookmarks pointing to the
159159+ commit.
160160+* `.tags() -> List<CommitRef>`: Local and remote tags pointing to the commit. A
161161+ tracked remote tag will be included only if its target is different from the
162162+ local one.
163163+* `.local_tags() -> List<CommitRef>`: All local tags pointing to the commit.
164164+* `.remote_tags() -> List<CommitRef>`: All remote tags pointing to the commit.
165165+* `.divergent() -> Boolean`: True if the commit's change ID corresponds to multiple
166166+ visible commits.
167167+* `.hidden() -> Boolean`: True if the commit is not visible (a.k.a. abandoned).
168168+* `.change_offset() -> Option<Integer>`: The [change offset](glossary.md#change-offset)
169169+ of this commit. May not be available for some commits.
170170+* `.immutable() -> Boolean`: True if the commit is included in [the set of
171171+ immutable commits](config.md#set-of-immutable-commits).
172172+* `.contained_in(revset: StringLiteral) -> Boolean`: True if the commit is included in
173173+ [the provided revset](revsets.md).
174174+* `.conflict() -> Boolean`: True if the commit contains merge conflicts.
175175+* `.empty() -> Boolean`: True if the commit modifies no files.
176176+* `.diff([files: StringLiteral]) -> TreeDiff`: Changes from the parents within [the
177177+ `files` expression](filesets.md). All files are compared by default, but it is
178178+ likely to change in future version to respect the command line path arguments.
179179+* `.files([files: StringLiteral]) -> List<TreeEntry>`: Files that exist in this commit,
180180+ matching [the `files` expression](filesets.md). Use `.diff().files()` to list
181181+ changed files.
182182+* `.conflicted_files() -> List<TreeEntry>`: Conflicted files in this commit.
183183+* `.root() -> Boolean`: True if the commit is the root commit.
184184+185185+### `CommitEvolutionEntry` type
186186+187187+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: no_
188188+189189+This type cannot be printed. The following methods are defined.
190190+191191+* `.commit() -> Commit`: New commit.
192192+* `.operation() -> Operation`: Operation where the commit was created or
193193+ rewritten.
194194+* `.predecessors() -> List<Commit>`: Predecessor commits of this entry.
195195+* `.inter_diff([files: StringLiteral]) -> TreeDiff`: Changes between this commit and its
196196+ predecessor version(s), rebased onto the parents of this commit to avoid unrelated
197197+ changes (similar to `jj evolog -p`).
198198+199199+### `ChangeId` type
200200+201201+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_
202202+203203+The following methods are defined.
204204+205205+* `.normal_hex() -> String`: Normal hex representation (0-9a-f) instead of the
206206+ canonical "reversed" (z-k) representation.
207207+* `.short([len: Integer]) -> String`
208208+* `.shortest([min_len: Integer]) -> ShortestIdPrefix`: Shortest unique prefix.
209209+210210+### `CommitId` type
211211+212212+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_
213213+214214+The following methods are defined.
215215+216216+* `.short([len: Integer]) -> String`
217217+* `.shortest([min_len: Integer]) -> ShortestIdPrefix`: Shortest unique prefix.
218218+219219+### `CommitRef` type
220220+221221+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_
222222+223223+The following methods are defined.
224224+225225+* `.name() -> RefSymbol`: Local bookmark or tag name.
226226+* `.remote() -> Option<RefSymbol>`: Remote name if this is a remote ref.
227227+* `.present() -> Boolean`: True if the ref points to any commit.
228228+* `.conflict() -> Boolean`: True if [the bookmark or tag is
229229+ conflicted](bookmarks.md#conflicts).
230230+* `.normal_target() -> Option<Commit>`: Target commit if the ref is not
231231+ conflicted and points to a commit.
232232+* `.removed_targets() -> List<Commit>`: Old target commits if conflicted.
233233+* `.added_targets() -> List<Commit>`: New target commits. The list usually
234234+ contains one "normal" target.
235235+* `.tracked() -> Boolean`: True if the ref is tracked by a local ref. The local
236236+ ref might have been deleted (but not pushed yet.)
237237+* `.tracking_present() -> Boolean`: True if the ref is tracked by a local ref,
238238+ and if the local ref points to any commit.
239239+* `.tracking_ahead_count() -> SizeHint`: Number of commits ahead of the tracking
240240+ local ref.
241241+* `.tracking_behind_count() -> SizeHint`: Number of commits behind of the
242242+ tracking local ref.
243243+* `.synced() -> Boolean`: For a local bookmark, true if synced with all tracked
244244+ remotes. For a remote bookmark, true if synced with the tracking local
245245+ bookmark.
246246+247247+### `ConfigValue` type
248248+249249+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_
250250+251251+This type can be printed in TOML syntax. The following methods are defined.
252252+253253+* `.as_boolean() -> Boolean`: Extract boolean.
254254+* `.as_integer() -> Integer`: Extract integer.
255255+* `.as_string() -> String`: Extract string. This does not convert non-string
256256+ value (e.g. integer) to string.
257257+* `.as_string_list() -> List<String>`: Extract list of strings.
258258+259259+### `CryptographicSignature` type
260260+261261+_Conversion: `Boolean`: no, `Serialize`: no, `Template`: no_
262262+263263+The following methods are defined.
264264+265265+* `.status() -> String`: The signature's status (`"good"`, `"bad"`, `"unknown"`,
266266+ `"invalid"`).
267267+* `.key() -> String`: The signature's key id representation (for GPG and SSH,
268268+ this is the public key fingerprint).
269269+* `.display() -> String`: The signature's display string (for GPG, this is the
270270+ formatted primary user ID; for SSH, this is the principal).
271271+272272+!!! warning
273273+274274+ Calling any of `.status()`, `.key()`, or `.display()` is slow, as it incurs
275275+ the performance cost of verifying the signature (for example shelling out
276276+ to `gpg` or `ssh-keygen`). Though consecutive calls will be faster, because
277277+ the backend caches the verification result.
278278+279279+!!! info
280280+281281+ As opposed to calling any of `.status()`, `.key()`, or `.display()`,
282282+ checking for signature presence through boolean coercion is fast:
283283+ ```
284284+ if(commit.signature(), "commit has a signature", "commit is unsigned")
285285+ ```
286286+287287+### `DiffStats` type
288288+289289+_Conversion: `Boolean`: no, `Serialize`: no, `Template`: yes_
290290+291291+This type can be printed as a histogram of the changes. The following methods
292292+are defined.
293293+294294+* `.files() -> List<DiffStatEntry>`: Per-file stats for changed files.
295295+* `.total_added() -> Integer`: Total number of insertions.
296296+* `.total_removed() -> Integer`: Total number of deletions.
297297+298298+### `DiffStatEntry` type
299299+300300+_Conversion: `Boolean`: no, `Serialize`: no, `Template`: no_
301301+302302+This type holds the diff stats per file. The following methods are defined.
303303+304304+* `.bytes_delta() -> Integer`: The difference in size of the file, in bytes.
305305+* `.lines_added() -> Integer`: Number of lines added.
306306+* `.lines_removed() -> Integer`: Number of lines deleted.
307307+* `.path() -> RepoPath`: Path to the entry. If the entry is a copy/rename, this
308308+ points to the target (or right) entry.
309309+* `.display_diff_path() -> String`: Format path for display, taking into account copy/rename information.
310310+* `.status() -> String`: One of `"modified"`, `"added"`, `"removed"`, `"copied"`, or `"renamed"`.
311311+* `.status_char() -> String`: One of `"M"` (modified), `"A"` (added), `"D"` (removed),
312312+ `"C"` (copied), or `"R"` (renamed).
313313+314314+### `Email` type
315315+316316+_Conversion: `Boolean`: yes, `Serialize`: yes, `Template`: yes_
317317+318318+The email field of a signature may or may not look like an email address. It may
319319+be empty, may not contain the symbol `@`, and could in principle contain
320320+multiple `@`s.
321321+322322+The following methods are defined.
323323+324324+* `.local() -> String`: the part of the email before the first `@`, usually the
325325+ username.
326326+* `.domain() -> String`: the part of the email after the first `@` or the empty
327327+ string.
328328+329329+### `Integer` type
330330+331331+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_
332332+333333+No methods are defined.
334334+335335+### `List` type
336336+337337+_Conversion: `Boolean`: yes, `Serialize`: maybe, `Template`: maybe_
338338+339339+A list can be implicitly converted to `Boolean`. The following methods are
340340+defined.
341341+342342+* `.len() -> Integer`: Number of elements in the list.
343343+* `.join(separator: Template) -> Template`: Concatenate elements with
344344+ the given `separator`.
345345+* `.filter(|item| expression) -> List`: Filter list elements by predicate
346346+ `expression`. Example: `description.lines().filter(|s| s.contains("#"))`
347347+* `.map(|item| expression) -> ListTemplate`: Apply template `expression`
348348+ to each element. Example: `parents.map(|c| c.commit_id().short())`
349349+* `.any(|item| expression) -> Boolean`: Returns true if any element satisfies
350350+ the predicate `expression`. Example: `parents.any(|c| c.description().contains("fix"))`
351351+* `.all(|item| expression) -> Boolean`: Returns true if all elements satisfy
352352+ the predicate `expression`. Example: `parents.all(|c| c.mine())`
353353+354354+### `List<Trailer>` type
355355+356356+The following methods are defined. See also the `List` type.
357357+358358+* `.contains_key(key: Stringify) -> Boolean`: True if the commit description
359359+ contains at least one trailer with the key `key`.
360360+361361+### `ListTemplate` type
362362+363363+_Conversion: `Boolean`: no, `Serialize`: no, `Template`: yes_
364364+365365+The following methods are defined.
366366+367367+* `.join(separator: Template) -> Template`: Concatenate elements with
368368+ the given `separator`.
369369+370370+### `Operation` type
371371+372372+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: no_
373373+374374+This type cannot be printed. The following methods are defined.
375375+376376+* `.current_operation() -> Boolean`
377377+* `.description() -> String`
378378+* `.id() -> OperationId`
379379+* `.tags() -> String`
380380+* `.time() -> TimestampRange`
381381+* `.user() -> String`
382382+* `.snapshot() -> Boolean`: True if the operation is a snapshot operation.
383383+* `.root() -> Boolean`: True if the operation is the root operation.
384384+* `.parents() -> List<Operation>`
385385+386386+### `OperationId` type
387387+388388+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_
389389+390390+The following methods are defined.
391391+392392+* `.short([len: Integer]) -> String`
393393+394394+### `Option` type
395395+396396+_Conversion: `Boolean`: yes, `Serialize`: maybe, `Template`: maybe_
397397+398398+An option can be implicitly converted to `Boolean` denoting whether the
399399+contained value is set. If set, all methods of the contained value can be
400400+invoked. If not set, an error will be reported inline on method call.
401401+402402+On comparison between two optional values or optional and non-optional values,
403403+unset value is not an error. Unset value is considered less than any set values.
404404+405405+### `RefSymbol` type
406406+407407+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_
408408+409409+[A `String` type](#string-type), but is formatted as revset symbol by quoting
410410+and escaping if necessary. Unlike strings, this cannot be implicitly converted
411411+to `Boolean`.
412412+413413+### `RepoPath` type
414414+415415+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_
416416+417417+A slash-separated path relative to the repository root. The following methods
418418+are defined.
419419+420420+* `.absolute() -> String`: Format as absolute path using platform-native
421421+ separator.
422422+* `.display() -> String`: Format path for display. The formatted path uses
423423+ platform-native separator, and is relative to the current working directory.
424424+* `.parent() -> Option<RepoPath>`: Parent directory path.
425425+426426+### `Serialize` type
427427+428428+An expression that can be serialized in machine-readable format such as JSON.
429429+430430+!!! note
431431+432432+ Field names and value types in the serialized output are usually stable
433433+ across jj versions, but the backward compatibility isn't guaranteed. If the
434434+ underlying data model is updated, the serialized output may change.
435435+436436+### `ShortestIdPrefix` type
437437+438438+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_
439439+440440+The following methods are defined.
441441+442442+* `.prefix() -> String`
443443+* `.rest() -> String`
444444+* `.upper() -> ShortestIdPrefix`
445445+* `.lower() -> ShortestIdPrefix`
446446+447447+### `Signature` type
448448+449449+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_
450450+451451+The following methods are defined.
452452+453453+* `.name() -> String`
454454+* `.email() -> Email`
455455+* `.timestamp() -> Timestamp`
456456+457457+### `SizeHint` type
458458+459459+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: no_
460460+461461+This type cannot be printed. The following methods are defined.
462462+463463+* `.lower() -> Integer`: Lower bound.
464464+* `.upper() -> Option<Integer>`: Upper bound if known.
465465+* `.exact() -> Option<Integer>`: Exact value if upper bound is known and it
466466+ equals to the lower bound.
467467+* `.zero() -> Boolean`: True if upper bound is known and is `0`. Equivalent to
468468+ `.upper() == 0`.
469469+470470+### `String` type
471471+472472+_Conversion: `Boolean`: yes, `Serialize`: yes, `Template`: yes_
473473+474474+A string can be implicitly converted to `Boolean`. The following methods are
475475+defined.
476476+477477+* `.len() -> Integer`: Length in UTF-8 bytes.
478478+* `.contains(needle: Stringify) -> Boolean`: Whether the string contains the
479479+ provided stringifiable value as a substring.
480480+* `.match(needle: StringPattern) -> String`: Extracts
481481+ the first matching part of the string for the given pattern.
482482+483483+ An empty string is returned if there is no match.
484484+* `.replace(pattern: StringPattern, replacement: Stringify, [limit: Integer]) -> String`:
485485+ Replace occurrences of the given `pattern` with the `replacement` string.
486486+487487+ By default, all occurrences are replaced. If `limit` is specified, at most
488488+ that many occurrences are replaced.
489489+490490+ Supports capture groups in patterns using `$0` (entire match), `$1`, `$2` etc.
491491+* `.first_line() -> String`
492492+* `.lines() -> List<String>`: Split into lines excluding newline characters.
493493+* `.split(separator: StringPattern, [limit: Integer]) -> List<String>`: Split into
494494+ substrings by the given `separator` pattern. If `limit` is specified, it
495495+ determines the maximum number of elements in the result, with the remainder
496496+ of the string returned as the final element. A `limit` of 0 returns an empty list.
497497+* `.upper() -> String`
498498+* `.lower() -> String`
499499+* `.starts_with(needle: Stringify) -> Boolean`
500500+* `.ends_with(needle: Stringify) -> Boolean`
501501+* `.remove_prefix(needle: Stringify) -> String`: Removes the passed prefix, if
502502+ present.
503503+* `.remove_suffix(needle: Stringify) -> String`: Removes the passed suffix, if
504504+ present.
505505+* `.trim() -> String`: Removes leading and trailing whitespace
506506+* `.trim_start() -> String`: Removes leading whitespace
507507+* `.trim_end() -> String`: Removes trailing whitespace
508508+* `.substr(start: Integer, end: Integer) -> String`: Extract substring. The
509509+ `start`/`end` indices should be specified in UTF-8 bytes. Indices are 0-based
510510+ and `end` is exclusive. Negative values count from the end of the string,
511511+ with `-1` being the last byte. If the `start` index is in the middle of a UTF-8
512512+ codepoint, the codepoint is fully part of the result. If the `end` index is in
513513+ the middle of a UTF-8 codepoint, the codepoint is not part of the result.
514514+* `.escape_json() -> String`: Serializes the string in JSON format. This
515515+ function is useful for making machine-readable templates. For example, you
516516+ can use it in a template like `'{ "foo": ' ++ foo.escape_json() ++ ' }'` to
517517+ return a JSON/JSONL.
518518+519519+### `StringLiteral` type
520520+521521+A string literal known at parse time. Unlike `Stringify`, this cannot be a
522522+dynamic expression - it must be a literal value like `"main"` or `"format"`.
523523+524524+String literals must be surrounded by single or double quotes (`'` or `"`).
525525+A double-quoted string literal supports the following escape sequences:
526526+527527+* `\"`: double quote
528528+* `\\`: backslash
529529+* `\t`: horizontal tab
530530+* `\r`: carriage return
531531+* `\n`: new line
532532+* `\0`: null
533533+* `\e`: escape (i.e., `\x1b`)
534534+* `\xHH`: byte with hex value `HH`
535535+536536+Other escape sequences are not supported. Any UTF-8 characters are allowed
537537+inside a string literal, with two exceptions: unescaped `"`-s and uses of `\`
538538+that don't form a valid escape sequence.
539539+540540+A single-quoted string literal has no escape syntax. `'` can't be expressed
541541+inside a single-quoted string literal.
542542+543543+String literals have their own type so that the value can be validated at parse
544544+time. For example, `contained_in(revset)` requires a literal so the revset can
545545+be parsed and checked before the template is evaluated.
546546+547547+### `Stringify` type
548548+549549+An expression that can be converted to a `String`.
550550+551551+Any types that can be converted to `Template` can also be `Stringify`. Unlike
552552+`Template`, color labels are stripped.
553553+554554+### `StringPattern` type
555555+556556+_Conversion: `Boolean`: no, `Serialize`: no, `Template`: no_
557557+558558+These are the exact same as the [String pattern type] in revsets, except that
559559+quotes are mandatory.
560560+561561+Literal strings may be used, which are interpreted as case-sensitive substring
562562+matching.
563563+564564+Currently `StringPattern` values cannot be passed around as values and may
565565+only occur directly in the call site they are used in.
566566+567567+[String pattern type]: revsets.md#string-patterns
568568+569569+### `Template` type
570570+571571+_Conversion: `Boolean`: no, `Serialize`: no, `Template`: yes_
572572+573573+Most types can be implicitly converted to `Template`. No methods are defined.
574574+575575+### `Timestamp` type
576576+577577+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_
578578+579579+The following methods are defined.
580580+581581+* `.ago() -> String`: Format as relative timestamp.
582582+* `.format(format: StringLiteral) -> String`: Format with [the specified strftime-like
583583+ format string](https://docs.rs/chrono/latest/chrono/format/strftime/).
584584+* `.utc() -> Timestamp`: Convert timestamp into UTC timezone.
585585+* `.local() -> Timestamp`: Convert timestamp into local timezone.
586586+* `.after(date: StringLiteral) -> Boolean`: True if the timestamp is exactly at or
587587+ after the given date. Supported date formats are the same as the revset
588588+ [Date pattern type].
589589+* `.before(date: StringLiteral) -> Boolean`: True if the timestamp is before, but
590590+ not including, the given date. Supported date formats are the same as the
591591+ revset [Date pattern type].
592592+593593+[Date pattern type]: revsets.md#date-patterns
594594+595595+### `TimestampRange` type
596596+597597+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_
598598+599599+The following methods are defined.
600600+601601+* `.start() -> Timestamp`
602602+* `.end() -> Timestamp`
603603+* `.duration() -> String`
604604+605605+### `Trailer` type
606606+607607+_Conversion: `Boolean`: no, `Serialize`: no, `Template`: yes_
608608+609609+The following methods are defined.
610610+611611+* `.key() -> String`
612612+* `.value() -> String`
613613+614614+### `TreeDiff` type
615615+616616+_Conversion: `Boolean`: no, `Serialize`: no, `Template`: no_
617617+618618+This type cannot be printed. The following methods are defined.
619619+620620+* `.files() -> List<TreeDiffEntry>`: Changed files.
621621+* `.color_words([context: Integer]) -> Template`: Format as a word-level diff
622622+ with changes indicated only by color.
623623+* `.git([context: Integer]) -> Template`: Format as a Git diff.
624624+* `.stat([width: Integer]) -> DiffStats`: Calculate stats of changed lines.
625625+* `.summary() -> Template`: Format as a list of status code and path pairs.
626626+627627+### `TreeDiffEntry` type
628628+629629+_Conversion: `Boolean`: no, `Serialize`: no, `Template`: no_
630630+631631+This type cannot be printed. The following methods are defined.
632632+633633+* `.path() -> RepoPath`: Path to the entry. If the entry is a copy/rename, this
634634+ points to the target (or right) entry.
635635+* `.display_diff_path() -> String`: Format path for display, taking into account copy/rename information.
636636+* `.status() -> String`: One of `"modified"`, `"added"`, `"removed"`,
637637+ `"copied"`, or `"renamed"`.
638638+* `.status_char() -> String`: Single-character status indicator: `"M"` for modified,
639639+ `"A"` for added, `"D"` for removed, `"C"` for copied, or `"R"` for renamed.
640640+* `.source() -> TreeEntry`: The source (or left) entry.
641641+* `.target() -> TreeEntry`: The target (or right) entry.
642642+643643+### `TreeEntry` type
644644+645645+_Conversion: `Boolean`: no, `Serialize`: no, `Template`: no_
646646+647647+This type cannot be printed. The following methods are defined.
648648+649649+* `.path() -> RepoPath`: Path to the entry.
650650+* `.conflict() -> Boolean`: True if the entry is a merge conflict.
651651+* `.conflict_side_count() -> Integer`: Number of sides in the merge conflict (1 if not
652652+ conflicted, 2 or more for multi-way merges).
653653+* `.file_type() -> String`: One of `"file"`, `"symlink"`, `"tree"`,
654654+ `"git-submodule"`, or `"conflict"`.
655655+* `.executable() -> Boolean`: True if the entry is an executable file.
656656+657657+### `WorkspaceRef` type
658658+659659+_Conversion: `Boolean`: no, `Serialize`: yes, `Template`: yes_
660660+661661+The following methods are defined.
662662+663663+* `.name() -> RefSymbol`: Returns the workspace name as a symbol.
664664+* `.target() -> Commit`: Returns the working-copy commit of this workspace.
665665+666666+## Color labels
667667+668668+You can [customize the output colors][config-colors] by using color labels. `jj`
669669+adds some labels automatically; they can also be added manually.
670670+671671+Template fragments are usually **automatically** labeled with the command name,
672672+the context (or the top-level object), and the method names. For example, the
673673+following template is labeled as `op_log operation id short` automatically:
674674+675675+```sh
676676+jj op log -T 'self.id().short()'
677677+```
678678+679679+The exact names of such labels are often straightforward, but are not currently
680680+documented. You can discover the actual label names used with the
681681+`--color=debug` option, e.g.
682682+683683+```sh
684684+jj op log -T 'self.id().short()' --color=debug
685685+```
686686+687687+Additionally, you can **manually** insert arbitrary labels using the
688688+`label(label, content)` function. For example,
689689+690690+```sh
691691+jj op log -T '"ID: " ++ self.id().short().substr(0, 1) ++ label("id short", "<redacted>")'
692692+```
693693+694694+will print "ID:" in the default style, and the string `<redacted>` in the same
695695+style as the first character of the id. It would also be fine to use an
696696+arbitrary template instead of the string `"<redacted>"`, possibly including
697697+nested invocations of `label()`.
698698+699699+You are free to use custom label names as well. This will only have a visible
700700+effect if you also [customize their colors][config-colors] explicitly.
701701+702702+[config-colors]: config.md#custom-colors-and-styles
703703+704704+## Configuration
705705+706706+The default templates and aliases() are defined in the `[templates]` and
707707+`[template-aliases]` sections of the config respectively. The exact definitions
708708+can be seen in the [`cli/src/config/templates.toml`][1] file in jj's source
709709+tree.
710710+711711+[1]: https://github.com/jj-vcs/jj/blob/main/cli/src/config/templates.toml
712712+713713+<!--- TODO: Find a way to embed the default config files in the docs -->
714714+715715+New keywords and functions can be defined as aliases, by using any
716716+combination of the predefined keywords/functions and other aliases.
717717+718718+Alias functions can be overloaded by the number of parameters. However, builtin
719719+functions will be shadowed by name, and can't co-exist with aliases.
720720+721721+For example:
722722+723723+```toml
724724+[template-aliases]
725725+'commit_change_ids' = '''
726726+concat(
727727+ format_field("Commit ID", commit_id),
728728+ format_field("Change ID", change_id),
729729+)
730730+'''
731731+'format_field(key, value)' = 'key ++ ": " ++ value ++ "\n"'
732732+```
733733+734734+## Examples
735735+736736+Get short commit IDs of the working-copy parents:
737737+738738+```sh
739739+jj log --no-graph -r @ -T 'parents.map(|c| c.commit_id().short()).join(",")'
740740+```
741741+742742+Show machine-readable list of full commit and change IDs:
743743+744744+```sh
745745+jj log --no-graph -T 'commit_id ++ " " ++ change_id ++ "\n"'
746746+```
747747+748748+Print the description of the current commit, defaulting to `(no description set)`:
749749+750750+```sh
751751+jj log -r @ --no-graph -T 'coalesce(description, "(no description set)\n")'
752752+```