git-stk
Git-native stacked branch workflow helper with GitHub and GitLab review integration.
git-stk keeps stacks as ordinary Git branches. Stack parent metadata is stored locally in .git/config as
branch.<name>.stkParent, and GitHub PR bases or GitLab MR target branches can be used to reconstruct that metadata.

Reporting issues
Planned work and known issues are tracked in GitHub issues.
Feel free to report bugs, feedback, feature requests, or ask questions there; just be polite 😉
Install
|
Installers are also attached to GitHub Releases, or install
from crates.io with cargo install git-stk --locked.
Then install the man page and wire up shell completions (idempotent; prompts before touching your shell rc):
New to stacking? git stk guide offers short interactive tours in a disposable sandbox repository:
intro (the whole loop - create a stack, submit, restack, land it), conflicts (resolve and continue an
interrupted restack), and repair (rebuild lost stack metadata). A built-in demo provider stands in for
GitHub, so nothing real is touched and no network is needed; git config stk.provider demo works in any
scratch repo for the same offline playground.
Upgrade an installer-managed copy with:
Shell Completions
git stk setup configures these automatically. Completions are dynamic: the shell asks the binary for
candidates at completion time, so subcommands, flags, and even branch names complete (git stk up <TAB>
offers only the current branch's stack children). The installed binary prints its own registration script,
so completions stay in sync across upgrades:
# bash: add to ~/.bashrc (the guard keeps shell startup quiet if git-stk is removed)
&&
# zsh: write to a directory on your fpath
Elvish, fish, and PowerShell are also supported. The bash and zsh output includes a wrapper so git's own
completion can complete git stk <TAB> in addition to git-stk <TAB>.
Install For Development
After installation, Git can use the binary as a sub-command:
Commands
Git's own narration (rebase progress, switch advice, push chatter) is captured and shown only when a
git command fails; pass -v/--verbose to any command to stream it through instead. Output is colored
when the terminal supports it; pipes and NO_COLOR turn it off.
Local stack metadata:
rename is git branch -m plus stack upkeep: children pointing at the old name are retargeted, and it
warns when an open review still heads the old branch (platforms do not follow local renames).
list prints the stack leaf-first, like a pile sitting on its base, with the trunk labeled:
◉ feature/b
○ feature/a
○ main (trunk)
status and list append hint: lines pointing at the next command when there is one: restack when a
branch is behind its parent, submit when a review base went stale, sync when a review in the stack
merged.
list --markdown prints a shareable summary instead - a status line and the PRs in merge order with
links and states, ready to paste into Slack or a tracking issue:
2 PRs, base `main`, 1 open / 1 merged
1. 2.
Branches without reviews degrade to plain names, so it works before submitting too.
Navigation and re-stacking:
Provider-backed workflows:
sync is the merge-loop one-shot: it fetches the trunk (without leaving your branch), refreshes stack
metadata from open reviews, cleans up merged branches (retargeting children and deleting), moves you off
any branch it deletes, restacks and pushes the remainder, and ends by printing the next PR to merge -
or stack complete when the loop is done. After squash-merging a PR, git stk sync is the only command
you need.
merge merges the review at the bottom of the stack via the provider CLI (strategy from
stk.mergeStrategy; squash by default), confirming first unless -y is passed, then runs the full sync
flow. Landing a stack becomes one git stk merge per PR - or just git stk merge --all, which repeats
merge-and-sync bottom-up until the stack is complete, with a single confirmation up front. With required
checks still running, --auto schedules the merge instead (GitHub --auto, GitLab auto-merge); a merge
that only got scheduled - on GitLab that is the default - skips the sync (and stops --all) with a note
to rerun git stk sync once checks pass. merge --all --wait (or git config stk.mergeWait true) waits
for each review's checks to settle before merging it - on GitHub through gh pr checks --watch's live
table - turning the landing into genuinely one command; a failing check stops the loop, and --no-wait
overrides the config. There is no artificial timeout: the wait runs as long as the platform reports
checks in progress, and ctrl-c is always safe (rerun to resume from the new bottom).
submit --stack and restack operate on the whole stack containing the current branch - walk to the
root, then every branch above it - so it never matters where in the stack you are standing. With
git config stk.submitStack true, bare submit does this by default; --no-stack or naming a branch
submits a single branch.
submit --downstack submits the stack from its bottom through the current branch only, so
work-in-progress branches above you stay local. --draft (or git config stk.submitDraft true) opens
new reviews as drafts; --no-draft overrides the config, and submit --ready flips the submitted
branches' existing drafts to ready for review.
submit --push (or git config stk.pushOnSubmit true) pushes the submitted branches with
-u --force-with-lease before creating or updating reviews, so new branches exist remotely and rebased
ones are updated safely.
submit --stack also maintains a stack overview at the end of every PR/MR description: the full stack as
linked bullets (leaf-first, with a pointer on the PR being viewed) sitting on the trunk, plus a footer
crediting the tool. The overview is a ledger, not a snapshot: entries are styled by status (🟢 open,
🟣 merged, 🔴 closed, the latter two struck through), and merged or closed PRs stay listed even after
their local branches are gone. sync (and therefore merge) and cleanup refresh the overview
mid-loop, so the remaining PRs never show stale state. The section lives between HTML comment markers and self-repairs on
the next update if the markup is hand-edited away.
submit also links issues from branch names: a branch like 123-fix-thing or fix/issue-123 gets a
Closes #123 line in its PR/MR description, so the platform closes the issue when the review merges.
submit --desc <text> (or -d) writes a description block at the top of the review body, above the
managed sections, for the current or named branch only. It sticks across resubmits until changed;
--desc "" removes it.
Upgrading:
upgrade is driven by the install receipt the shell installer writes to ~/.config/git-stk/
(%LOCALAPPDATA%\git-stk on Windows): it records the installed version and where the binary lives, so
upgrade knows what to replace. Copies installed with cargo install have no receipt and should upgrade
through cargo instead.
--head requires a Rust tool-chain, prompts before installing a pre-release build, and intentionally
leaves the receipt's version stale - the HEAD build did not come from a release, so the receipt keeps
pointing at the last one. git stk upgrade --force is the way back onto releases afterwards.
Once a day, the common commands (list, status, sync, submit, merge, restack) check for a newer
release after their work is done - capped at two seconds, silent on any failure or when stderr is not a
terminal - and print a one-line nudge when behind. The check stamps update-check next to the receipt;
git config stk.noUpdateCheck true turns it off.
Configuration
All settings live under [stk] in git config, so the tool's footprint stays separated from git's own.
Everything is optional; defaults shown below:
[stk]
; Review provider: github, gitlab, or demo (offline playground).
; Default: auto-detect from the remote URL.
provider = github
; Remote used for provider detection and pushes. Default: origin.
remote = origin
; Pass --update-refs to git rebase during restack. Default: false.
updateRefs = true
; Force-push (with lease) rebased branches after restack (also the restack
; step inside sync and merge). Default: false.
pushOnRestack = true
; Push branches (-u --force-with-lease) before submitting reviews. Default: false.
pushOnSubmit = true
; Bare `submit` submits the whole stack instead of one branch. Default: false.
submitStack = true
; Strategy for `merge`: squash, rebase, or merge. Default: squash.
mergeStrategy = squash
; `merge --all` waits for each review's checks before merging it. Default: false.
mergeWait = true
; Open new reviews as drafts. Default: false.
submitDraft = true
; Skip the once-a-day check for a newer release. Default: false.
noUpdateCheck = true
The tool also manages per-branch metadata: branch.<name>.stkParent (the stack parent) and
branch.<name>.stkBase (the recorded fork point). These are written by new, adopt, rename, sync, restack,
cleanup, and repair; you normally never touch them by hand.
Branches are the real state; the metadata is just annotation. If it is ever lost or stale, git stk repair
rebuilds it from review bases (when gh/glab is available) and branch ancestry, and verifies recorded
fork points. Anything it cannot resolve safely is reported for a manual git stk adopt.
Inspect everything stk reads or wrote with:
Providers
Provider detection uses stk.provider first, then stk.remote, then origin. GitHub support shells out
to gh. GitLab support shells out to glab. Authenticate those CLIs before using provider commands.
Re-stacking
restack follows the stk.updateRefs config (default false). Use --update-refs or --no-update-refs to
override that for one run. If a rebase conflicts, git-stk records state in .git/stack-state; resolve
conflicts and run git stk continue, or run git stk abort.
git-stk records each branch's fork point in .gitconfig as branch.<name>.stkBase and rebases with
--onto, so only a branch's own commits are replayed. This makes restacking safe after a parent is
squash-merged, rebase-merged, or amended. A missing or stale fork point falls back to a plain rebase.
After a restack, every rebased branch's remote counterpart is stale. Pass --push (or set
git config stk.pushOnRestack true) to force-push (with lease) all rebased branches automatically,
including after a conflicted restack finishes via git stk continue. Without it, restack prints the
exact push command instead. --no-push overrides the config for one run; stk.remote picks the remote
(default origin).
Generated Assets
Shell completions and a man page can be generated with:
Generated files are written under target/generated.
Project Tasks
License
Copyright (c) 2026 Lara Kelley. MIT License. See LICENSE.