Skip to content

hydro-project/jj-pr

Repository files navigation

jj-pr

A CLI tool that syncs Jujutsu (jj) bookmarks with GitHub Pull Requests.

Core philosophy:

  • No hidden state — everything is derived at runtime from jj, gh, and change description trailers like PR: #1234
  • jj owns structure — the DAG of changes
  • GitHub owns presentation — PR titles, descriptions, review state
  • Invalid states are first class — ambiguities and inconsistencies are clearly displayed to you and don't halt your work — fix them when you feel like it

Installation

cargo install --git https://github.com/hydro-project/jj-pr --locked
jj-pr util install-aliases

The second line installs jj pr as a subcommand alias and revset aliases (pr(n), pr_root(n)) into your user config.

Dependencies

Requires jj (of course) and gh (GitHub CLI) on your PATH.

To install jj:

cargo install jj-cli --bin jj --locked

To install gh, see here.

Example Workflow

1. Fetch latest changes

jj git fetch

Note: jj-pr does not fetch for you — fetch first to update the local repo to the latest remote state.

2. View the PR DAG

jj pr          # or: jj-pr show
  PR #2804  Ready  mingwei/fix-inline-tasksfix(dfir_rs): defer `_counter` task spawning via `Context::request_task` buffer  PR #2746  Ready  mingwei/sqs-rebase
├─╯  feat(hydro_test): add example of AWS + SQS support
│ ◆  trunk()
├─╯
│     PR #2812*  Draft  mingwei/delete-subgraph-id
│ ├─╮  refactor(dfir_rs): remove SubgraphId, simplify StateLifespan
│ │   PR #2811*  Draft  mingwei/delete-schedule-subgraph-argsPR #2810*  Draft  mingwei/delete-metrics-slotvec
│ ├─╯  refactor(dfir_rs): use slotmap SecondaryMap for metrics  PR #2801*  Draft  mingwei/delete-state-api-usage
╭─┤  refactor(dfir_lang): operators capture local state  PR #2798  Ready  mingwei/delete-intermediate-subgraph
├─╯  refactor(dfir_lang): remove intermediate subgraphs
~  (elided revisions)

PR numbers are clickable hyperlinks in supported terminals. A * after the PR number means there are pending actions for that PR.

If there are ambiguous commits, they show up as warnings with hints.

  PR #2798*  Ready  mingwei/delete-intermediate-subgraphrefactor(dfir_lang): double-buffer defer_tick handoffs, remove intermediate subgraphs [ci-bench]
│   PR #2801*  Draft  mingwei/delete-state-api-usage
├─╯  refactor(dfir_lang): operators capture local state instead of using the state API [ci-bench]
⚠  ambiguous shared between PR #2798, PR #2801 (trailer: PR #2798)
│  (restructure PRs to resolvestack one on the other)
◆  trunk()

3. Diagnose and fix issues

Use jj pr log to see individual commits and their PR associations.

jj pr log          # commits associated with PRs
jj pr log --all    # include commits not in any PR

This is mainly useful for diagnosing ambiguous commits.

  lqxnymrprnpl a6f5cee01ecb PR #2801* Draftrefactor(dfir_lang): add write_tick_end to OperatorWriteOutput, convert fold to inline state  wnkkzpnlkwzt ca43c533b6f6 mingwei/delete-intermediate-subgraph sandbox-d11a1381-9aa3-49f8-abf1-16585609c8a6 PR #2798* Ready
├─╯  Address PR #2798 review comments: extract helper methods, fix stale commentsprupozyltvpz cf49be1b8fc8 ambiguous [PR #2798, PR #2801]
│  refactor(dfir_lang): double-buffer defer_tick handoffs, remove intermediate subgraphsxqxnxvxoyltz 7a71c32b196c ambiguous [PR #2798, PR #2801]
│  test: add test_mutual_defer_tick reproducing as_code topo sort cycletrunk()

Fix ambiguities by restructuring with jj (e.g., jj rebase to stack one PR on the other, or jj describe to edit "PR: #1234" trailers).

4. Sync with GitHub

jj pr sync

This stamps missing trailers, rebases children of merged PRs onto trunk(), abandons merged commits, pushes affected bookmarks, and updates GitHub base branches. Bookmarks with content-conflicted commits are skipped automatically. You'll be prompted before any changes are applied.

  stamp #15 trailer on knynvqkoypsy
  stamp #14 trailer on olqtvyvtvzyq
  push: #15 (my-feature-a)
Apply 3 action(s)? [Y/n]
Stamping PR #15 on knynvqkoypsy
Stamping PR #14 on olqtvyvtvzyq
Pushing 1 bookmark(s): my-feature-a

5. Create a new PR

jj bookmark create my-feature-b  # first create the bookmark in jj
jj pr create my-feature-b        # then create a draft PR for it

The base branch is auto-detected by walking the parent graph.

Pushing my-feature-b
Creating PR: feat: my new features (my-feature-b → main) [draft]
Created PR #16

How It Works

PR membership is determined from the jj DAG structure: each commit belongs to the PR whose bookmark is its nearest descendant. Commits shared between multiple PRs become ambiguous nodes. Trailers (PR: #1234) are appended to change descriptions as a safety net. See the decide() function in pr_dag.rs for the full membership algorithm.

See DESIGN.md for the full design rationale.

Known Limitations

  • Remote hardcoded to origin — fork-based workflows where PRs come from a different remote (e.g., fork) are not yet supported.
  • Multiple PRs sharing the same bookmark — if you have e.g. an open and a closed/merged PR both using the same branch name, only one will be tracked. See the head_to_pr TODO in pr_dag.rs.
  • Some other things, probably

About

Stacked PRs are out, DAG PRs are in!

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors