jjj

2 min·41 sentences·64.52 cm·28.04.2026

Several jujutsu commands require a revset. When I am hacking on a project, I tend to poke at multiple stacks simultaneously. Many of these stacks use a generated bookmark name.

Identifying them becomes a bit tricky after a while, so I've created a little script with fzf, called jjj (jujutsu jump i suppose), to help me select revsets:

cmd="${1:-show}"

selected=$(
  jj log -r 'all()' --color=always \
    | fzf \
        --min-height=15 \
        --cycle \
        --ansi \
        --prompt "jj $cmd> "
) || exit 0

rev=$(echo "$selected" | awk '{for(i=1;i<=NF;i++) if(length($i)>=7){print $i; exit}}')

jj "$cmd" -r "$rev" "$@"

jjj is exactly like jj, except your revset selection happens inside fzf. Every other arg is just passed straight through.

This opens up jj log in an fzf window:

$ jjj new
jj new>                                                         
  4774/4774                                                     
  @  srppunpn (empty) (no description set) default@ 2h          
> •  qknsmttw knotserver: remove `--allow-empty` from `git am`..
  •  lqvzlslt knotserver: calculate old/new SHAs in merge 2h    
  │ ·  lqvzlslt knotserver: calculate old/new SHAs in merge op..
  ├─╯                                                           
  •  xqwtwoxr appview,blog: add twitter link to footer 20h      
  •  vsrmrrlt knotserver/xrpc: emit `refUpdate` event on `repo..
  •  orsknlnx knotserver/git: allow applying empty patches 20h  
  │ •  xqwtwoxr appview,blog: add twitter link to footer icy/x..
  ├─╯                                                           
  •  nrrqukxt ogre: center align footer items 21h               
  │ ·  llvrxstp appview/timeline: announce vouching in timelin..
  │ ·  uztumlpw appview/pages: add vouch hats 20h               
  │ ·  urzpovyu appview/state: add vouch handlers 20h           
  │ ·  xwysqnul appview/ingester: ingest vouch records 23h      
  │ ·  vrpmqwpu appview/{db,models}: add table and models for ..
  │ ·  oswtmkqr lexicons: add vouch lexicon 1d                  
  ├─╯                                                           
  │ ·  lxrslzty (empty) (no description set) 1d                 
  │ ·  nzqqortv knotserver: limit request size op/nzqqortvpnzz..

You can filter by typing a keyword, say, "vouch" in my case:

$ jjj new
jj new> vouch                                                   
  515/4774                                                      
> │ ·  oswtmkqr lexicons: add vouch lexicon 1d                  
  │ ·  uztumlpw appview/pages: add vouch hats 20h               
  │ ·  urzpovyu appview/state: add vouch handlers 20h           
  │ ·  xwysqnul appview/ingester: ingest vouch records 23h      
  ..u appview/{db,models}: add table and models for vouching 23h
  │ ·  llvrxstp appview/timeline: announce vouching in timelin..
  •  vouqlrtz improve branch-pr ux branch-prs-2 1y              
  •  vxozrylw deps: use fork of chroma 11mo                     
  •  vwknkkmt fix default value for resubmitCheck 1y            
  •  vnxlozsk feature: ability to change default branch for a ..
  ..ysx knotserver: have top level router use cors header func..

Hitting enter simply substitutes that rev into a jj new command:

$ jjj new
# perform a selection in fzf

$ jj new -r oswtmkqr

You could use any jj command really:

$ jjj edit
$ jjj show
$ jjj evolog -p

This is because almost all jj commands accept a revset using the -r flag:

$ jj show --help
  .
  .
  .
Arguments:
  [REVSET]
          Show changes in this revision, compared to its parent(s) [default: @] [aliases: -r]

You can add a preview window, to view the patch for each change, with these flags:

  --preview 'echo {} | awk "{for(i=1;i<=NF;i++) if(length(\$i)>=7){print \$i; exit}}" | xargs jj show --color=always ' \
  --preview-window=right:60% \

Note that the jjj is tuned to my log template. It requires that your log output be one-line per change and that you print a full 7+ character change-id in the log. Mutate as required.