GitHub - jondeibel/git-workflow · GitHub
Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

gw

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.

gw log output showing three stacks with branches and commits

Table of contents

Why this exists

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.

Alternatives

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.

What gw does

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.

Install

1. Install the GitHub CLI (strongly recommended)

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 login

See cli.github.com for other install methods.

2. Install gw

brew install jondeibel/tap/gw

Or with the install script:

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/jondeibel/git-workflow/releases/latest/download/gw-installer.sh | sh

Or build from source (needs a Rust toolchain):

cargo install --git https://github.com/jondeibel/git-workflow.git

Shell completions

Tab 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 | source

Claude Code

gw 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.

Quick start

# 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 switch

Already have a fat branch? Split it

If 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 split

An 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 main

Plan 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.

Already have branches? Adopt them

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 dev

The 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.

Configuration

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

Commands

Command What it does
gw Show a fast summary of all stacks and branches
gw --pr Include PR status in the stack summary
gw --json Print the fast summary as structured JSON
gw overview [--pr] [--json] Show the explicit fast overview command
gw log Show all stacks with branches and commits
gw log --pr Include PR status from GitHub
gw stack create <name> Create a new stack (prompts for root branch name)
gw stack create <name> --branch <branch> Create a new stack with a specific root branch name
gw stack delete <name> Remove stack metadata (branches stay)
gw stack rename <old> <new> Rename a stack
gw stack list List all stacks
gw branch create <name> Insert a branch after the current branch
gw branch create <name> --after <branch> Insert after another tracked branch
gw branch remove <name> Remove a branch and re-parent children
gw branch rename <old> <new> Rename a branch and update stack metadata
gw adopt <branches...> Adopt existing branches into a stack
gw split Interactively split a branch into a stack
gw split --plan <file> Split using a plan file
gw split --continue Resume after resolving cherry-pick conflicts
gw split --abort Roll back split and delete created branches
gw rebase Propagate rebases to descendants
gw rebase --continue Resume after resolving conflicts
gw rebase --abort Roll back all branches
gw sync Fetch the base and remove merged roots; rebase only when a merge requires it
gw sync --rebase Explicitly rebase stack onto latest base
gw sync --merged <branch> Manually indicate a branch was merged
gw sync --continue Resume a conflicted sync and finish its cleanup
gw sync --abort Restore branches and stack metadata from a conflicted sync
gw push Push the current branch
gw push --stack Push the current branch and all descendants
gw switch [branch] Switch branches interactively or by name
gw status Show current branch commits, working tree, remote, and stack position
gw diff [--stat] Diff the current branch against its stack parent
gw doctor [--fix] Diagnose metadata drift and remove safe stale entries
gw config set-base <branch> Set the default base branch
gw config set-delete-on-merge <bool> Auto-delete local branches on merge
gw config show Show current configuration
gw completions <shell> Generate shell completions (zsh/bash/fish)
gw mcp-setup Configure the MCP server for Claude Code

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages