Keeping PRs moving
Once the stack is pushed, the work becomes normal review work: open the PRs, let CI run, fix feedback, rebase when main moves, and push again.
The nice part is that my local graph can stay comfortable while GitHub sees ordinary branches.
Open the PR
When I push a completed change with -c, jj creates a generated bookmark for that revision:
jj git push -c @-
On GitHub, the remote usually prints a pull-request link:
$ jj git push -c @-
Creating bookmark push-omrwltwrpyzx for revision omrwltwrpyzx
Changes to push to origin:
Add bookmark push-omrwltwrpyzx to a9f4f7097dd4
remote:
remote: Create a pull request for 'push-omrwltwrpyzx' on GitHub by visiting:
remote: https://github.com/example-org/example-repo/pull/new/push-omrwltwrpyzx
remote:
Most of the time I click that link and create the PR. The generated name is not precious. It only needs to be stable enough for GitHub, CI, and reviewers to talk about the change.
Keep the stack pushed
If I already pushed push-* bookmarks, I usually want to keep them current on the remote:
jj git fetch
jj rebase -b 'bookmarks("push-*")' -o main
jj git push -b 'push-*'
With my aliases:
jj git fetch
jj rs
jj ps
That broad push is intentional. If I opened a generated review branch, I usually want remote CI to keep running on the latest version of it.
If I have not fetched recently and the remote moved, jj git push rejects the update. Good. I fetch, rebase if needed, and push again.
I do not alias this broader command:
jj bookmark list
jj git push --all
--all pushes all bookmarks. It does not create bookmarks for anonymous local changes.
Fix feedback where it belongs
Sometimes feedback belongs in an existing change, even if that change is in the middle of the stack.
I edit that change directly:
jj edit <change>
# make the fix
jj diff
jj git push -b 'push-*'
When that change gets a new commit id, jj rebases its descendants locally. I do not need to manually walk the stack and fix every child PR one by one.
With my aliases, the last command is:
jj ps
This is one of the reasons stacked work feels good in jj. I can fix the commit where the problem belongs, and the rest of the stack follows.
If the review tool can show interdiffs between versions of the same change, this direct-edit style is even nicer. On GitHub I may still add a review commit when I want the response to be very visible, but locally I do not need special ceremony.
When a PR lands
When the bottom PR in a stack lands, I fetch and rebase the remaining stack onto main:
jj git fetch
jj rebase -b 'bookmarks("push-*")' -o main
jj git push -b 'push-*'
With my aliases:
jj git fetch
jj rs
jj ps
If the review tool landed the same commit, or a rebase-style merge that preserves the commit identity, the local graph usually lines up with main naturally.
If GitHub squash-merges a PR, my local copy of that review change may hang around as an empty commit. I do not try to get clever. I look for empties, make sure they are old review commits, and abandon them one by one:
jj empty
jj abandon <change>
The plain revset is:
jj log -r 'empty() & main..'
I still look before abandoning. The point is cleanup, not cleverness.
Clean up old push bookmarks
Generated push-* bookmarks are cheap, but I do not want stale review branches hanging around forever.
I start by looking:
jj bookmark list 'push-*'
If I only want to stop caring about a local bookmark, I forget it:
jj bookmark forget push-abc...
If I want to delete the bookmark and propagate that deletion to the remote:
jj bookmark delete push-abc...
jj git push --deleted
Most of the time this is boring cleanup after the review branch is no longer useful.
Name bookmarks only when it helps
I only name bookmarks when the name buys me something.
Maybe the PRs will live for a week and teammates will say the names out loud:
jj bookmark create auth-0 -r <change-a>
jj bookmark create auth-1 -r <change-b>
jj bookmark create auth-2 -r <change-c>
jj git push -b 'auth-*'
If the branch is just a short-lived review handle, the generated push-* name is good enough.
Keep the graph useful, not clever
The mistakes I try to avoid are usually old habits:
- overengineering bookmark names
- treating bookmarks as the main unit of work instead of commits and changes
- avoiding
jj squashandjj split - turning everything into one large PR instead of a stack
- drifting away from trunk-based development, where reviewed commits land on
mainthrough whatever merge queue or protection the team uses
The goal is not to make the graph impressive. The goal is to keep the work easy to review, easy to reshape, and easy to land.