GitHub Stacks in Jujutsu

birdculture1 pts0 comments

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,...

stack commit github bookmark push stacks

Related Articles