GitHub Stacks in Jujutsu | Alan Norbauer
GitHub Stacks in Jujutsu
This article is part of a series on<br>Stacks in Jujutsu . Other articles in<br>the series:
GitHub Stacks in Jujutsu
Stacks in Jujutsu
GitHub released its<br>stacked pull request (PR) feature<br>into public preview on 2026-07-30. It mainly targets git users but<br>has documented support for other, git-compatible version control<br>systems like Jujutsu (jj).
This is a primer for using GitHub stacks with jj.
Prerequisites
Install CLIs
You need the git (instructions) and jj (instructions) CLIs installed.
You need the gh GitHub CLI installed (instructions).
Install the stacking extension with<br>gh extension install github/gh-stack
This article was written/tested against:
jj v0.43.0
git v2.52.0
gh v2.96.0
github/gh-stack v0.1.0
Configure jj
I will be re-using some jj aliases and revset aliases we<br>defined in the previous article,<br>Stacks in Jujutsu.
Bonus: Learn jj’s bookmark aliases
GitHub’s Stacked PRs require every PR to have a<br>jj bookmark (git branch), so you’ll be creating a<br>lot of these. UI tools in the future will surely automate<br>creating branch names under the covers, but for now, learn the<br>jj aliases for easily managing bookmarks:
Command<br>Description
jj b l<br>list bookmarks
jj b s -r
Create (or move) a bookmark to the given commit
jj b a
Advance the nearest bookmark to the current commit<br>(like jj tug)
jj b --help<br>Docs with the aliases for all the commands
💡
Did you know that you can improve the default<br>jj bookmark advance (jj b a)<br>behavior by customizing the revset it uses? If you’re on a<br>new, empty commit ABOVE a commit with code, you probably<br>want to advance the bookmark to the code commit below you.<br>You can accomplish this with:
~/.config/jj/config.toml
[revset-aliases."closest_pushable(to)"]
definition = 'heads(::to & mutable() & ~empty() & description(regex:".+"))'
doc = "Closest mutable, non-empty, described commits at or behind to"
[revsets]
bookmark-advance-to = "closest_pushable(@)"
jj makes it super easy to customize behavior by overriding<br>the built-in bookmark-advance-to revset to<br>whatever you want.
Manual Workflows
The gh stack CLI has a bunch of subcommands, most<br>of which manage or consume local tracking of the stack (e.g.<br>init, view). In a jj repo you will<br>only use the few subcommands that manage the remote stack on<br>GitHub (e.g. link, push), as follows:
Create a stack
Either:
Manually create a bookmark for each commit in your<br>stack:
Terminal window
# go to the bottom of your stack using our revset alias
jj bottom --edit
# give commit a name
jj b c
# go to the next commit
jj next --edit
# give commit a name
jj b c
# ... repeat for each commit in stack
jj next --editjj b c "
Or push your commits to GitHub with automatic branch<br>names:
Terminal window
jj git push --change "stack()"
--change takes the place of<br>-r, also takes a revset argument, and:
It uses templates.git_push_bookmark to<br>create a bookmark for each change it pushes.
templates.git_push_bookmark has a<br>default value of<br>"push-" ++ change_id.short(),<br>e.g. push-lltmmqnymopn, and can be<br>customized.
If a change already has a bookmark,<br>jj git push -c will NOT use it. It<br>still uses a new<br>push-lltmmqnymopn bookmark.
If you re-push, it re-uses the existing bookmark.
jj git push -c is smart about not<br>pushing changes with no description, e.g. if you’re<br>on a new empty commit and try to push it with -c<br>you’ll get an error.
Lobste.rs users offby1 and bengesoff<br>shared their custom templates:
[template-aliases]
"slugify(str)" = '''
truncate_end(
65,
str.first_line()
.replace(regex:'[^[[:alnum:]].]', '-')
.replace(regex:'-{2,}', '-')
.replace(regex:'\.{2,}', '.')
.replace(regex:'(^-+|-+$)', '')
10
.lower()
11
12
'''
13
git_push_bookmark = 'slugify(description) ++ "/" ++ change_id.short()'
With this, push-lltmmqnymopn becomes<br>backend-api-changes/lltmmqnymopn.
Upload your bookmarks to GitHub, create draft PRs for<br>each, and link them all into a stack:
Using our<br>stack alias:
Terminal window
gh stack link $(jj sb -r "stack()")
Or, using a jj revision range:
Terminal window
gh stack link $(jj sb -r commit1::commitN)
Or, since jj sb defaults to your<br>substack, you can go to the<br>top<br>(where your stack and substack are the same thing) and<br>then link the entire stack:
Terminal window
jj top --edit
gh stack link $(jj sb)
This last one is probably the easiest to remember.
Whether you used your own bookmarks or<br>jj git push --change "stack()"<br>above, your draft PRs are going to need cleaning up before<br>they’re ready to publish, so head to the GitHub web UI and<br>clean up the titles and PR descriptions for each PR, and<br>then mark them as ready to review.
Add a new commit to the TOP of the<br>stack
Assuming the new commit is change id abc:
Make sure you’re editing your new commit:
Terminal window
jj edit -r abc
Create a bookmark for the new, current commit:
Terminal window
jj b c
If you’ve modified any of the downstack commits,...