You break a big feature into multiple PRs. You push the first one for review, get feedback, commit a fix, and now you have to manually rebase every branch after it. When that PR finally gets squash-merged, you have to figure out which commit on dev matches your branch, remove it from the chain, and rebase everything again. Over and over.
gw handles all of that. It tracks the parent-child relationships between your branches, automatically propagates rebases through the chain, and detects squash merges so it can clean up the stack. Your branches are real git branches, your PRs are normal GitHub PRs, and gw just does the tedious coordination between them.
Stack metadata lives in Git's common directory under gw/ and never gets pushed to the remote. Linked worktrees share stacks while keeping active operation state isolated per worktree.
- Why this exists
- Install
- Quick start
- Already have a fat branch? Split it
- Already have branches? Adopt them
- Configuration
- Commands
If you stack branches, you know the pain. You update a branch in the middle of the chain and now you have to rebase everything after it. A PR gets squash-merged and you have to figure out what landed, remove the branch, and rebase the rest. It's all manual, it's error-prone, and it scales with the number of branches in the chain.
There's also the force push problem. Most stacking tools auto-rebase your entire stack onto the latest base branch when you sync. Every branch gets new SHAs and needs a force push. GitHub resets your reviewer's "viewed" state on force pushes, orphans inline comments, and loses the ability to show what changed between review rounds. So you end up making review harder in the name of keeping your stack up to date.
Graphite has both a cloud platform and a local CLI. The local tool works but you end up working around its assumptions about how your workflow should look, and the cloud features keep pulling you toward their platform for the full experience.
ghstack rewrites your branches into synthetic ones for GitHub to display as individual PRs. What's on your machine doesn't match what's on GitHub, and that gets confusing when you're debugging a rebase or a PR diff doesn't look like what you see locally.
git-branchless is close to what you want but the squash merge detection falls over most of the time, which leaves you doing the manual cleanup work anyways. It's also a different mental model entirely, inspired by Mercurial and Phabricator, and if your team already does one-branch-per-PR with squash merges that abstraction doesn't map cleanly.
Your branches are real git branches. Your PRs are normal GitHub PRs. Nothing gets rewritten and nothing gets synced anywhere. gw just handles propagating rebases through the chain, detecting squash merges, and cleaning up the stack.
gw sync does not rebase your stack onto the latest base branch unless a branch was actually merged or you explicitly pass --rebase. Your stack stays pinned, your open PRs don't get force pushed because someone else merged to main, and you control when updates happen.
gw works without it, but you lose squash merge detection, PR status in gw log --pr, and anything that talks to GitHub. That's roughly half the functionality. If you're stacking branches for PR review, you almost certainly want this.
brew install gh
gh auth loginSee cli.github.com for other install methods.
brew install jondeibel/tap/gwOr with the install script:
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/jondeibel/git-workflow/releases/latest/download/gw-installer.sh | shOr build from source (needs a Rust toolchain):
cargo install --git https://github.com/jondeibel/git-workflow.gitTab completion for commands, flags, and branch names:
# zsh (add to ~/.zshrc)
source <(gw completions zsh)
# bash (add to ~/.bashrc)
eval "$(gw completions bash)"
# fish
gw completions fish | sourcegw has a plugin that sets up everything for Claude Code in one shot: the MCP tools for managing stacks and a skill that teaches Claude when to use gw instead of raw git.
/plugin marketplace add jondeibel/git-workflow
/plugin install gw@git-workflow
Restart Claude Code and you're good to go. Claude will automatically use gw for branch management, pushing, rebasing, and syncing.
If you'd rather just get the MCP tools without the plugin system, run gw mcp-setup to write the config to .mcp.json.
# Set your base branch (if not main)
gw config set-base dev
# Create a stack (prompts for root branch name, defaults to stack name)
gw stack create auth
# Or specify the branch name directly
gw stack create auth --branch auth-login
# Do work, commit, then add the next branch
gw branch create auth-tests
# See the fast stack summary
gw
# Get the same fast read model as structured JSON
gw overview --json
# Include commits for every branch
gw log
# Address PR feedback on auth, then propagate rebases
gw rebase
# Push when ready
gw push
# Push this branch and every descendant after a propagated rebase
gw push --stack
# After auth gets squash-merged
gw sync
# Explicitly rebase onto latest base when you're ready
gw sync --rebase
# Switch between branches interactively
gw switchIf you've got a single branch with a bunch of commits that should really be separate PRs, gw split decomposes it into a clean stack:
gw splitAn interactive TUI lets you assign each commit to a named bucket. When you confirm, gw cherry-picks the commits into new branches forming a linear stack. The original branch is kept as a backup.
You can also use a plan file for scripting:
gw split --plan split-plan.txt --base mainPlan file format:
pick <full-sha> auth
pick <full-sha> auth
pick <full-sha> dashboard
pick <full-sha> dashboard
If a cherry-pick hits a conflict, resolve it and run gw split --continue. To undo everything: gw split --abort.
If you've already got a chain of branches you've been managing by hand, you don't need to recreate anything. Just tell gw about them:
gw adopt feature-api feature-tests feature-ui --base devThe argument order defines the stack order, so feature-api becomes the root and feature-ui becomes the leaf. If the branches aren't already rebased into a chain, gw handles that for you and asks before making changes. You can also name the stack explicitly with --name or let it default to the first branch name.
This is the easiest way to migrate onto gw. You keep all your existing branches and commits, gw just starts tracking the relationships between them.
Config lives in .git/gw/config.toml and is per-repo. View it with gw config show.
| Setting | Default | Command | Description |
|---|---|---|---|
default_base |
auto-detected | gw config set-base <branch> |
Base branch for new stacks (e.g. dev, main) |
delete_on_merge |
false |
gw config set-delete-on-merge true |
Delete local branches after sync detects they were merged |
By default, gw sync removes merged branches from the stack but keeps the local git branches around in case you need them. If you'd rather have sync clean everything up automatically:
gw config set-delete-on-merge true
