After procrastinating for ~6 months on using jj due to their initial lack of support for Gerrit, I’ve finally made the switch recently. I was expecting more friction from adopting a new VCS, but the experience has been really wonderful!

Below are some things that I’ve found particularly useful when using jj for Go development.

Configs

Configs in my ~/.jjconfig.toml.

Default remote and branch

All Go repositories have the same remote and branch name. So, setting it globally is pretty nice to reduce the flags I have to pass when using jj gerrit upload.

[gerrit]
default-remote = "origin"
default-remote-branch = "master"

Syncing to HEAD

Pulling in remote changes in jj is unfortunately a two-step process for a git repo. As far as I can tell, users usually just run jj git fetch followed by jj rebase.

When working with Gerrit, fetching and rebasing against your submitted commits also would leave empty commits that you’d usually then have to abandon by default.

I now have a little alias to sync to HEAD cleanly:

[aliases]
sync = [
  "util", "exec", "--", "bash", "-c", '''
  jj git fetch
  jj rebase --skip-emptied -b "mutable()" --onto master@origin
  ''', "--"
]

Add issue number automatically

When solving for a particular issue, I like to break up my changes into several consecutive commits. Having to manually add For #NNNNN or Updates #NNNNN to each commit message gets old really fast. So, I modified my template to automatically do that when using jj commit or jj describe.

[templates]
draft_commit_description = '''
  if(self.description() == "" && !parents.first().immutable(),
    "TODO: Subject\n\n" ++
    "TODO: BODY\n\n" ++
    parents.first().description().trim().lines().filter(|s|
      s.contains("#") && (s.contains("For") || s.contains("Updates"))
    ).join("\n") ++
    "\nJJ: ignore-rest\n" ++
    diff.git(),
    self.description()
  )
'''

Commands

Commands that I keep reverse-searching for in my zsh history, but is small / adjusted often enough that I didn’t make an alias for them.

Abandon all empty changes

jj abandon 'empty() & mutable()'

Uploading changes to Gerrit

jj gerrit upload -l=Commit-Queue --wip
jj gerrit upload -l=Commit-Queue -l=Auto-Submit --ready --reviewer $REVIEWER

LLM instructions

jj-related instructions that I give to my LLMs in their system prompts.

Flags

I like using LLM to review my commits before I send them off to teammates. I tell my LLMs to always use the --git and --config 'ui.paginate="never"' flags when viewing my commit diffs and logs.

By default, jj shows + and - diffs in one line with different colors when they are small, and LLM often gets confused due to not seeing the color. --git flag ensures that all + and - diffs are shown on separate lines.

In a similar vein, I’ve seen some LLMs getting stuck because jj log output an interactive page. --config 'ui.paginate="never"' prevents this problem.