Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

My small jj config

The workflow so far works with plain jj.

This is the small config I put on top. It is not a framework. It is just enough personal vocabulary to make the common moves feel natural.

[ui]
paginate = "never"
default-command = "log"
diff-formatter = ["difft", "--color=always", "$left", "$right"]

[user]
name = "Your Name"
email = "you@example.com"
username = "your-handle"

[aliases]
tug = ["bookmark", "advance"]

stack = ["log", "-r", "stack"]
bough = ["log", "-r", "bough"]
wip = ["log", "-r", "wip"]
private = ["log", "-r", "private"]
empty = ["log", "-r", "empty_stack"]

rs = ["rebase", "-b", "bookmarks('push-*')", "-o", "trunk()"]
ps = ["git", "push", "-b", "push-*"]

[revsets]
bookmark-advance-to = "@-"

[revset-aliases]
'stack' = 'trunk()..@'

'bough(x, m)' = 'descendants(ancestors(x) ~ ancestors(m))'
'bough(x)' = 'bough(x, trunk())'
'bough' = 'bough(@)'

'wip' = 'subject("wip:*") & stack'
'private' = 'subject("private:*") & stack'
'empty_stack' = 'empty() & stack ~ @'

[git]
private-commits = "subject('private:*')"

The difft line is optional. If you do not use difft, delete it and keep jj’s default diff.

The plain examples in this book use main. In my config aliases I use trunk() because it follows whatever the repo considers trunk.

Why I like this config

jj by itself becomes my graph view:

jj

That is just default-command = "log". I like it because I look at the graph constantly.

Without my config, I spell the bookmark move explicitly:

jj bookmark advance --to @-

The same explicit command with jj’s short command names is:

jj b a --to @-

With this config:

[revsets]
bookmark-advance-to = "@-"

I can drop --to @-:

jj b a

My alias is:

jj tug

So jj tug is just my name for jj b a with bookmark-advance-to = "@-" set. Since @- is the parent of @, right after jj commit it points at the change I just committed, so tugging moves the bookmark there.

The plain stack maintenance loop is:

jj git fetch
jj rebase -b 'bookmarks("push-*")' -o 'trunk()'
jj git push -b 'push-*'

My aliases are:

jj git fetch
jj rs
jj ps

That is intentionally broad. If I already pushed a generated push-* bookmark for review, I usually want to keep it current on the remote.

I do not alias jj git push --all. It is useful, but it is broad enough that I want to type it deliberately:

jj bookmark list
jj git push --all

--all means all bookmarks. It does not create bookmarks for anonymous local changes.

Logs I actually use

These are just ways to ask “what am I looking at?”

The plain commands are:

jj log -r 'trunk()..@'
jj log -r 'subject("wip:*") & trunk()..@'
jj log -r 'subject("private:*") & trunk()..@'
jj log -r 'empty() & trunk()..@ ~ @'

My aliases are:

jj stack
jj wip
jj private
jj empty

stack is the normal view: work from trunk to @.

For bough, the plain revset is not something I want to type by hand. That is exactly why it is an alias:

jj bough

bough is wider. It is useful when I have side experiments or local integration commits around the same branch of thought. I use it for looking around, not as a push target.

wip and private are message-prefix searches inside the current stack.

empty shows empty changes in the stack, excluding the current empty @. That is handy after squash merges or cleanup.

That is the whole point of the config: keep the actual workflow visible, but make the moves I do all day feel good to type.

Accounts and SSH

If I need different author identities per folder, I use config scopes:

[[--scope]]
--when.repositories = ["~/dev/work"]
[--scope.user]
email = "you@work.example"
username = "work-handle"

[[--scope]]
--when.repositories = ["~/dev/personal"]
[--scope.user]
email = "you@personal.example"
username = "personal-handle"

SSH keys stay in ~/.ssh/config, because jj uses Git for remote operations:

Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_oss
  IdentitiesOnly yes

Host gerrit.example.com
  HostName gerrit.example.com
  User my-google-account
  IdentityFile ~/.ssh/id_ed25519_google
  IdentitiesOnly yes

If two accounts live on the same host, I use host aliases like github-oss and github-work, then put that alias in the remote URL.