A small Bun/TypeScript CLI that turns a Jujutsu commit stack into GitHub pull requests. For each mutable commit in a revset, it ensures there is a PR from the commit’s head branch to its inferred base branch, creating or updating PRs as needed, and prints a concise stack summary.
- Pushes changes for the given revset:
jj git push -c all:<revset>. - Lists all mutable commits in the revset:
jj log --no-graph -r "<revset> & mutable()" -T change_id. - For each commit:
- Determines the head branch via
jj bookmark list -r <commit_id> -T name.- If none exists, creates
feature/<cid8>withjj bookmark setand pushes it.
- If none exists, creates
- Determines an appropriate base branch via
jj bookmark list -r closest_bookmark(<commit_id>-) -T name(falls back tomain). - Checks for an existing PR with
gh pr list --head <head> --json number,title,baseRefName.- If no PR exists, creates a draft PR with
gh pr create --head <head> --base <base> --draft --fill. - If a PR exists and its base differs, updates base with
gh pr edit <num> --base <base>.
- If no PR exists, creates a draft PR with
- Determines the head branch via
- Prints a summary line for each commit.
Example output:
PR Stack:
---------
# 123 Add widget feature/abc12345 -> main (new)
# 124 Polish copy feature/def67890 -> feature/abc12345 (updated)
Status meanings:
new: PR was created for this head/base.updated: existing PR’s base was changed to the inferred base.no change: existing PR already matched the inferred base.
- Bun v1.2+ (runtime)
- Jujutsu (
jj) configured for the repo - GitHub CLI (
gh) authenticated to the repo’s GitHub remote
This tool performs writes:
jj git pushto your configured Git remote(s)gh pr create/gh pr editto GitHub
Run it only in repos where you intend to create or update PRs.
Install deps:
bun installRun against the current commit (@) or any revset:
bun run index.ts # defaults to "@"
bun run index.ts "@" # explicit
bun run index.ts "@ | ancestors(HEAD)"The CLI prints the PR stack and exits with Bun’s shell exit codes if any command fails.
These environment variables can override the external binaries (primarily for testing or custom setups):
JJ_BIN: path (or command) for thejjbinary. Example:JJ_BIN="/usr/local/bin/jj"orJJ_BIN="bun test/stubs/jj.ts".GH_BIN: path (or command) for theghbinary. Example:GH_BIN="/usr/local/bin/gh"orGH_BIN="bun test/stubs/gh.ts".
Note: If you pass a multi-word command (e.g. bun test/stubs/jj.ts), it is respected as-is by the runner.
This repository includes a hermetic E2E test suite that stubs gh and jj so tests can run without network or credentials.
- Stubs live in
test/stubs/gh.tsandtest/stubs/jj.ts. - Tests run the CLI end-to-end, but with
GH_BIN/JJ_BINpointing to those stubs. - Stub state is stored per-test in temporary JSON files via
GH_STUB_STATE/JJ_STUB_STATE.
Run tests:
bun testWhat the tests cover:
- Creating a PR for a commit with no head bookmark (auto-create
feature/<cid8>) - Updating the base branch for an existing PR when the inferred base changes
- The inferred base uses
closest_bookmark(<commit_id>-)and falls back tomain. If your workflow uses a different convention, you may want to customize that logic. gh pr create --draft --fillrelies on GitHub’s generated title/description; you can modifyindex.tsto customize titles or bodies.- See
KNOWN_BUGS.mdfor open issues and rough edges around thejj pr <revset>flow.
- Main entry:
index.ts - Simple assertion helper:
lib/assert.ts - Tests:
test/e2e.test.ts, stubs undertest/stubs/
Contributions to improve the PR inference or add more test scenarios are welcome.