Last active
June 18, 2025 03:31
-
-
Save thoughtpolice/8f2fd36ae17cd11b8e7bd93a70e31ad6 to your computer and use it in GitHub Desktop.
my jujutsu config
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## ------------------------------------------------------------------------------------------------ | |
# Schema published automatically on the website. This allows TOML language | |
# servers to autocomplete and show documentation for the entries below. | |
# | |
# I use the prerelease version as my builds on my machines are often from trunk. | |
"$schema" = "https://jj-vcs.github.io/jj/prerelease/config-schema.json" | |
## ------------------------------------------------------------------------------------------------ | |
## ---- Basic settings | |
[user] | |
name = "Austin Seipp" | |
# email has to be configured per-repository | |
[ui] | |
default-command = "log" | |
diff-formatter = "difft" | |
merge-editor = "vscode" | |
log-synthetic-elided-nodes = true | |
graph.style = "square" | |
should-sign-off = true | |
[merge-tools.difft] | |
program = "difft" | |
diff-args = ["--color=always", "$left", "$right"] | |
[git] | |
auto-local-bookmark = false | |
push-bookmark-prefix = "aseipp/push-" | |
private-commits = 'blacklist()' | |
#colocate = true | |
write-change-id-header = true | |
[snapshot] | |
# FIXME (upstream): why isn't this on by default? | |
auto-update-stale = true | |
[gerrit] | |
enabled = false | |
url = 'http://example.com' | |
## ------------------------------------------------------------------------------------------------ | |
## ---- Scope settings | |
[[--scope]] | |
--when.repositories = ["~/src"] | |
[--scope.user] | |
email = "[email protected]" | |
[[--scope]] | |
--when.repositories = ["~/work/ersc"] | |
[--scope.user] | |
email = "[email protected]" | |
## ------------------------------------------------------------------------------------------------ | |
## ---- Revsets & filesets | |
[revsets] | |
# By default, show all my current stacks of work. | |
log = 'stack(mine() | @) | trunk() | @' | |
# Prioritize megamerges so that they always appear on the 'left-most' side of | |
# the graph. | |
log-graph-prioritize = 'coalesce(megamerge(), trunk())' | |
[revset-aliases] | |
# Useful on Windows. Technically conflicts with any bookmark/tag named 'at', but | |
# seems OK... | |
'at' = '@' | |
# FIXME (upstream): should this be upstream? | |
'user(x)' = 'author(x) | committer(x)' | |
'mine()' = 'user("[email protected]") | user("[email protected]")' | |
# By default, show the repo trunk, the remote bookmarks, and all remote tags. We | |
# don't want to change these in most cases, but in some repos it's useful. | |
'immutable_heads()' = 'present(trunk()) | untracked_remote_bookmarks() | tags()' | |
# Useful to ignore this, in many repos. For repos like `jj` these are | |
# consistently populated with a bunch of auto-generated commits, so ignoring it | |
# is often nice. | |
'gh_pages()' = 'ancestors(remote_bookmarks(exact:"gh-pages"))' | |
# Private and WIP commits that should never be pushed anywhere. Often part of | |
# work-in-progress merge stacks. | |
'wip()' = 'description(glob:"wip:*")' | |
'private()' = 'description(glob:"private:*")' | |
'blacklist()' = 'wip() | private()' | |
# stack(x, n) is the set of mutable commits reachable from 'x', with 'n' | |
# parents. 'n' is often useful to customize the display and return set for | |
# certain operations. 'x' can be used to target the set of 'roots' to traverse, | |
# e.g. @ is the current stack. | |
'stack()' = 'stack(@)' | |
'stack(x)' = 'stack(x, 2)' | |
'stack(x, n)' = 'ancestors(reachable(x, mutable()), n)' | |
# The current set of "open" works. It is defined as: all stacks that are | |
# reachable from my working copy, or any other commit I wrote. | |
# n = 1, meaning that nothing from `trunk()` is included, so all resulting | |
# commits are mutable by definition. | |
'open()' = 'stack(mine() | @, 1)' | |
# the set of 'ready()' commits. defined as the set of open commits, but nothing | |
# that is blacklisted or any of their children. | |
# | |
# often used with gerrit, which you can use to submit whole stacks at once: | |
# | |
# - jj gerrit send -r 'ready()' --dry-run | |
'ready()' = 'open() ~ descendants(blacklist())' | |
# Find the megamerge. Mostly useful in combination with other aliases, primarily | |
# 'sandwich'. Normally when there's only one megamerge, sandwich works perfectly | |
# fine and is basically "magic". However, there are more complex cases; consider | |
# something like this which represents a forked repository of an upstream: | |
# | |
# | |
# ----> P1 ... Pn -----------\ | |
# / \ | |
# /---> X --\ (main) \ | |
# B M1 --> T1 ... Tn --> M2 --> @ | |
# \---> Y --/ | |
# | |
# X and Y are typical features on top of base B, combined inside megamerge M1. | |
# However, we may want changes T1...Tn to go on top of M1, because (in my case) | |
# they're custom and will never go upstream, but are correctly published as part | |
# of the fork; Tn is where the main bookmark points. Finally, we have changes P1 | |
# ... Pn which are private and should never be pushed upstream at all. | |
# | |
# In this case, sandwich will fail because 'reachable(stack(), merges())' will | |
# resolve to {M1, M2}, which is an invalid argument for 'rebase -B' due to not | |
# having an all: prefix modifier. So to handle that case, we allow the user | |
# to either specify the merge via literal tag "megamerge". In this case if we | |
# 'bookmark set megamerge -r M1' then sandwich will always work correctly. | |
'megamerge()' = 'coalesce(present(megamerge), reachable(stack(), merges()))' | |
## ------------------------------------------------------------------------------------------------ | |
## ---- UX/UI Configuration | |
[aliases] | |
# "New Trunk" | |
nt = ["new", "trunk()"] | |
# Shorter (and thus better) names, IMO | |
credit = ["file", "annotate"] | |
cat = ["file", "show"] | |
# TODO FIXME (aseipp): is this a better name? | |
streamline = ["simplify-parents"] | |
# Find the closest ancestor with a bookmark pointing at it, and move it to the | |
# parent of the working copy. | |
tug = ["bookmark", "move", "--from", "heads(::@- & bookmarks())", "--to", "@-"] | |
# Get all open stacks of work | |
open = ["log", "-r", "open()"] | |
# Retrunk revisions. Typically as `jj retrunk -s ...` -- notably can be used | |
# with open: | |
# - jj retrunk -s 'all:roots(open())' | |
retrunk = ["rebase", "-d", "trunk()"] | |
# Retrunk the current stack of work. | |
reheat = ["rebase", "-d", "trunk()", "-s", "all:roots(trunk()..stack(@))"] | |
# 'jj sandwich xyz' to move xyz into the megamerge in parallel to everything | |
# else. See notes on 'megamerge()' above for how it's resolved. | |
sandwich = [ "rebase", "-B", "megamerge()", "-A", "trunk()", "-r"] | |
# Take content from any change, and move it into @. The inverse of 'eject' | |
# - jj consume xyz path/to/file` | |
consume = ["squash", "--into", "@", "--from"] | |
# Eject content from @ into any other change. The inverse of 'consume' | |
# - jj eject xyz --interactive | |
eject = ["squash", "--from", "@", "--into"] | |
# EXPERIMENTAL: ultra-generalized command that subsumes both 'diff' and 'show' | |
examine = ["log", "-T", "builtin_log_detailed", "-p", "-r"] | |
[colors] | |
# Base customizations | |
"normal change_id" = { bold = true, fg = "magenta" } | |
"immutable change_id" = { bold = false, fg = "bright cyan" } | |
# Used by log node template | |
"node" = { bold = true } | |
"node elided" = { fg = "bright black" } | |
"node working_copy" = { fg = "green" } | |
"node conflict" = { fg = "red" } | |
"node immutable" = { fg = "bright cyan" } | |
"node wip" = { fg = "yellow" } | |
"node normal" = { bold = false } | |
# Used in other various templates | |
"text link" = { bold = true, fg = "magenta" } | |
"text warning" = { bold = true, fg = "red" } | |
[template-aliases] | |
# Code to hyperlink something for the terminal. | |
# FIXME (upstream): should this go upstream? | |
'hyperlink(url, text)' = ''' | |
concat( | |
raw_escape_sequence("\e]8;;" ++ url ++ "\e\\"), | |
label("text link", text), | |
raw_escape_sequence("\e]8;;\e\\"), | |
) | |
''' | |
# Basic customizations. | |
'format_short_signature(signature)' = '"<" ++ if(signature.email(), signature.email(), label("text warning", "NO EMAIL")) ++ ">"' | |
'format_timestamp(ts)' = '"[" ++ ts.ago() ++ "]"' | |
'render_bookmarks(commit)' = ''' | |
commit.bookmarks().map(|b| | |
if(b.remote(), | |
b, | |
hyperlink(gh_pr_base() ++ "/tree/" ++ b.name(), b), | |
) | |
) | |
''' | |
# Commit header. This includes code to automatically link to Gerrit code reviews | |
# for matching commits in a very basic way. | |
'format_short_commit_header(commit)' = '''separate(" ", | |
format_short_change_id_with_hidden_and_divergent_info(commit), | |
format_short_signature(commit.author()), | |
format_timestamp(commit_timestamp(commit)), | |
render_bookmarks(commit), | |
commit.tags(), | |
commit.working_copies(), | |
format_short_commit_id(commit.commit_id()), | |
if(has_ghpr_url(commit.description()), | |
"[" ++ | |
hyperlink( | |
get_ghpr_url(commit.description()), | |
"GH: #1234" | |
) ++ "]" | |
), | |
if(has_change_id(commit.description()), | |
"[" ++ | |
hyperlink( | |
config("gerrit.url").as_string() ++ "/q/" ++ gerrit_id(change_id), | |
"CR: " ++ gerrit_id(change_id, 10) | |
) ++ "]" | |
), | |
if(commit.git_head(), label("git_head", "git_head()")), | |
if(commit.conflict(), label("conflict", "conflict")), | |
)''' | |
'has_ghpr_url(s)' = 's.contains("GH-PR: ")' | |
'get_ghpr_url(s)' = ''' | |
s.lines().filter(|l| l.starts_with("GH-PR: ")).map(|l| l.remove_prefix("GH-PR: ")).join("") | |
''' | |
# 6a6a636c is hex("jjcl"). | |
'has_change_id(s)' = 's.contains("Change-Id: I")' | |
'gerrit_id(c, n)' = '"I" ++ raw_escape_sequence(c.normal_hex().substr(0, n)) ++ if(n > 31, "6a6a636c")' | |
'gerrit_id(c)' = 'gerrit_id(c, 31)' | |
'gh_pr_base()' = '"undefined"' | |
[templates] | |
# Improve the default representation in the log. This colors | |
# and changes the node icon for each. | |
op_log_node = 'if(current_operation, "@", "◉")' | |
log_node = ''' | |
label("node", | |
coalesce( | |
if(!self, label("elided", "⇋")), | |
if(current_working_copy, label("working_copy", "◉")), | |
if(conflict, label("conflict", "x")), | |
if(immutable, label("immutable", "◆")), | |
if(description.starts_with("wip: "), label("wip", "!")), | |
label("normal", "○") | |
) | |
) | |
''' | |
# Draft commit description. Includes: | |
# - Change-Id fields for Gerrit, and | |
# - Signed-off-by lines | |
draft_commit_description = ''' | |
concat( | |
description, | |
"\n", | |
if( | |
config("gerrit.enabled").as_boolean() && !has_change_id(description), | |
"\nChange-Id: I" ++ gerrit_id(change_id), | |
), | |
if( | |
config("ui.should-sign-off").as_boolean() && !description.contains("Signed-off-by: " ++ author.name()), | |
"\nSigned-off-by: " ++ author.name() ++ " <" ++ author.email() ++ ">", | |
), | |
"\n", | |
surround( | |
"\nJJ: This commit contains the following changes:\n", "", | |
indent("JJ: ", diff.summary()), | |
), | |
) | |
''' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
for gerrit change-id generation: this example isn't working for me as of revision 19, see alternative solution
https://github.com/avamsi/dotfiles/blob/28a0e40439de56fe179a1b0e705727c85e075464/.jjconfig.toml#L146-L156