Queued pull requests for Git alpha

Manage queues of small, reviewable PRs on GitHub

Split a big change into dependent branches. git-queue rebases the whole queue on its base and opens one numbered PR per branch — each targeting the one below.

⚠️ Alpha software — git-queue rewrites branches and force-pushes (with lease) as part of normal operation. Run at your own risk.

$cargo install git-queue crates.io
★ Star on GitHub Read the workflow →
Single Rust binary · git queue … dispatched by Git · MIT
~/repo — git queue
01 · Track order

Branches form a forest under trunk via parent pointers. A queue line is the linear chain from trunk to a leaf.

02 · Requeue on trunk

Change a lower branch and every descendant is rebased onto its new tip — so you never hand-rebase a queue.

03 · One PR per branch

submit pushes bottom-first, targets each PR at the branch below, writes [k/n] titles and a linked queue map.

01

Install

Pick your platform below (we preselect the one you're on). Afterwards, git queue setup installs the man page, shell completion, and an optional git q alias.

install script · recommended
$ curl --proto '=https' --tlsv1.2 -LsSf \
    https://github.com/freshtonic/git-queue/releases/latest/download/git-queue-installer.sh | sh
install script · recommended
> powershell -ExecutionPolicy ByPass -c "irm https://github.com/freshtonic/git-queue/releases/latest/download/git-queue-installer.ps1 | iex"
homebrew
$ brew install freshtonic/git-queue/git-queue
cargo · from crates.io
$ cargo install git-queue
prebuilt binary

Grab an archive for your platform from the latest GitHub release (macOS/Linux x86-64 & arm64 .tar.xz, Windows x86-64 .zip), extract it, and put git-queue on your PATH. Archives and install scripts are built by dist.

Because the binary is named git-queue, Git runs git queue … for you via its standard subcommand mechanism. Then git queue setup installs the man page — afterwards both man git-queue and git queue --help work (Git routes --help to the man page; use git queue help for inline output).

Optional · auto-requeue hooks

Per-repo. Makes plain git commit/amend auto-requeue descendants.

$ git queue setup
$ git queue setup --undo

Without hooks, use git queue commit / git queue amend explicitly.

Requirements
git

commit/sync use git replay (≥ 2.44); amend/reword use git history (≥ 2.55). Other commands work on older git and tell you if a feature is missing.

Authenticated GitHub CLI, for submit/yank. Not needed for local-only queue operations.

// The man page is generated from the CLI definition (clap_mangen) so it never drifts — regenerate with git-queue man --dir <man1-dir>.

02

Getting started

The core workflow. Build a queue branch-by-branch, submit numbered PRs, and keep everything rebased as trunk moves.

core workflow
$ git checkout main
$ git queue create api           # new branch on trunk; make commits
$ git queue create service       # new branch on top of `api`
$ git queue create ui            # new branch on top of `service`

$ git queue status               # view the queue and PR states
$ git queue down / up            # walk the queue (aliases: prev/next)

$ git queue submit               # push all branches + open/update numbered PRs
#  … trunk moves, a teammate pushes, or you amend a lower branch …
$ git queue sync                 # pull remote, requeue onto trunk, push (with lease)
$ git queue submit               # refresh the PRs
the queue, at any point
◉ ui        #12 [OPEN]
◯ service   #11 [OPEN]
◯ api       #10 [OPEN]
┴
  main (trunk)

current branch · tracked · bottom merges into trunk first.

The one distinction that matters most

commit vs amend. Learn this pair and the rest follows.

git queue commit

Adds a new commit — new work — then requeues all descendants onto the new tip.

engine · git replay
git queue amend

Folds staged changes into the branch's tip commit and updates descendants — like commit --amend, queue-aware. The everyday tool for review feedback on a lower PR.

engine · git history fixup
The golden rule — don't hand-rebase a queue.

When you change a branch that has descendants, use the git-queue command for it so the change propagates to every branch above (forks included). Running git rebase on a tracked branch detaches its children.

03

Command reference

The full command set, grouped by task. Filter to find one fast.

/ 23 cmds
Setup
git queue create <name> [--base <b>]Create the next branch of the queue; in namespaced queues a short name becomes queue/<qname>/<name>, like edit's sections.
learn more

Creates the next branch of the queue at the current branch's tip (or at --base <branch>'s tip). In a namespaced queue you give the short name and the branch is created as queue/<name>/<short>, exactly like edit's sections; names containing / are used as-is, and plain-named queues stay plain. Extending inherits the queue name; starting a new one asks (or takes --queue). Branch arguments elsewhere (--base, track --parent, checkout) accept the same short names.

git queue tuiInteractively reshape the current queue line in a full terminal UI — reorder, reword, split and move commits, add branches, with live preview and undo.
learn more

Opens a ratatui interface over the current queue line: the commits and their branches on the left, with optional message and diff panes on the right (each toggleable; drag the dividers to resize). Reorder by picking a commit up with Space, moving the insertion cursor with j/k, and placing it with Enter — the rebase runs once, on place, not per keystroke. Reword, split, squash, add branches and open PR links inline; every change mutates the queue immediately with full undo/redo (u/Ctrl-r). Press ? for the in-app help.

git queue editOpen the whole queue in an editor: each branch is a [name] section over its commits — move the headers to reassign commits. No commit is rewritten. (alias: split)
before — one big branchMmainABCbigafter — branch refs placed at the editor's boundariesMmainAqueue/f/apiBqueue/f/svcCqueue/f/ui
learn more

Opens the whole queue in an editor: every branch is a [name] section header, with its commits listed beneath — the first section is the front of the queue (merges first). The commit sequence is fixed: commits cannot be reordered or deleted, only assigned to branches. Move, rename, add, or remove the header lines: add one to split a branch in two, remove one to dissolve a branch into its neighbours, move one to shift commits between adjacent branches. Branch refs simply move to the new section boundaries; no commit is rewritten, so PRs keep their history. Removed branches are deleted (their commits are covered by the remaining branches). Short header names resolve within the queue; new ones are created as queue/<name>/<short> in namespaced queues. Also works on an untracked branch: it becomes one section over trunk, ready to divide

git queue track [--parent <b>]Adopt the current branch into a queue (parent defaults to trunk); offers to stamp Stable-Commit-Ids onto its commits (asks first — hashes change). --edit then opens the queue editor to divide the commits into multiple branches.
learn more

Adopts an existing branch into a queue: records its parent (trunk by default, or --parent), asks for a queue name when starting a new one, and offers to stamp Stable-Commit-Ids onto the adopted commits — asking first because stamping rewrites them (hashes change; an already-pushed branch will be force-pushed with lease on the next sync). --stamp-ids/--no-stamp-ids decide non-interactively, and --edit continues straight into the queue editor to divide the adopted commits into multiple branches.

git queue untrackForget the current branch's queue metadata.
learn more

Forgets the current branch's queue metadata (parent, anchor, cached PR number, descriptions, membership). The branch and its commits are untouched — this only removes it from the queue's structure.

Navigate & inspect
git queue statusShow the queue tree with PR numbers and states.
learn more

Read-only view of the current line: one row per branch, front of the queue at the bottom, with PR number and state, a left-margin marker on the checked-out branch, and — when a branch holds persisted conflict markers — a warning listing the conflicting files. Markers are detected live, so the warning can never be stale. Output is colourised on a terminal, and PR numbers are clickable links in terminals that render hyperlinks.

git queue logThe status tree with each branch's commits indented beneath it, newest first, each prefixed by its abbreviated Stable-Commit-Id.
learn more

status with one more level of depth: each branch's commits are listed beneath it, newest first, prefixed with the abbreviated Stable-Commit-Id ((no id) for unstamped commits). Those abbreviations are accepted by every command that takes a commit, so log is the natural way to find the argument for move, checkout or reword.

git queue up (next)Check out the child branch.
learn more

Checks out the child branch — one step toward the back of the queue. Errors helpfully at the top or at a fork (listing the children so you can pick one).

git queue down (prev)Check out the parent branch.
learn more

Checks out the parent branch — one step toward the front of the queue.

git queue checkout <commit>Detach HEAD on a queue commit (SHA or Stable-Commit-Id) to edit in place: git commit inserts after it, --amend revises it (id kept); the rest of the queue rebases on top.
git queue checkout A — detached mid-queueMmainAHEADBapiCuigit commit — N inserted after A; the rest rebasedMmainANHEADB′apiC′uigit commit --amend — A revised, id kept; the rest rebasedMmainA′HEADB′apiC′ui
learn more

Detaches HEAD on a commit of the current queue — named by SHA or Stable-Commit-Id — after validating membership and a clean stage. From there, edit and git add, then: plain git commit INSERTS a new commit right after it, while git commit --amend REVISES it (the message carries over, so its Stable-Commit-Id is preserved). Either way everything that followed rebases onto the new commit, automatically with hooks installed or via git queue requeue. HEAD stays detached at the edited commit so you can keep going; git queue checkout <branch> reattaches and ends the session.

Edit
git queue reword [<commit>]Rewrite a commit message and update descendants (defaults to HEAD).
learn more

Rewrites a commit's message — HEAD by default, or any queue commit named by SHA or Stable-Commit-Id — and updates every descendant (engine: git history reword; atomic, aborts cleanly on conflict). Content is untouched.

git queue move <c>[..<c>] --new-parent <c>Move a commit (or inclusive range) elsewhere in the queue — within one PR or across PRs; commits can be named by revision or Stable-Commit-Id. The rest is requeued; conflicts persist as markers.
before — move B --new-parent AMmainAapiCBuiafter — B follows A (joins api); the rest rebasedMmainAB′apiC′ui
learn more

Relocates a commit, or an inclusive <first>..<last> range of consecutive commits, to directly follow --new-parent — reordering within one PR or moving work to a different PR (the commits join the branch segment their new parent belongs to). All arguments accept SHAs or Stable-Commit-Ids. The whole line is rewritten in one git rebase --update-refs pass: branch refs ride along, conflicts are persisted as markers and flagged, and passing the base branch's tip as --new-parent moves commits to the very front of the queue.

git queue describe [-m <text>]Describe the QUEUE; the “About this queue” section of every PR in it.
learn more

Sets the QUEUE's description — the "About this queue" section rendered into every PR of the queue on the next submit/sync. Use it for the narrative that spans the whole queue: what the series achieves and how the pieces fit. Opens $EDITOR without -m; an empty message clears it.

git queue describe-branch [-m <text>]Describe the BRANCH; the “About this branch” section of its PR.
learn more

Sets the current BRANCH's description — the "About this branch" section of its PR. Use it for what this one slice does. A hand-written body on a PR adopted from before the queue existed is imported here automatically rather than overwritten. Opens $EDITOR without -m; an empty message clears it.

git queue name [<name>]Show or set the current queue's name (every queue is named; shown in PR headers).
learn more

Shows the current queue's name, or (re)names it. Every queue is named: the name appears in each PR's header, namespaces the queue's branches (queue/<name>/<branch>), and keys the queue-level description and activity time. Naming records membership on every branch of the line, so branches that don't follow the queue/<name>/… convention still resolve.

git queue lsList every queue in the repo, most recently touched first.
learn more

Lists every queue in the repository, most recently touched first — each with its branch roster, the first line of its description, and a marker for the queue you're on. Activity timestamps update whenever a queue operation runs, so the top entry is what you worked on last. Unnamed queues are listed with instructions to name them.

Without hooks

Only needed when the auto-requeue hooks (git queue setup) aren't installed — with hooks, plain git commit / git commit --amend requeue the branches behind you automatically. (git queue amend also differs on conflict: it aborts atomically instead of persisting markers.)

git queue commit [-m <msg>]New commit on the current branch, then requeue all descendants onto the new tip.
before — staged changes, on apiMmainAapiBuiafter — A rewritten in place; ui followsMmainA′apiB′ui
beforeMmainAapiBuiafter — new commit N on api; ui requeued on topMmainANapiB′ui
learn more

Commits like git commit, then requeues every descendant branch onto the new tip in one atomic pass (engine: git replay), so the branches behind yours never go stale. Stamps a Stable-Commit-Id if the commit-msg hook didn't. With hooks installed, plain git commit behaves the same — this command exists for hookless repositories and for scripting.

git queue amendFold staged changes into the current commit and update every descendant.
learn more

Folds the STAGED changes into the current branch's tip commit and updates every descendant, atomically (engine: git history fixup): if propagating would conflict with a descendant, it aborts and changes nothing — it cannot leave conflict markers. This is the everyday tool for addressing review feedback on a PR that has others queued behind it. The commit message, and with it the Stable-Commit-Id, is preserved.

git queue requeue (restack)Requeue the current branch's descendants onto its tip.
before — api gained N; ui still parented on AMmainANapiBuiafter — ui's commits replayed onto api's tipMmainANapiB′ui
learn more

The repair primitive: makes every descendant of the current branch consistent with its tip — nothing else. No network, no PR edits, no pruning. It is what the hooks run after a plain git commit/--amend (--auto stays silent when there is nothing to do), and the only command that reintegrates a git queue checkout editing session when hooks aren't installed. Reach for it manually after hand-made history surgery (cherry-picks, resets) leaves the branches above you stale.

Remote & PRs
git queue sync [--no-push]Pull remote, drop merged-PR branches (reparenting children), requeue onto the base, push with --force-with-lease.
before — trunk advanced to M2 under the queueM1main@{1}AapiBuiafter — whole queue requeued onto the new trunk tipM2mainA′apiB′ui
learn more

The converge-with-reality command. Fetches with --prune, fast-forwards each queue's base branch, stamps Stable-Commit-Ids onto any queue commits missing them, drops branches whose work has landed (merged PRs, and squash-merges detected by Stable-Commit-Id), pulls genuinely new teammate commits (id correspondence guarantees your own rewrites are never re-applied over themselves), requeues the whole forest onto its bases, pushes everything back with --force-with-lease, and reconciles the PRs of every published queue — opening missing ones, reviving closed ones, refreshing bases, titles and queue maps. --no-push stops after the local requeue. It never pushes a branch that would make GitHub mislabel an open child PR as merged.

git queue submit [--draft] (push)Push the current queue line and open/update its numbered PRs (revives a child PR GitHub closed when its base was deleted).
learn more

Publishes the current line: pushes every branch (front first, so bases exist), opens or revives its numbered PRs, and rewrites each PR's base, [k/n] title and body (queue map + About sections). Adopted PRs keep their hand-written titles (renumbered) and bodies. With the status gate enabled it also posts the red/green merge-order statuses. --draft opens new PRs as drafts.

git queue yankClose every open (non-merged) PR in the current queue.
learn more

Closes every open PR of the current queue without merging — for abandoning or restarting a published queue. Merged PRs, local branches and metadata are all left untouched.

Merge-order
git queue setup [--yes] [--undo]Interactive, per-step setup: the git hooks, the merge-order gate, the Claude Code skill, and an AGENTS.md section when other agents are detected. --undo reverses everything.
learn more

Walks through git-queue's optional integrations, asking permission for each: hooks (auto-requeue after plain commits + Stable-Commit-Id stamping), the advisory merge-order gate, the Claude Code skill (when ~/.claude exists), and a marker-delimited git-queue section in this repo's AGENTS.md when other agent CLIs (Codex, Cursor, Gemini, Windsurf, Copilot) are detected — AGENTS.md being the cross-agent convention they all read. --yes accepts the two repo-local steps for scripts; --undo removes hooks, gate, skill and the AGENTS.md section.

git queue doctorReport whether merge-order signalling is enabled (read-only).
learn more

Read-only diagnosis of merge-order signalling: whether the status gate is enabled, and whether the GitHub CLI is ready. Changes nothing.

// Help quirk: git queue <cmd> --help is intercepted by Git and opens the man page — use git queue help for inline CLI help.

04

Realistic examples

Three worked, end-to-end sessions — each paired with the PR-body queue map git-queue generates.

Build a three-branch queue from trunk, describe the front PR, and open all three cross-linked PRs in one submit.

session
$ git checkout main
$ git queue create types         # front of the queue
#   …edit, then:
$ git queue commit -m "Add domain types"
$ git queue describe -m "Introduces the core domain types shared by the queue."

$ git queue create service
$ git queue commit -m "Add service layer"

$ git queue create api
$ git queue commit -m "Expose HTTP API"

$ git queue status               # see the three-branch queue
$ git queue submit               # opens PRs #1/3, #2/3, #3/3, cross-linked
generated PR-body queue map
freshtonic commented just nowAuthor

📚 Queued PR  ·  2 of 3

Part of a queue. The PRs merge in FIFO order — the numbered order below, #1 first.

🟣 #10 apimain
♻️🟢 #11 serviceapi  👈 this PR
⏳🟢 #12 uiservice

✅ approved · ♻️ changes requested · ⏳ review pending  |  🟣 merged · 🟢 open · ⚫ closed — status as of the last git queue submit.

🥞 Managed by git-queue — do not edit this list by hand.

About this queue

<your git queue describe-branch text>

About this branch

<your git queue describe text>

05

How do I …?

Task-focused recipes — the honest versions, including where a workflow takes more than one command.

Check it out and run git queue track. It records the parent (trunk by default, or --parent <branch>), asks you to name the queue, and offers to stamp Stable-Commit-Ids onto the existing commits — that rewrites their hashes, so it asks first. Then git queue submit opens the PR; a PR that already existed is adopted, keeping its hand-written title and body.
git queue track --edit. After adoption it opens the queue editor: every branch is a [name] section header over its commits — move, rename, or add headers, and each section become one branch/PR, created as queue/<name>/<segment>. No commits are rewritten (branch refs are placed at the existing boundaries), and if no segment reuses the original branch's name, git-queue offers to delete the now-redundant old ref.
There's no single command (yet), but it's short: the queue's top branch already contains the entire history, so nothing needs merging. If PRs are open, git queue yank closes them. Then check out each branch and run git queue untrack to drop its metadata, delete the intermediate branches (git branch -D …), and keep — or git branch -m rename — the top branch. Your commits keep their Stable-Commit-Id trailers, which is harmless.
The branches and PRs are shared through the remote as usual, but queue structure lives in each clone's local git config — so your collaborator rebuilds it once: check out each branch, front to back, and git queue track --parent <its parent> (branches named queue/<name>/… resolve their queue name automatically). From then on you both just git queue sync: pushes are --force-with-lease so nobody clobbers unseen work, and Stable-Commit-Id correspondence means teammate commits are pulled exactly once while your own rewrites are never re-applied over their stale remote copies. If you both rewrite at the same moment, the second pusher's lease fails — they re-run sync and it settles.
Half of it does, and the comparison is instructive. The replay family — commit, sync, requeue, move — always completes: on conflict, the markers are committed into the affected files and the branches are loudly flagged (⚠ conflict markers in status); you resolve and git queue amend. The history family — amend, reword — is atomic instead: on conflict it aborts and changes nothing, because its engine cannot represent a conflicted state. And unlike jj, the markers are plain text in real commits, not first-class conflict objects — git-queue tracks where they are, but resolving one doesn't auto-propagate the resolution anywhere else.
Go to that branch (git queue prev/next, or check it out directly), stage your fix, and run git queue amend — it folds the staged changes into the branch's tip commit and updates every branch behind it, atomically. The commit's message and Stable-Commit-Id are preserved. For a commit that isn't a branch tip, git queue checkout <id> then git commit --amend edits it in place. Finish with git queue submit to refresh the PRs.
git queue log shows every commit with its abbreviated Stable-Commit-Id; use those with git queue move <id> --new-parent <id> (or an inclusive range, <first>..<last>). The moved commits join the branch segment their new parent belongs to — so moving onto a branch's tip commit moves work into that PR — and everything downstream rebases in one pass.
Merge the front PR on GitHub — with git queue setup on, it's the one with the green merge-order ✓ — then run git queue sync: it detects the merge (even a squash-merge, via Stable-Commit-Ids), drops the landed branch, retargets the next PR onto trunk and promotes it to green. Repeat until the queue is empty. Branch auto-deletion on merge is handled, including the PR-closing side effects.
Start it there: git checkout release-1.2 && git queue create fix-a, or from anywhere with git queue create fix-a --base release-1.2. That branch becomes the queue's base — the front PR targets it, and sync keeps the queue rebased on it as it moves. Trunk stays uninvolved.
git queue setup, once per repo. Every open PR then carries a git-queue/merge-order commit status: green ✓ on the front PR, red ✗ ("merge PR #N first", linking to the blocker) on the ones behind it. It's advisory by design — the hard blocks GitHub offers live on base-branch rules, which would also reject git-queue's own pushes — but it makes merging out of order a deliberate act instead of an accident.
Nothing is lost: the commits are in whatever branch you merged into, and GitHub will have marked any PR whose commits became reachable from its base as merged. Run git queue sync — it prunes branches whose work has landed, reparents the survivors, revives any PR GitHub closed, and retargets what remains. git-queue also refuses pushes that would cause the mislabel in the first place (a branch whose tip contains an open child PR's head).
06

FAQ

FIFO — the oldest PR (#1, front of the queue) merges first, then the next. After a PR lands, run git queue sync (detects the merge, reparents branches above onto trunk, rebases them), then git queue submit (retargets PR bases, refreshes the list).
Partly because git-stack was taken. But mostly because the standard nomenclature is wrong: “stacked” PRs don’t pop last-in-first-out — they merge first-in-first-out, front of the line first. That’s a queue, so this tool calls it one. If you already think in stacked PRs, mentally replace “queue” with “stack” and everything maps one-to-one.
Yes — git queue setup (once per repo) enables a git-queue/merge-order commit status on every open PR: green on the bottom mergeable PR, red (“merge PR #N first”) on every PR above it, linking to the PR that must land first. It's advisory — the red ✗ shows in the checks list but doesn't disable the merge button; PRs stay normal, reviewable, non-draft. git queue doctor reports whether it's on.
Every hard block GitHub offers lives on the base branch's rules. With base-chaining, a PR behind the front targets an intermediate queue branch — so a ruleset that gates the merge also rejects git-queue's own pushes to that branch. Draft PRs (the one PR-level block) read socially as “not ready for review.” A commit status is the strongest signal that keeps PRs normal and pushes unblocked.
Depends on the operation. commit/requeue/sync (engine git replay) always finish — on conflict they persist the conflict markers into the committed files and flag the affected branches (shown as ⚠ conflict markers in git queue status). amend/reword (engine git history) are atomic — on conflict they abort and change nothing. When you see markers: go to each flagged branch, search for <<<<<<<, resolve, and git queue amend. Never submit a branch that still has markers.
Handled. If a merge deletes the branch (--delete-branch or repo auto-delete), sync reparents orphaned children onto trunk, and because GitHub closes a PR whose base was deleted, submit revives that child PR — reopening it or opening a fresh one retargeted to trunk.
git queue sync pushes with --force-with-lease, so it won't overwrite commits pushed after your last fetch. If a push is rejected, run sync again to pull their work in first. Use git queue sync --no-push to requeue locally without touching the remote. git-queue never edits trunk's commits.
Entirely in the repo's own git config — nothing outside git. Keys: queue.trunk, queue.remote, branch.<n>.queueParent, branch.<n>.queueParentSha, branch.<n>.queuePr, branch.<n>.queueDescription, queue.gate. Inspect with git config --get-regexp '^branch\..*\.queue'. Conflict-marker state is not stored — status detects <<<<<<< live, so it's never stale.
No — run git queue setup and a post-commit / post-rewrite hook calls git queue requeue for you after plain git commit / git commit --amend. The hooks are guarded against recursion and no-op off a queue.
Git intercepts --help on subcommands and routes it to man git-queue. Run git queue setup so that page exists; otherwise use git queue help or git-queue --help for inline help.
Stable change identity — the same idea as Gerrit's Change-Id. SHAs change on every amend, move and requeue; the message doesn't, so a Stable-Commit-Id: trailer identifies the change across rewrites. git-queue uses it to pull only genuinely new teammate commits during sync (your own rewrites can never conflict with their stale remote copies), and to detect squash-merged work whose SHAs are gone. git queue commit stamps it automatically; git queue setup covers plain git commit; git queue status Commands that take commits (move, reword) accept ids too — full or unique prefix, as git queue log displays them.