A git queue subcommand that manages queues — ordered series of branches
where branch N depends on branch N-1 — and the creation of numbered pull
requests from those branches.
Every tracked branch stores a pointer to its parent branch. Branches thus form
a forest rooted at trunk. A queue line is the linear chain from trunk up
to a leaf. This is the model used by Graphite/gt; it is chosen over an
explicit "named ordered list of branches" because:
- The parent relationship is the dependency, expressed once per branch.
- Branching a queue (two children on one branch) falls out naturally.
- Merge order is implied: FIFO along a line, front (oldest) first.
Trade-off: a "queue" is not a first-class named object, so operations pick the
line running through the current branch. Forks are supported for tracking and
status, but submit/numbering operate on a single line and warn at a fork.
No sidecar files, no external database — state lives in the repo's git config so
it is versioned per-clone and inspectable with plain git config:
| Key | Meaning |
|---|---|
queue.trunk |
Trunk branch. |
queue.remote |
Remote (default origin). |
branch.<n>.queueParent |
Parent branch. |
branch.<n>.queueParentSha |
Parent tip at last (re)base — the rebase anchor. |
branch.<n>.queuePr |
Cached PR number. |
The tool drives the git and gh executables rather than linking a library.
Rebase and conflict behavior is then exactly what the user gets by hand, output
(progress, conflict markers) is native, and the dependency surface is tiny. The
cost — parsing text/JSON output — is small and localized in git.rs / gh.rs.
git fetch <remote>(non-fatal: falls back to local trunk when offline).- New trunk tip =
<remote>/<trunk>if present, else local<trunk>. - Visit tracked branches in topological order (parents before children).
- For each branch, rebase only its own commits onto the current parent tip:
git rebase --onto <parent-tip> <stored-anchor> <branch>, then update the stored anchor to the new parent tip. - A conflict stops the run with instructions; re-running resumes cleanly because already-requeueed branches are detected as up to date (anchor == parent tip) and skipped.
Using the stored anchor (not merge-base) is what keeps a parent's commits from
being replayed onto the child.
For queue line main ← A ← B ← C:
- Base chaining: PR(A)→
main, PR(B)→A, PR(C)→B. Each PR shows only its own diff. - Push order: front-first, so every PR's base branch already exists on the remote (force-with-lease, sets upstream).
- Two passes: pass 1 pushes and creates-or-finds each PR to learn its
number; pass 2 writes the
[k/n]title, correct base, and a shared navigation block (BEGIN/END-delimited so a user's own body text is preserved) linking every PR in the line and marking the current one. - Refuses to open a PR for a branch with no commits beyond its base.
init, create, status (ls/list), track, untrack, up/next,
down/prev, sync, submit (push).
| File | Responsibility |
|---|---|
main.rs |
Clap CLI definition and dispatch. |
git.rs |
Process wrappers over git. |
gh.rs |
Process wrappers over gh (PR list/create/edit). |
meta.rs |
Read/write queue metadata in git config. |
queue.rs |
Domain model: chains, forks, topological order. |
render.rs |
Pure rendering: status tree, PR titles, nav block (unit-tested). |
commands.rs |
One function per subcommand. |
- Unit tests (
render.rs): title prefixing, body-block compose/replace — pure, no git. - Integration tests (
tests/integration.rs): run the built binary against throwaway repos (tempdirs) coveringinit,create, navigation,track,untrack, and the queuedsync.submitis excluded because it needs an authenticatedghand a GitHub remote.
submitoperates on one linear line; forked queues warn and submit one line.syncconflict recovery is resume-by-re-run, not an explicit--continue.- No
git queue merge/ land automation yet (rely on GitHub merge queue). - Forge is GitHub via
ghonly; aForgetrait could add GitLab later.