just playing with tangled
1# Configuration
2
3These are the config settings available to jj/Jujutsu.
4
5
6## Config files and TOML
7
8`jj` loads several types of config settings:
9
10- The built-in settings. These cannot be edited. They can be viewed in the
11 `cli/src/config/` directory in `jj`'s source repo.
12
13- The user settings. These can be edited with `jj config edit --user`. User
14settings are located in [the user config files], which can be found with `jj
15config path --user`.
16
17- The repo settings. These can be edited with `jj config edit --repo` and are
18located in `.jj/repo/config.toml`.
19
20- Settings [specified in the command-line](#specifying-config-on-the-command-line).
21
22These are listed in the order they are loaded; the settings from earlier items
23in the list are overridden by the settings from later items if they disagree.
24Every type of config except for the built-in settings is optional.
25
26You can enable JSON Schema validation in your editor by adding a `$schema`
27reference at the top of your TOML config files. See [JSON Schema
28Support] for details.
29
30See the [TOML site] and the [syntax guide] for a detailed description of the
31syntax. We cover some of the basics below.
32
33[the user config files]: #user-config-files
34[TOML site]: https://toml.io/en/
35[syntax guide]: https://toml.io/en/v1.0.0
36[JSON Schema Support]: #json-schema-support
37
38The first thing to remember is that the value of a setting (the part to the
39right of the `=` sign) should be surrounded in quotes if it's a string.
40
41### Dotted style and headings
42In TOML, anything under a heading can be dotted instead. For example,
43`user.name = "YOUR NAME"` is equivalent to:
44
45```toml
46[user]
47name = "YOUR NAME"
48```
49
50For future reference, here are a couple of more complicated examples,
51
52```toml
53# Dotted style
54template-aliases."format_short_id(id)" = "id.shortest(12)"
55colors."commit_id prefix".bold = true
56
57# is equivalent to:
58[template-aliases]
59"format_short_id(id)" = "id.shortest(12)"
60
61[colors]
62"commit_id prefix" = { bold = true }
63```
64
65The docs below refer to keys in text using dotted notation, but example
66blocks will use heading notation to be unambiguous. If you are confident with TOML
67then use whichever suits you in your config. If you mix dotted keys and headings,
68**you must put the dotted keys before the first heading**.
69
70That's probably enough TOML to keep you out of trouble but the [syntax guide] is
71very short if you ever need to check.
72
73
74## User settings
75
76```toml
77[user]
78name = "YOUR NAME"
79email = "YOUR_EMAIL@example.com"
80```
81
82Don't forget to change these to your own details!
83
84## UI settings
85
86### Colorizing output
87
88Possible values are `always`, `never`, `debug` and `auto` (default: `auto`).
89`auto` will use color only when writing to a terminal. `debug` will print the
90active labels alongside the regular colorized output.
91
92This setting overrides the `NO_COLOR` environment variable (if set).
93
94```toml
95[ui]
96color = "never" # Turn off color
97```
98
99### Custom colors and styles
100
101You can customize the colors used for various elements of the UI. For example:
102
103```toml
104[colors]
105commit_id = "green"
106```
107
108The following colors are available:
109
110* black
111* red
112* green
113* yellow
114* blue
115* magenta
116* cyan
117* white
118* default
119
120All of them but "default" come in a bright version too, e.g. "bright red". The
121"default" color can be used to override a color defined by a parent style
122(explained below).
123
124You can also use a 6-digit hex code for more control over the exact color used:
125
126```toml
127[colors]
128change_id = "#ff1525"
129```
130
131If you use a string value for a color, as in the examples above, it will be used
132for the foreground color. You can also set the background color, reverse colors
133(swap foreground and background), or make the text bold, italic, or underlined.
134For that, you need to use a table:
135
136```toml
137[colors]
138commit_id = { fg = "green", bg = "#ff1525", bold = true, underline = true }
139change_id = { reverse = true, italic = true }
140```
141
142The key names are called "labels". The above used `commit_id` as label. You can
143also create rules combining multiple labels. The rules work a bit like CSS
144selectors. For example, if you want to color commit IDs green in general but
145make the commit ID of the working-copy commit also be underlined, you can do
146this:
147
148```toml
149[colors]
150commit_id = "green"
151"working_copy commit_id" = { underline = true }
152```
153
154Parts of the style that are not overridden - such as the foreground color in the
155example above - are inherited from the style of the parent label.
156
157Which elements can be colored is not yet documented, but see
158the [default color configuration](https://github.com/jj-vcs/jj/blob/main/cli/src/config/colors.toml)
159for some examples of what's possible.
160
161### Default command
162
163When `jj` is run with no explicit subcommand, the value of the
164`ui.default-command` setting will be used instead. Possible values are any valid
165subcommand name, subcommand alias, or user-defined alias (defaults to `"log"`).
166
167```toml
168[ui]
169default-command = ["log", "--reversed"]
170```
171
172### Default description
173
174The editor content of a commit description can be populated by the
175`draft_commit_description` template.
176
177```toml
178[templates]
179draft_commit_description = '''
180concat(
181 coalesce(description, "\n"),
182 surround(
183 "\nJJ: This commit contains the following changes:\n", "",
184 indent("JJ: ", diff.stat(72)),
185 ),
186 "\nJJ: ignore-rest\n",
187 diff.git(),
188)
189'''
190```
191
192The value of the `ui.default-description` setting can also be used in order to
193fill in things like BUG=, TESTED= etc.
194
195```toml
196[ui]
197default-description = "\n\nTESTED=TODO"
198```
199
200### Diff colors and styles
201
202In color-words and git diffs, word-level hunks are rendered with underline. You
203can override the default style with the following keys:
204
205```toml
206[colors]
207# Highlight hunks with background
208"diff removed token" = { bg = "#221111", underline = false }
209"diff added token" = { bg = "#002200", underline = false }
210# Alternatively, swap colors
211"diff token" = { reverse = true, underline = false }
212```
213
214### Diff format
215
216```toml
217[ui]
218# Possible values: "color-words" (default), "git", "summary"
219diff.format = "git"
220```
221
222#### Color-words diff options
223
224In color-words diffs, changed words are displayed inline by default. Because
225it's difficult to read a diff line with many removed/added words, there's a
226threshold to switch to traditional separate-line format. You can also change
227the default number of lines of context shown.
228
229* `max-inline-alternation`: Maximum number of removed/added word alternation to
230 inline. For example, `<added> ... <added>` sequence has 1 alternation, so the
231 line will be inline if `max-inline-alternation >= 1`. `<added> ... <removed>
232 ... <added>` sequence has 3 alternation.
233
234 * `0`: disable inlining, making `--color-words` more similar to `--git`
235 * `1`: inline removes-only or adds-only lines
236 * `2`, `3`, ..: inline up to `2`, `3`, .. alternation
237 * `-1`: inline all lines
238
239 The default is `3`.
240
241 **This parameter is experimental.** The definition is subject to change.
242* `context`: Number of lines of context to show in the diff. The default is `3`.
243
244```toml
245[diff.color-words]
246max-inline-alternation = 3
247context = 3
248```
249
250#### Git diff options
251
252In git diffs you can change the default number of lines of context shown.
253
254* `context`: Number of lines of context to show in the diff. The default is `3`.
255
256```toml
257[diff.git]
258context = 3
259```
260
261### Generating diffs by external command
262
263If `ui.diff.tool` is set, the specified diff command will be called instead of
264the internal diff function.
265
266```toml
267[ui]
268# Use Difftastic by default
269diff.tool = ["difft", "--color=always", "$left", "$right"]
270# Use tool named "<name>" (see below)
271diff.tool = "<name>"
272```
273
274The external diff tool can also be enabled by `diff --tool <name>` argument.
275For the tool named `<name>`, command arguments can be configured as follows.
276
277```toml
278[merge-tools.<name>]
279# program = "<name>" # Defaults to the name of the tool if not specified
280diff-args = ["--color=always", "$left", "$right"]
281```
282
283- `$left` and `$right` are replaced with the paths to the left and right
284 directories to diff respectively.
285
286By default `jj` will invoke most external tools with a directory containing the left
287and right sides.
288
289For diff tools, the `diff-invocation-mode` config can be used to toggle between
290this behavior and file-by-file invocations:
291
292```toml
293[ui]
294diff.tool = "gvimdiff"
295
296[merge-tools.gvimdiff]
297diff-invocation-mode = "dir" # Or "file-by-file"
298```
299
300By default `jj` will display a warning when the command exits with a non-success
301error code. The `diff-expected-exit-codes` config can suppress this warning
302message for specific exit codes:
303
304```toml
305[merge-tools.delta]
306diff-expected-exit-codes = [0, 1]
307```
308
309### Conflict marker style
310
311You can configure which style of conflict markers to use when materializing
312conflicts:
313
314```toml
315[ui]
316# Shows a single snapshot and one or more diffs to apply to it
317conflict-marker-style = "diff"
318# Shows a snapshot for each side and base of the conflict
319conflict-marker-style = "snapshot"
320# Uses Git's "diff3" conflict markers to support tools that depend on it
321conflict-marker-style = "git"
322```
323
324For more details about these conflict marker styles, see the [conflicts
325page](conflicts.md#conflict-markers).
326
327### Set of immutable commits
328
329You can configure the set of immutable commits via
330`revset-aliases."immutable_heads()"`. The default set of immutable heads is
331`builtin_immutable_heads()`, which in turn is defined as
332`present(trunk()) | tags() | untracked_remote_bookmarks()`. For example, to
333also consider the `release@origin` bookmark immutable:
334
335```toml
336[revset-aliases]
337"immutable_heads()" = "builtin_immutable_heads() | release@origin"
338```
339
340To prevent rewriting commits authored by other users:
341
342```toml
343# The `trunk().. &` bit is an optimization to scan for non-`mine()` commits
344# only among commits that are not in `trunk()`.
345[revset-aliases]
346"immutable_heads()" = "builtin_immutable_heads() | (trunk().. & ~mine())"
347```
348
349Ancestors of the configured set are also immutable. The root commit is always
350immutable even if the set is empty.
351
352Immutable commits (other than the root commit) can be rewritten using the
353`--ignore-immutable` CLI flag.
354
355!!! warning
356
357 Using `--ignore-immutable` will allow you to rewrite any commit in the
358 history, and all descendants, without warning. Use this power wisely, and
359 remember `jj undo`.
360
361### Behavior of prev and next commands
362
363If you prefer using an "edit-based" workflow, rather than squashing
364modifications into parent changes, you may find yourself using the `prev` and
365`next` commands with their `--edit` flag often to move between your changes. You
366can avoid having to type the `--edit` flag every time you need it by actually
367making it the default:
368
369```toml
370[ui.movement]
371edit = true
372```
373
374You can pass the `--no-edit` flag to `prev` and `next` if you find yourself
375needing the original behavior.
376
377## List
378
379### Default Template
380
381You can configure the template used when no `-T` is specified.
382
383- `templates.config_list` for `jj config list`
384
385```toml
386[templates]
387# Use builtin config list template
388config_list = "builtin_config_list"
389```
390
391If you want to see the config variable origin (type and path) when you do `jj config list`
392you can add this to your config:
393
394```toml
395[templates]
396config_list = "builtin_config_list_detailed"
397```
398
399
400## Log
401
402### Default revisions
403
404You can configure the revisions `jj log` would show when neither `-r` nor any paths are specified.
405
406```toml
407[revsets]
408# Show commits that are not in `main@origin`
409log = "main@origin.."
410```
411
412The default value for `revsets.log` is
413`'present(@) | ancestors(immutable_heads().., 2) | present(trunk())'`.
414
415### Default Template
416
417You can configure the template used when no `-T` is specified.
418
419- `templates.log` for `jj log`
420- `templates.op_log` for `jj op log`
421- `templates.show` for `jj show`
422
423```toml
424[templates]
425# Use builtin log template
426log = "builtin_log_compact"
427# Use builtin op log template
428op_log = "builtin_op_log_compact"
429# Use builtin show template
430show = "builtin_log_detailed"
431```
432
433If you want to see the full description when you do `jj log` you can add this to
434your config:
435
436```toml
437[templates]
438log = "builtin_log_compact_full_description"
439```
440
441### Graph style
442
443```toml
444[ui]
445# Possible values: "curved" (default), "square", "ascii", "ascii-large"
446graph.style = "square"
447```
448
449#### Node style
450
451The symbols used to represent commits or operations can be customized via
452templates.
453
454- `templates.log_node` for commits (with `Option<Commit>` keywords)
455- `templates.op_log_node` for operations (with `Operation` keywords)
456
457For example:
458```toml
459[templates]
460log_node = '''
461coalesce(
462 if(!self, "🮀"),
463 if(current_working_copy, "@"),
464 if(root, "┴"),
465 if(immutable, "●", "○"),
466)
467'''
468op_log_node = 'if(current_operation, "@", "○")'
469```
470
471### Wrap log content
472
473If enabled, `log`/`evolog`/`op log` content will be wrapped based on
474the terminal width.
475
476```toml
477[ui]
478log-word-wrap = true
479```
480
481### Display of commit and change ids
482
483Can be customized by the `format_short_id()` template alias.
484
485```toml
486[template-aliases]
487# Highlight unique prefix and show at least 12 characters (default)
488'format_short_id(id)' = 'id.shortest(12)'
489# Just the shortest possible unique prefix
490'format_short_id(id)' = 'id.shortest()'
491# Show unique prefix and the rest surrounded by brackets
492'format_short_id(id)' = 'id.shortest(12).prefix() ++ "[" ++ id.shortest(12).rest() ++ "]"'
493# Always show 12 characters
494'format_short_id(id)' = 'id.short(12)'
495```
496
497To customize these separately, use the `format_short_commit_id()` and
498`format_short_change_id()` aliases:
499
500```toml
501[template-aliases]
502# Uppercase change ids. `jj` treats change and commit ids as case-insensitive.
503'format_short_change_id(id)' = 'format_short_id(id).upper()'
504```
505
506Operation ids can be customized by the `format_short_operation_id()` alias:
507
508```toml
509[template-aliases]
510# Always show 12 characters
511'format_short_operation_id(id)' = 'id.short(12)'
512```
513
514To get shorter prefixes for certain revisions, set `revsets.short-prefixes`:
515
516```toml
517[revsets]
518# Prioritize the current bookmark
519short-prefixes = "(main..@)::"
520```
521
522### Relative timestamps
523
524Can be customized by the `format_timestamp()` template alias.
525
526```toml
527[template-aliases]
528# Full timestamp in ISO 8601 format
529'format_timestamp(timestamp)' = 'timestamp'
530# Relative timestamp rendered as "x days/hours/seconds ago"
531'format_timestamp(timestamp)' = 'timestamp.ago()'
532```
533
534`jj op log` defaults to relative timestamps. To use absolute timestamps, you
535will need to modify the `format_time_range()` template alias.
536
537```toml
538[template-aliases]
539'format_time_range(time_range)' = 'time_range.start() ++ " - " ++ time_range.end()'
540```
541
542### Author format
543
544Can be customized by the `format_short_signature()` template alias.
545
546```toml
547[template-aliases]
548# Full email address (default)
549'format_short_signature(signature)' = 'signature.email()'
550# Both name and email address
551'format_short_signature(signature)' = 'signature'
552# Username part of the email address
553'format_short_signature(signature)' = 'signature.username()'
554```
555
556### Commit timestamp
557
558Commits have both an "author timestamp" and "committer timestamp". By default,
559jj displays the committer timestamp, but can be changed to show the author
560timestamp instead.
561
562The function must return a timestamp because the return value will likely be
563formatted with `format_timestamp()`.
564
565```toml
566[template-aliases]
567'commit_timestamp(commit)' = 'commit.author().timestamp()'
568```
569
570### Signature format
571
572Can be enabled with `ui.show-cryptographic-signatures`, and
573customized with `format_short_cryptographic_signature(sig)` and
574`format_detailed_cryptographic_signature(sig)`.
575
576Note that the formatting functions take an `Option<CryptographicSignature>`.
577This allows you to emit a custom message if a signature is not present, but
578will raise an error if you try to access methods on a signature that is not
579available.
580
581```toml
582[ui]
583# default is false
584show-cryptographic-signatures = true
585
586[template-aliases]
587'format_short_cryptographic_signature(sig)' = '''
588 if(sig,
589 sig.status(),
590 "(no sig)",
591 )
592'''
593```
594
595## Allow "large" revsets by default
596
597Certain commands (such as `jj rebase`) can take multiple revset arguments, but
598default to requiring each of those revsets to expand to a *single* revision.
599This restriction can be overridden by prefixing a revset that the user wants to
600be able to expand to more than one revision with the [`all:`
601modifier](revsets.md#the-all-modifier).
602
603Another way you can override this check is by setting
604`ui.always-allow-large-revsets` to `true`. Then, `jj` will allow every one of
605the revset arguments of such commands to expand to any number of revisions.
606
607```toml
608[ui]
609# Assume `all:` prefix before revsets whenever it would make a difference
610always-allow-large-revsets = true
611```
612
613## Pager
614
615The default pager is can be set via `ui.pager` or the `PAGER` environment
616variable. The priority is as follows (environment variables are marked with
617a `$`):
618
619`ui.pager` > `$PAGER`
620
621`less -FRX` is the default pager in the absence of any other setting, except
622on Windows where it is `:builtin`.
623
624The special value `:builtin` enables usage of the [integrated
625pager](#builtin-pager).
626
627If you are using a standard Linux distro, your system likely already has
628`$PAGER` set and that will be preferred over the built-in. To use the built-in:
629
630```shell
631jj config set --user ui.pager :builtin
632```
633
634It is possible the default will change to `:builtin` for all platforms in the
635future.
636
637Additionally, paging behavior can be toggled via `ui.paginate` like so:
638
639```toml
640[ui]
641# Enable pagination for commands that support it (default)
642paginate = "auto"
643# Disable all pagination, equivalent to using --no-pager
644paginate = "never"
645```
646
647### Builtin pager
648
649Our builtin pager is based on
650[`streampager`](https://github.com/markbt/streampager/) but is configured within
651`jj`'s config. It is configured via the `ui.streampager` table.
652
653#### Key bindings
654
655The built-in pager supports both navigation via arrows and Vim-style navigation.
656Beyond that, here are some useful keybindings for the pager:
657
658| Key | Action |
659| :-------------- | :-------------------- |
660| `Ctrl-c` or `q` | Quit |
661| `h` or `F1` | Show all key bindings |
662| `Esc` | Close help or prompt |
663| `\` | Toggle line wrapping |
664| `#` | Toggle line numbers |
665| `Ctrl-r` | Toggle the ruler |
666
667The built-in pager does not support mouse input.
668
669#### Wrapping config
670
671Wrapping performed by the pager happens *in addition to* any
672wrapping that `jj` itself does.
673
674```toml
675[ui.streampager]
676wrapping = "anywhere" # wrap at screen edge (default)
677wrapping = "word" # wrap on word boundaries
678wrapping = "none" # strip long lines, allow scrolling
679 # left and right like `less -S`
680```
681
682#### Auto-exit, clearing the screen on startup or exit
683
684You can configure whether the pager clears the screen on startup or exit, and
685whether it quits automatically on short inputs. When the pager auto-quits,
686features like word-wrapping are disabled.
687
688```toml
689[ui.streampager]
690# Do not clear screen on exit. Use a full-screen interface for long
691# output only. Like `less -FX`.
692interface = "quit-if-one-page" # (default).
693# Always use a full-screen interface, ask the terminal to clear the
694# screen on exit. Like `less -+FX`.
695interface = "full-screen-clear-output"
696# Use the alternate screen if the input is either long or takes more
697# than 2 seconds to finish. Similar but not identical to `less -F -+X`.
698interface = "quit-quickly-or-clear-output"
699```
700
701
702### Processing contents to be paged
703
704If you'd like to pass the output through a formatter e.g.
705[`diff-so-fancy`](https://github.com/so-fancy/diff-so-fancy) before piping it
706through a pager you must do it using a subshell as, unlike `git` or `hg`, the
707command will be executed directly. For example:
708
709```toml
710[ui]
711pager = ["sh", "-c", "diff-so-fancy | less -RFX"]
712```
713
714Some formatters (like [`delta`](https://github.com/dandavison/delta)) require
715git style diffs for formatting. You can configure this style of
716diff as the default with the `ui.diff` setting. For example:
717
718```toml
719[ui]
720pager = "delta"
721
722[ui.diff]
723format = "git"
724```
725
726## Aliases
727
728You can define aliases for commands, including their arguments. For example:
729
730```toml
731[aliases]
732# `jj l` shows commits on the working-copy commit's (anonymous) bookmark
733# compared to the `main` bookmark
734l = ["log", "-r", "(main..@):: | (main..@)-"]
735```
736
737This alias syntax can only run a single jj command. However, you may want to
738execute multiple jj commands with a single alias, or run arbitrary scripts that
739complement your version control workflow. This can be done, but be aware of the
740danger:
741
742!!! warning
743
744 The following technique just provides a convenient syntax for running
745 arbitrary code on your system. Using it irresponsibly may cause damage
746 ranging from breaking the behavior of `jj undo` to wiping your file system.
747 Exercise the same amount of caution while writing these aliases as you would
748 when typing commands into the terminal!
749
750 This feature may be removed or replaced by an embedded scripting language in
751 the future.
752
753The command `jj util exec` will simply run any command you pass to it as an
754argument. Additional arguments are passed through. Here are some examples:
755
756```toml
757[aliases]
758my-script = ["util", "exec", "--", "my-jj-script"]
759# ^^^^
760# This makes sure that flags are passed to your script instead of parsed by jj.
761my-inline-script = ["util", "exec", "--", "bash", "-c", """
762#!/usr/bin/env bash
763set -euo pipefail
764echo "Look Ma, everything in one file!"
765echo "args: $@"
766""", ""]
767# ^^
768# This last empty string will become "$0" in bash, so your actual arguments
769# are all included in "$@" and start at "$1" as expected.
770```
771
772## Editor
773
774The default editor is set via `ui.editor`, though there are several places to
775set it. The priority is as follows (environment variables are marked with
776a `$`):
777
778`$JJ_EDITOR` > `ui.editor` > `$VISUAL` > `$EDITOR`
779
780Pico is the default editor (Notepad on Windows) in the absence of any other
781setting, but you could set it explicitly too.
782
783```toml
784[ui]
785editor = "pico"
786```
787
788To use NeoVim instead:
789
790```toml
791[ui]
792editor = "nvim"
793```
794
795For GUI editors you possibly need to use a `-w` or `--wait`. Some examples:
796
797```toml
798[ui]
799editor = "code -w" # VS Code
800editor = "code.cmd -w" # VS Code on Windows
801editor = "bbedit -w" # BBEdit
802editor = "subl -n -w" # Sublime Text
803editor = "mate -w" # TextMate
804editor = ["C:/Program Files/Notepad++/notepad++.exe",
805 "-multiInst", "-notabbar", "-nosession", "-noPlugin"] # Notepad++
806editor = "idea --temp-project --wait" #IntelliJ
807```
808
809Obviously, you would only set one line, don't copy them all in!
810
811## Editing diffs
812
813The `ui.diff-editor` setting affects the default tool used for editing diffs
814(e.g. `jj split`, `jj squash -i`). If it is not set, the special value
815`:builtin` is used. It launches a built-in TUI tool (known as [scm-diff-editor])
816to edit the diff in your terminal.
817
818[scm-diff-editor]: https://github.com/arxanas/scm-record?tab=readme-ov-file#scm-diff-editor
819
820You can try a different tool temporarily by doing e.g. `jj split --tool meld` or
821you can set the option to change the default. This requires that you have an
822appropriate tool installed, see for example [the instructions for using
823Meld](#using-meld-as-a-diff-editor).
824
825**Suggestion:** If possible, it is recommended to try an external diff tool like
826[Meld](#using-meld-as-a-diff-editor) (see below for some other possibilities)
827for splitting commits and other diff editing, in addition to the built-in diff
828editor. It is good to know the capabilities of both. The built-in diff editor
829does not require external tools to be available, is faster for tasks like
830picking hunks, and does not require leaving the terminal. External tools give
831you the flexibility of picking out portions of lines from the diff or even
832arbitrarily editing the text of the files.
833
834If `ui.diff-editor` is a string, e.g. `"meld"`, the arguments will be read from
835the following config keys.
836
837```toml
838[merge-tools.meld]
839# program = "meld" # Defaults to the name of the tool if not specified
840program = "/path/to/meld" # May be necessary if `meld` is not in the PATH
841edit-args = ["--newtab", "$left", "$right"]
842```
843
844`jj` makes the following substitutions:
845
846- `$left` and `$right` are replaced with the paths to the left and right
847 directories to diff respectively.
848
849- If no `edit-args` are specified, `["$left", "$right"]` are set by default.
850
851Finally, `ui.diff-editor` can be a list that specifies a command and its arguments.
852
853Some examples:
854
855```toml
856[ui]
857# Use merge-tools.meld.edit-args
858diff-editor = "meld" # Or `kdiff3`, or `diffedit3`, ...
859# Specify edit-args inline
860diff-editor = ["/path/to/binary", "--be-helpful", "$left", "$right"]
861# Equivalent to ["binary", "$left", "$right"] arguments by default
862diff-editor = "binary"
863```
864
865
866### Experimental 3-pane diff editing
867
868We offer two special "3-pane" diff editor configs:
869
870- `meld-3`, which requires installing [Meld](https://meldmerge.org/), and
871- `diffedit3`, which requires installing [`diffedit3`](https://github.com/ilyagr/diffedit3/releases).
872
873`Meld` is a graphical application that is recommended, but can be difficult to
874install in some situations. `diffedit3` is designed to be easy to install and to
875be usable in environments where Meld is difficult to use (e.g. over SSH via port
876forwarding). `diffedit3` starts a local server that can be accessed via a web
877browser, similarly to [Jupyter](https://jupyter.org/).
878
879There is also the `diffedit3-ssh` which is similar to `diffedit3` but does not
880try to open the web browser pointing to the local server (the URL
881printed to the terminal) automatically. `diffedit3-ssh` also always uses ports in between
88217376-17380 and fails if they are all busy. This can be useful when working
883over SSH. Open the fold below for more details of how to set that up.
884
885<details>
886<summary> Tips for using `diffedit3-ssh` over SSH </summary>
887
888To use `diffedit3` over SSH, you need to set up port forwarding. One way to do
889this is to start SSH as follows (copy-paste the relevant lines):
890
891```shell
892ssh -L 17376:localhost:17376 \
893 -L 17377:localhost:17377 \
894 -L 17378:localhost:17378 \
895 -L 17379:localhost:17379 \
896 -L 17380:localhost:17380 \
897 myhost.example.com
898```
899
900`diffedit3-ssh` is set up to use these 5 ports by default. Usually, only the
901first of them will be used. The rest are used if another program happens to use
902one of them, or if you run multiple instances of `diffedit3` at the same time.
903
904Another way is to add a snippet to `~/.ssh/config`:
905
906```ssh-config
907Host myhost
908 User myself
909 Hostname myhost.example.com
910 LocalForward 17376 localhost:17376
911 LocalForward 17377 localhost:17377
912 LocalForward 17378 localhost:17378
913 LocalForward 17379 localhost:17379
914 LocalForward 17380 localhost:17380
915```
916
917With that configuration, you should be able to simply `ssh myhost`.
918
919</details>
920
921
922Setting either `ui.diff-editor = "meld-3"` or `ui.diff-editor = "diffedit3"`
923will result in the diff editor showing 3 panes: the diff on the left and right,
924and an editing pane in the middle. This allow you to see both sides of the
925original diff while editing.
926
927If you use `ui.diff-editor = "meld-3"`, note that you can still get the 2-pane
928Meld view using `jj diff --tool meld`. `diffedit3` has a button you can use to
929switch to a 2-pane view.
930
931To configure other diff editors in this way, you can include `$output` together
932with `$left` and `$right` in `merge-tools.TOOL.edit-args`. `jj` will replace
933`$output` with the directory where the diff editor will be expected to put the
934result of the user's edits. Initially, the contents of `$output` will be the
935same as the contents of `$right`.
936
937### `JJ-INSTRUCTIONS`
938
939When editing a diff, jj will include a synthetic file called `JJ-INSTRUCTIONS`
940in the diff with instructions on how to edit the diff. Any changes you make to
941this file will be ignored. To suppress the creation of this file, set
942`ui.diff-instructions = false`.
943
944### Using Meld as a diff editor
945
946[Meld](https://meldmerge.org) is a nice and polished free diff editor. It can be
947obtained as follows:
948
949- **Linux:** use your favorite package manager, e.g. `sudo apt install meld`.
950
951- **Windows:** Meld can be downloaded from <https://meldmerge.org/>.
952
953- **Mac OS:** Install Homebrew and run `brew install --cask dehesselle-meld`.
954 This will install both an app in `/Applications/Meld.app` and the command-line
955 `meld` command that `jj` uses. You can read about [more details and other
956 options](https://gist.github.com/ilyagr/1b40f6061d8ad320cee4c12843df1a23) but,
957 as of this writing, this is by far the easiest.
958
959 !!! warning
960
961 Do *not* use the Homebrew `meld` package.
962 It does not work on ARM Macs and may have problems on recent versions of macOS.
963
964`jj` has two diff editing configurations that use Meld: `meld` for a 2-pane view
965and `meld-3` for a [three-pane view](#experimental-3-pane-diff-editing).
966
967There is also a `meld` [merge tool](#3-way-merge-tools-for-conflict-resolution)
968that can be useful, but does not support displaying the merge base while
969merging.
970
971### Using Vim as a diff editor
972
973Using `ui.diff-editor = "vimdiff"` is possible but not recommended. For a better
974experience, you can follow [instructions from the Wiki] to configure the
975[DirDiff Vim plugin] and/or the [vimtabdiff Python script].
976
977[instructions from the Wiki]: https://github.com/jj-vcs/jj/wiki/Vim#using-vim-as-a-diff-tool
978
979[DirDiff Vim plugin]: https://github.com/will133/vim-dirdiff
980[vimtabdiff Python script]: https://github.com/balki/vimtabdiff
981
982## 3-way merge tools for conflict resolution
983
984The `ui.merge-editor` key specifies the tool used for three-way merge tools
985by `jj resolve`. For example:
986
987```toml
988[ui]
989# Use merge-tools.meld.merge-args
990merge-editor = "meld" # Or "vscode" or "vscodium" or "kdiff3" or "vimdiff"
991# Specify merge-args inline
992merge-editor = ["meld", "$left", "$base", "$right", "-o", "$output"]
993```
994
995The "vscode", "vscodium", "meld", "kdiff3", and "vimdiff" tools can be used out of the box,
996as long as they are installed.
997
998Using VS Code as a merge tool works well with VS Code's [Remote
999Development](https://code.visualstudio.com/docs/remote/remote-overview)
1000functionality, as long as `jj` is called from VS Code's terminal.
1001
1002### Setting up a custom merge tool
1003
1004To use a different tool named `TOOL`, the arguments to pass to the tool MUST be
1005specified either inline or in the `merge-tools.TOOL.merge-args` key. As an
1006example of how to set this key and other tool configuration options, here is
1007the out-of-the-box configuration of the three default tools. (There is no need
1008to copy it to your config file verbatim, but you are welcome to customize it.)
1009
1010```toml
1011[merge-tools.kdiff3]
1012# program = "kdiff3" # Defaults to the name of the tool if not specified
1013merge-args = ["$base", "$left", "$right", "-o", "$output", "--auto"]
1014[merge-tools.meld]
1015merge-args = ["$left", "$base", "$right", "-o", "$output", "--auto-merge"]
1016
1017[merge-tools.vimdiff]
1018merge-args = ["-f", "-d", "$output", "-M",
1019 "$left", "$base", "$right",
1020 "-c", "wincmd J", "-c", "set modifiable",
1021 "-c", "set write"]
1022program = "vim"
1023merge-tool-edits-conflict-markers = true # See below for an explanation
1024```
1025
1026`jj` makes the following substitutions:
1027
1028- `$output` (REQUIRED) is replaced with the name of the file that the merge tool
1029 should output. `jj` will read this file after the merge tool exits.
1030
1031- `$left` and `$right` are replaced with the paths to two files containing the
1032 content of each side of the conflict.
1033
1034- `$base` is replaced with the path to a file containing the contents of the
1035 conflicted file in the last common ancestor of the two sides of the conflict.
1036
1037- `$marker_length` is replaced with the length of the conflict markers which
1038 should be used for the file. This can be useful if the merge tool parses
1039 and/or generates conflict markers. Usually, `jj` uses conflict markers of
1040 length 7, but they can be longer if necessary to make parsing unambiguous.
1041
1042### Editing conflict markers with a tool or a text editor
1043
1044By default, the merge tool starts with an empty output file. If the tool puts
1045anything into the output file and exits with the 0 exit code,
1046`jj` assumes that the conflict is fully resolved, while if the tool exits with
1047a non-zero exit code, `jj` assumes that the merge should be cancelled.
1048This is appropriate for most graphical merge tools.
1049
1050For merge tools which try to automatically resolve conflicts without user input,
1051this behavior may not be desired. For instance, some automatic merge tools use
1052an exit code of 1 to indicate that some conflicts were unable to be resolved and
1053that the output file should contain conflict markers. In that case, you could
1054set the config option `merge-tools.TOOL.merge-conflict-exit-codes = [1]` to tell
1055`jj` to expect conflict markers in the output file if the exit code is 1. If a
1056merge tool produces output using Git's "diff3" conflict style, `jj` should be
1057able to parse it correctly, so many Git merge drivers should be usable with `jj`
1058as well.
1059
1060Some tools (e.g. `vimdiff`) can present a multi-way diff but don't resolve
1061conflict themselves. When using such tools, `jj`
1062can help you by populating the output file with conflict markers before starting
1063the merge tool (instead of leaving the output file empty and letting the merge
1064tool fill it in). To do that, set the
1065`merge-tools.vimdiff.merge-tool-edits-conflict-markers = true` option.
1066
1067With this option set, if the output file still contains conflict markers after
1068the conflict is done, `jj` assumes that the conflict was only partially resolved
1069and parses the conflict markers to get the new state of the conflict. The
1070conflict is considered fully resolved when there are no conflict markers left.
1071The conflict marker style can also be customized per tool using the
1072`merge-tools.TOOL.conflict-marker-style` option, which takes the same values as
1073[`ui.conflict-marker-style`](#conflict-marker-style).
1074
1075## Code formatting and other file content transformations
1076
1077The `jj fix` command allows you to efficiently rewrite files in complex commit
1078graphs with no risk of introducing conflicts, using tools like `clang-format` or
1079`prettier`. The tools run as subprocesses that take file content on standard
1080input and repeat it, with any desired changes, on standard output. The file is
1081only rewritten if the subprocess produces a successful exit code.
1082
1083### Enforce coding style rules
1084
1085Suppose you want to use `clang-format` to format your `*.c` and `*.h` files,
1086as well as sorting their `#include` directives.
1087
1088`jj fix` provides the file content anonymously on standard input, but the name
1089of the file being formatted may be important for include sorting or other output
1090like error messages. To address this, you can use the `$path` substitution to
1091provide the name of the file in a command argument.
1092
1093```toml
1094[fix.tools.clang-format]
1095command = ["/usr/bin/clang-format", "--sort-includes", "--assume-filename=$path"]
1096patterns = ["glob:'**/*.c'",
1097 "glob:'**/*.h'"]
1098```
1099
1100### Sort and remove duplicate lines from a file
1101
1102`jj fix` can also be used with tools that are not considered code formatters.
1103
1104Suppose you have a list of words in a text file in your repository, and you want
1105to keep the file sorted alphabetically and remove any duplicate words.
1106
1107```toml
1108[fix.tools.sort-word-list]
1109command = ["sort", "-u"]
1110patterns = ["word_list.txt"]
1111```
1112
1113### Execution order of tools
1114
1115If two or more tools affect the same file, they are executed in the ascending
1116lexicographical order of their configured names. This will remain as a tie
1117breaker if other ordering mechanisms are introduced in the future. If you use
1118numbers in tool names to control execution order, remember to include enough
1119leading zeros so that, for example, `09` sorts before `10`.
1120
1121Suppose you want to keep only the 10 smallest numbers in a text file that
1122contains one number on each line. This can be accomplished with `sort` and
1123`head`, but execution order is important.
1124
1125```toml
1126[fix.tools.1-sort-numbers-file]
1127command = ["sort", "-n"]
1128patterns = ["numbers.txt"]
1129
1130[fix.tools.2-truncate-numbers-file]
1131command = ["head", "-n", "10"]
1132patterns = ["numbers.txt"]
1133```
1134
1135### Disabling and enabling tools
1136
1137Tools can be disabled and enabled with the optional `enabled` config. This
1138allows you to define tools globally but enable them only for specific
1139repositories.
1140
1141In the user configuration, define a disabled tool for running rustfmt:
1142
1143```toml
1144[fix.tools.rustfmt]
1145enabled = false
1146command = ["rustfmt", "--emit", "stdout"]
1147patterns = ["glob:'**/*.rs'"]
1148```
1149
1150Then to use the tool in a specific repository, set the `enabled` config:
1151
1152```shell
1153$ jj config set --repo fix.tools.rustfmt.enabled true
1154```
1155
1156## Commit Signing
1157
1158`jj` can be configured to sign and verify the commits it creates using either
1159GnuPG or SSH signing keys.
1160
1161To do this you need to configure a signing backend.
1162
1163Setting the backend to `"none"` disables signing.
1164
1165### GnuPG Signing
1166
1167```toml
1168[signing]
1169behavior = "own"
1170backend = "gpg"
1171## You can set `key` to anything accepted by `gpg -u`
1172## If not set then defaults to the key associated with `user.email`
1173# key = "4ED556E9729E000F"
1174# key = "signing@example.com"
1175```
1176
1177By default the gpg backend will look for a `gpg` binary on your path. If you want
1178to change the program used or specify a path to `gpg` explicitly you can set:
1179
1180```toml
1181[signing]
1182backends.gpg.program = "gpg2"
1183```
1184
1185Also by default the gpg backend will consider key expiry when verifying commit signatures.
1186To consider expired keys as valid you can set:
1187
1188```toml
1189[signing]
1190backends.gpg.allow-expired-keys = true
1191```
1192
1193#### PKCS#12 Certificates
1194
1195PKCS#12 certificates can be used to sign commits using the `gpgsm` backend.
1196
1197```toml
1198[signing]
1199behavior = "own"
1200backend = "gpgsm"
1201## You can set `key` to anything accepted by `gpgsm -u`
1202## If not set then defaults to the key associated with `user.email`
1203# key = "4ED556E9729E000F"
1204# key = "signing@example.com"
1205```
1206
1207By default the gpgsm backend will look for a `gpgsm` binary on your path. If you want
1208to change the program used or specify a path to `gpgsm` explicitly you can set:
1209
1210```toml
1211[signing]
1212backends.gpgsm.program = "gpgsm"
1213```
1214
1215Also by default the gpgsm backend will consider key expiry when verifying commit signatures.
1216To consider expired keys as valid you can set:
1217
1218```toml
1219[signing]
1220backends.gpgsm.allow-expired-keys = true
1221```
1222
1223### SSH Signing
1224
1225```toml
1226[signing]
1227behavior = "own"
1228backend = "ssh"
1229key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGj+J6N6SO+4P8dOZqfR1oiay2yxhhHnagH52avUqw5h"
1230## You can also use a path instead of embedding the key
1231# key = "~/.ssh/id_for_signing.pub"
1232```
1233
1234By default the ssh backend will look for a `ssh-keygen` binary on your path. If you want
1235to change the program used or specify a path to `ssh-keygen` explicitly you can set:
1236
1237```toml
1238[signing]
1239backends.ssh.program = "/path/to/ssh-keygen"
1240```
1241
1242When verifying commit signatures the ssh backend needs to be provided with an allowed-signers
1243file containing the public keys of authors whose signatures you want to be able to verify.
1244
1245You can find the format for this file in the
1246[ssh-keygen man page](https://man.openbsd.org/ssh-keygen#ALLOWED_SIGNERS). This can be provided
1247as follows:
1248
1249```toml
1250[signing]
1251backends.ssh.allowed-signers = "/path/to/allowed-signers"
1252```
1253
1254### Sign commits only on `jj git push`
1255
1256Instead of signing all commits during creation when `signing.behavior` is
1257set to `own`, the `git.sign-on-push` configuration can be used to sign
1258commits only upon running `jj git push`. All mutable unsigned commits
1259being pushed will be signed prior to pushing. This might be preferred if the
1260signing backend requires user interaction or is slow, so that signing is
1261performed in a single batch operation.
1262
1263```toml
1264# Configure signing backend as before, but lazily signing only on push.
1265[signing]
1266behavior = "drop"
1267backend = "ssh"
1268key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGj+J6N6SO+4P8dOZqfR1oiay2yxhhHnagH52avUqw5h"
1269
1270[git]
1271sign-on-push = true
1272```
1273
1274### Manually signing commits
1275
1276You can use [`jj sign`](./cli-reference.md#jj-sign)/[`jj unsign`](./cli-reference.md#jj-unsign)
1277to sign/unsign commits manually.
1278
1279
1280!!! warning
1281
1282 `jj sign` is always signing commits, even if they are already signed by the
1283 user. While this is cumbersome for users signing via hardware devices, we
1284 cannot reliably check if a commit is already signed without creating a
1285 signature (see [this issue](https://github.com/jj-vcs/jj/issues/5786)).
1286
1287## Commit Signature Verification
1288
1289By default signature verification and display is **disabled** as it incurs a
1290performance cost when rendering medium to large change logs.
1291
1292If you want to display commit signatures in your templates, you can use
1293`commit.signature()` (see [Commit type](./templates.md#commit-type)). The
1294returned [CryptographicSignature
1295Type](./templates.md#cryptographicsignature-type) provides methods to retrieve
1296signature details.
1297
1298## Git settings
1299
1300### Default remotes for `jj git fetch` and `jj git push`
1301
1302By default, if a single remote exists it is used for `jj git fetch` and `jj git
1303push`; however if multiple remotes exist, the default remote is assumed to be
1304named `"origin"`, just like in Git. Sometimes this is undesirable, e.g. when you
1305want to fetch from a different remote than you push to, such as a GitHub fork.
1306
1307To change this behavior, you can modify the [repository
1308configuration](#config-files-and-toml) variable `git.fetch`, which can be a
1309single remote, or a list of remotes to fetch from multiple places:
1310
1311```sh
1312jj config set --repo git.fetch "upstream"
1313jj config set --repo git.fetch '["origin", "upstream"]'
1314```
1315
1316By default, the specified remote names matches exactly. You can also use a
1317[string pattern](revsets.md#string-patterns) to select remotes using patterns:
1318
1319```sh
1320jj config set --repo git.fetch "glob:*"
1321jj config set --repo git.fetch '["glob:remote*", "glob:upstream*"]'
1322```
1323
1324Similarly, you can also set the variable `git.push` to cause `jj git push` to
1325push to a different remote:
1326
1327```sh
1328jj config set --repo git.push "github"
1329```
1330
1331Note that unlike `git.fetch`, `git.push` can currently only be a single remote.
1332This is not a hard limitation, and could be changed in the future if there is
1333demand.
1334
1335### Automatic local bookmark creation
1336
1337When `jj` imports a new remote-tracking bookmark from Git, it can also create a
1338local bookmark with the same name. This feature is disabled by default because it
1339may be undesirable in some repositories, e.g.:
1340
1341- There is a remote with a lot of historical bookmarks that you don't
1342 want to be exported to the co-located Git repo.
1343- There are multiple remotes with conflicting views of that bookmark,
1344 resulting in an unhelpful conflicted state.
1345
1346You can enable this behavior by setting `git.auto-local-bookmark` like so,
1347
1348```toml
1349[git]
1350auto-local-bookmark = true
1351```
1352
1353This setting is applied only to new remote bookmarks. Existing remote bookmarks
1354can be tracked individually by using `jj bookmark track`/`untrack` commands.
1355
1356```shell
1357# import feature1 bookmark and start tracking it
1358jj bookmark track feature1@origin
1359# delete local gh-pages bookmark and stop tracking it
1360jj bookmark delete gh-pages
1361jj bookmark untrack gh-pages@upstream
1362```
1363
1364### Abandon commits that became unreachable in Git
1365
1366By default, when `jj` imports refs from Git, it will look for commits that used
1367to be [reachable][reachable] but no longer are reachable. Those commits will
1368then be abandoned, and any descendant commits will be rebased off of them (as
1369usual when commits are abandoned). You can disable this behavior and instead
1370leave the Git-unreachable commits in your repo by setting:
1371
1372```toml
1373[git]
1374abandon-unreachable-commits = false
1375```
1376
1377[reachable]: https://git-scm.com/docs/gitglossary/#Documentation/gitglossary.txt-aiddefreachableareachable
1378
1379### Prefix for generated bookmarks on push
1380
1381`jj git push --change` generates bookmark names with a prefix of "push-" by
1382default. You can pick a different prefix by setting `git.push-bookmark-prefix`. For
1383example:
1384
1385```toml
1386[git]
1387push-bookmark-prefix = "martinvonz/push-"
1388```
1389
1390### Set of private commits
1391
1392You can configure the set of private commits by setting `git.private-commits` to
1393a revset. The value is a revset of commits that Jujutsu will refuse to push. If
1394unset, all commits are eligible to be pushed.
1395
1396```toml
1397[git]
1398# Prevent pushing work in progress or anything explicitly labeled "private"
1399private-commits = "description(glob:'wip:*') | description(glob:'private:*')"
1400```
1401
1402If a commit is in `git.private-commits` but is already on the remote, then it is
1403not considered a private commit. Commits that are immutable are also excluded
1404from the private set.
1405
1406Private commits prevent their descendants from being pushed, since doing so
1407would require pushing the private commit as well.
1408
1409### Git subprocessing behaviour
1410
1411By default, Git remote interactions are handled by spawning a `git` subprocess.
1412If `git` is not on your OS path, or you want to specify a particular binary,
1413you can:
1414
1415```toml
1416[git]
1417executable-path = "/path/to/git"
1418```
1419
1420Previously, remote interactions were handled by
1421[`libgit2`](https://github.com/libgit2/libgit2) by default, which sometimes
1422caused [SSH problems](https://github.com/jj-vcs/jj/issues/4979) that could not
1423be solved by `jj` directly. If you have any issues with the `git`
1424subprocessing, you can switch back to `libgit2` with:
1425
1426```toml
1427[git]
1428subprocess = false
1429```
1430
1431Note that `libgit2` support will likely be removed in the future, so you are
1432encouraged to report any issues you experience with the default configuration.
1433
1434## Filesystem monitor
1435
1436In large repositories, it may be beneficial to use a "filesystem monitor" to
1437track changes to the working copy. This allows `jj` to take working copy
1438snapshots without having to rescan the entire working copy.
1439
1440This is governed by the `core.fsmonitor` option. Currently, the valid values are
1441`"none"` or `"watchman"`.
1442
1443### Watchman
1444
1445To configure the Watchman filesystem monitor, set
1446`core.fsmonitor = "watchman"`. Ensure that you have [installed the Watchman
1447executable on your system](https://facebook.github.io/watchman/docs/install).
1448
1449You can configure `jj` to use watchman triggers to automatically create
1450snapshots on filesystem changes by setting
1451`core.watchman.register-snapshot-trigger = true`.
1452
1453You can check whether Watchman is enabled and whether it is installed correctly
1454using `jj debug watchman status`.
1455
1456## Snapshot settings
1457
1458### Paths to automatically track
1459
1460All new files in the working copy that don't match the ignore patterns are
1461tracked by default. You can set the `snapshot.auto-track` to set which paths
1462get automatically tracked when they're added to the working copy. See the
1463[fileset documentation](filesets.md) for the syntax. Files with paths matching
1464[ignore files](working-copy.md#ignored-files) are never tracked automatically.
1465
1466You can use `jj file untrack` to untrack a file while keeping it in the working
1467copy. However, first [ignore](working-copy.md#ignored-files) them or remove them
1468from the `snapshot.auto-track` patterns; otherwise they will be immediately
1469tracked again.
1470
1471### Maximum size for new files
1472
1473By default, as an anti-footgun measure, `jj` will refuse to add new files to the
1474snapshot that are larger than a certain size; the default is 1MiB. This can be
1475changed by setting `snapshot.max-new-file-size` to a different value. For
1476example:
1477
1478```toml
1479[snapshot]
1480max-new-file-size = "10MiB"
1481# the following is equivalent
1482max-new-file-size = 10485760
1483```
1484
1485The value can be specified using a human readable string with typical suffixes;
1486`B`, `MiB`, `GB`, etc. By default, if no suffix is provided, or the value is a
1487raw integer literal, the value is interpreted as if it were specified in bytes.
1488
1489Files that already exist in the working copy are not subject to this limit.
1490
1491Setting this value to zero will disable the limit entirely.
1492
1493## Ways to specify `jj` config: details
1494
1495### User config files
1496
1497An easy way to find the user config file/directory is:
1498
1499```bash
1500jj config path --user
1501```
1502
1503On all platforms, the user's global `jj` configurations are by default loaded in
1504the following precedence order (with later configs overriding earlier ones):
1505
1506- `$HOME/.jjconfig.toml`
1507- `<PLATFORM_SPECIFIC>/jj/config.toml` (preferred)
1508- `<PLATFORM_SPECIFIC>/jj/conf.d/*.toml`
1509
1510where `$HOME` represents the user's home directory (`%USERPROFILE%` on Windows),
1511and `<PLATFORM_SPECIFIC>` represents the platform-specific configuration
1512directory shown in the table below. The platform-specific location is
1513recommended for better integration with platform services.
1514
1515The files in the `conf.d` directory are loaded in lexicographic order. This allows
1516configs to be split across multiple files and combines well
1517with [Conditional Variables](#conditional-variables).
1518
1519| Platform | Location of `<PLATFORM_SPECIFIC>` dir | Example config file location |
1520| :------- | :------------------------------------ | :-------------------------------------------------------- |
1521| Linux | `$XDG_CONFIG_HOME` or `$HOME/.config` | `/home/alice/.config/jj/config.toml` |
1522| macOS | `$HOME/Library/Application Support` | `/Users/Alice/Library/Application Support/jj/config.toml` |
1523| Windows | `{FOLDERID_RoamingAppData}` | `C:\Users\Alice\AppData\Roaming\jj\config.toml` |
1524
1525The location of the `jj` user config files/directories can also be overridden with the
1526`JJ_CONFIG` environment variable. If it is not empty, it will be used instead
1527of any configuration files in the default locations. If it is a path to a TOML
1528file, then that file will be loaded instead. If it is a path to a directory,
1529then all the TOML files in that directory will be loaded in lexicographic order
1530and merged. Multiple paths can be specified by separating them with a
1531platform-specific path separator (`:` on Unix-like systems, `;` on Windows).
1532
1533For example, the following could be used to run `jj` without loading any user configs:
1534
1535```bash
1536JJ_CONFIG= jj log # Ignores any settings specified in the config file.
1537```
1538
1539### JSON Schema Support
1540
1541Many popular editors support TOML file syntax highlighting and validation. To
1542enable schema validation in your editor, add this line at the top of your TOML
1543config files:
1544
1545```toml
1546"$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json"
1547```
1548
1549This enables features like:
1550
1551- Autocomplete for config keys
1552- Type checking of values
1553- Documentation on hover
1554- Validation of settings
1555
1556Here are some popular editors with TOML schema validation support:
1557
1558- VS Code
1559 - Install [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml)
1560
1561- Neovim/Vim
1562 - Use with [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) and [taplo](https://github.com/tamasfe/taplo)
1563
1564- JetBrains IDEs (IntelliJ, PyCharm, etc)
1565 - Install [TOML](https://plugins.jetbrains.com/plugin/8195-toml) plugin
1566
1567- Emacs
1568 - Install [lsp-mode](https://github.com/emacs-lsp/lsp-mode) and [toml-mode](https://github.com/dryman/toml-mode.el)
1569 - Configure [taplo](https://github.com/tamasfe/taplo) as the LSP server
1570
1571### Specifying config on the command-line
1572
1573You can use one or more `--config`/`--config-file` options on the command line
1574to specify additional configuration settings. This overrides settings defined in
1575config files or environment variables. For example,
1576
1577```shell
1578# Must not have spaces around the `=`
1579jj --config ui.color=always --config ui.diff-editor=meld split
1580```
1581
1582Config value should be specified as a TOML expression. If string value isn't
1583enclosed by any TOML constructs (such as array notation), quotes can be omitted.
1584Here is an example with more advanced TOML constructs:
1585
1586```shell
1587# Single quotes and the '\' are interpreted by the shell and assume a Unix shell
1588# Double quotes are passed to jj and are parsed as TOML syntax
1589jj log --config \
1590 'template-aliases."format_timestamp(timestamp)"="""timestamp.format("%Y-%m-%d %H:%M %:::z")"""'
1591```
1592
1593To load an entire TOML document, use `--config-file`:
1594
1595```shell
1596jj --config-file=extra-config.toml log
1597```
1598
1599### Conditional variables
1600
1601You can conditionally enable config variables by using `--when`.
1602
1603#### Using `[[--scope]]` tables
1604
1605Variables defined in `[[--scope]]` tables are expanded to the root table.
1606`--when` specifies the condition to enable the scope table.
1607
1608If no conditions are specified, the table is always enabled. If multiple
1609conditions are specified, their intersection is used.
1610
1611```toml
1612[user]
1613name = "YOUR NAME"
1614email = "YOUR_DEFAULT_EMAIL@example.com"
1615
1616# override user.email if the repository is located under ~/oss
1617[[--scope]]
1618--when.repositories = ["~/oss"]
1619[--scope.user]
1620email = "YOUR_OSS_EMAIL@example.org"
1621
1622# disable pagination for `jj status`, use `delta` for `jj diff` and `jj show`
1623[[--scope]]
1624--when.commands = ["status"]
1625[--scope.ui]
1626paginate = "never"
1627[[--scope]]
1628--when.commands = ["diff", "show"]
1629[--scope.ui]
1630pager = "delta"
1631```
1632
1633#### Using multiple files
1634
1635`--when` can also be used on the top level of a TOML file, which is convenient
1636when splitting your config across multiple files.
1637The behavior of conditions are the same as when using `[[--scope]]` tables.
1638
1639```toml
1640# In $XDG_CONFIG_HOME/jj/config.toml
1641[user]
1642name = "YOUR NAME"
1643email = "YOUR_DEFAULT_EMAIL@example.com"
1644```
1645
1646```toml
1647# In $XDG_CONFIG_HOME/jj/conf.d/work.toml
1648--when.repositories = ["~/the/work/repo"]
1649
1650[user]
1651email = "YOUR_WORK_EMAIL@workplace.com"
1652
1653[revset-aliases]
1654work = "heads(::@ ~ description(exact:''))::"
1655
1656[aliases]
1657wip = ["log", "-r", "work"]
1658```
1659
1660#### Available condition keys
1661
1662* `--when.repositories`: List of paths to match the repository path prefix.
1663
1664 Paths should be absolute. Each path component (directory or file name, drive
1665 letter, etc.) is compared case-sensitively on all platforms. A path starting
1666 with `~` is expanded to the home directory. On Windows, directory separator may
1667 be either `\` or `/`. (Beware that `\` needs escape in double-quoted strings.)
1668
1669 Use `jj root` to see the workspace root directory. Note that the repository path
1670 is in the main workspace if you're using multiple workspaces with `jj
1671 workspace`.
1672
1673
1674* `--when.commands`: List of subcommands to match.
1675
1676 Subcommands are space-separated and matched by prefix.
1677
1678 ```toml
1679 --when.commands = ["file"] # matches `jj file show`, `jj file list`, etc
1680 --when.commands = ["file show"] # matches `jj file show` but *NOT* `jj file list`
1681 --when.commands = ["file", "log"] # matches `jj file` *OR* `jj log` (or subcommand of either)
1682 ```