Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

git-gud (gg) is a stacked-diffs CLI for GitHub and GitLab.

It helps you split large changes into a sequence of small commits that reviewers can understand quickly. In git-gud, each commit in your stack maps to its own PR/MR, and dependencies are wired automatically.

Why this workflow exists

Stacked diffs solve a common problem: features are often too large for a single review, but splitting work manually into many dependent branches is painful.

With git-gud, you can:

  • Keep reviews small and focused
  • Keep moving while earlier changes are in review
  • Preserve clean, logical commit history
  • Land big projects incrementally without long-lived feature branches

Learn more about stacked diffs

Provider support

git-gud supports:

  • GitHub through gh
  • GitLab through glab

Provider selection is auto-detected from your remote URL (github.com / gitlab.com). For self-hosted instances, run gg setup and select the provider explicitly.

Getting Started

Installation

Homebrew (macOS/Linux)

brew install mrmans0n/tap/gg-stack

crates.io

cargo install gg-stack

From source

cargo install --path .

Prerequisites

Before using git-gud, make sure you have:

Authentication

Authenticate with your provider CLI first:

# GitHub
gh auth login

# GitLab
glab auth login

If authentication is missing, gg sync and gg land cannot create or merge PRs/MRs.

Initial setup

After installing, run the setup wizard in any git repository to configure git-gud:

gg setup

This interactively sets your base branch, username for branch naming, provider (auto-detected for github.com/gitlab.com), and optional lint commands. Configuration is stored per-repo in .git/gg/config.json.

Tip: gg setup is optional — git-gud auto-detects sensible defaults. But it’s useful for setting lint commands, customizing your username prefix, or configuring self-hosted GitHub Enterprise / GitLab instances.

See Configuration for all available options.

Quick start: first stack in 2 minutes

# 1) Create a stack
gg co my-feature

# 2) Commit in small slices
git add . && git commit -m "Add data model"
git add . && git commit -m "Add API endpoint"
git add . && git commit -m "Add UI"

# 3) Inspect current stack
gg ls

# 4) Push branches and create PRs/MRs
gg sync --draft

# 5) Navigate to edit an earlier commit
gg mv 1
# ...make changes...
gg sc

# 6) Re-sync after changes
gg sync

# 7) Land approved changes
gg land --all

# 8) Clean merged stack
gg clean

For a full walkthrough with expected outputs and decision points, see Your First Stack.

Core Concepts

Stacked diffs in one sentence

A stack is a series of commits where each commit is reviewed as its own PR/MR, and each PR/MR depends on the previous one.

The git-gud model: one commit = one PR/MR

In git-gud, each commit is an “entry” in the stack:

  • Entry 1 targets your base branch (for example, main)
  • Entry 2 targets entry 1’s branch
  • Entry 3 targets entry 2’s branch
  • …and so on

That gives reviewers small units, while preserving execution order.

GG-IDs

Each stack commit carries a stable trailer, for example:

GG-ID: c-abc1234

Why GG-IDs matter:

  • They keep commit-to-PR/MR mappings stable across rebases
  • They let git-gud identify entries by a durable ID (not just SHA)
  • They make reconcile and navigation safer after history edits

Branch naming convention

git-gud uses predictable branch names:

  • Stack branch: <username>/<stack-name>
  • Entry branch: <username>/<stack-name>--<gg-id>

Example:

  • nacho/user-auth
  • nacho/user-auth--c-abc1234

This convention is what makes remote discovery (gg ls --remote) and reconciliation possible.

PR/MR dependency chains

Dependency chaining is automatic during gg sync:

  • First PR/MR targets main (or your configured base)
  • Next PR/MR targets previous entry branch
  • This continues until stack head

Result: reviewers can review from bottom to top, and gg land can merge safely in order.

Guides

This section is practical and task-oriented.

If you’re new to stacked diffs, start with Your First Stack. If you’re already using git-gud day-to-day, jump to the workflow you need (editing, remote collaboration, worktrees, landing, linting, reconcile).

Your First Stack

This walkthrough covers the full lifecycle: create → commit → sync → edit → land → clean.

1) Create a stack

gg co user-auth

This creates/switches to stack branch your-user/user-auth.

2) Build the feature in reviewable commits

git add . && git commit -m "Add user model"
git add . && git commit -m "Add auth endpoints"
git add . && git commit -m "Add login UI"

Keep each commit small and self-contained.

3) Inspect stack structure

gg ls

You should see ordered entries, each with a GG-ID.

4) Publish review chain

gg sync --draft

This pushes one branch per entry and creates one PR/MR per commit, chained by dependencies.

5) Address feedback in an older commit

gg mv 1
# edit files
git add .
gg sc

gg sc amends the current entry and rebases subsequent entries automatically.

6) Update remote PRs/MRs

gg sync

7) Land approved entries

gg land --all

Use --wait if you want git-gud to wait for CI/approvals:

gg land --all --wait

8) Cleanup

gg clean

Editing Commits in a Stack

The most common stacked-diff operation is “change commit N without losing N+1, N+2…”.

gg mv 2
# or use gg first / gg next / gg prev / gg last

Make changes and fold them in

# after editing files
git add .
gg sc

Use gg sc --all to include unstaged changes too.

Reorder commits

Interactive:

gg reorder

Explicit order:

gg reorder --order "3,1,2"

Absorb scattered staged edits automatically

gg absorb

Useful flags:

  • --dry-run: preview only
  • --and-rebase: absorb and rebase in one step
  • --whole-file: match whole-file changes instead of hunks
  • --squash: squash fixups directly

After major edits, run:

gg sync

Working with Remote Stacks

Use this when a stack exists on origin but not in your local checkout (new machine, pairing, takeover).

Discover remote-only stacks

gg ls --remote

Check out a remote stack

gg co user-auth

If a local stack doesn’t exist, git-gud can reconstruct it from remote entry branches and mappings.

Typical collaboration loop

gg co teammate-feature
gg ls
# make changes
gg sync

Tips:

Using Worktrees

Worktrees let you keep your main checkout clean while developing a stack in a dedicated directory.

Create stack in a managed worktree

gg co user-auth --worktree

Short flag:

gg co user-auth -w

Why use worktrees

  • Keep your main checkout untouched
  • Work on multiple stacks side by side
  • Avoid stashing/switching overhead

Default path behavior

By default git-gud creates:

../<repo-name>.<stack-name>

You can change this with defaults.worktree_base_path in .git/gg/config.json.

Cleanup behavior

gg clean removes merged stacks and associated managed worktrees.

Landing and Cleanup

Use gg land to merge entries in order, from the bottom of the stack upward.

Land one approved entry

gg land

Land the whole stack

gg land --all

Wait for CI and approvals

gg land --all --wait

Land only part of a stack

gg land --until 2
# or by GG-ID / SHA

Merge strategy and provider-specific behavior

gg land --no-squash

GitLab auto-merge queue:

gg land --auto-merge

Auto-clean after landing

One-off:

gg land --all --clean

Or make it default with land_auto_clean in config.

Manual cleanup remains available:

gg clean

Linting Your Stack

gg lint runs your configured lint commands commit-by-commit across the stack.

Configure lint commands

In .git/gg/config.json:

{
  "defaults": {
    "lint": [
      "cargo fmt --check",
      "cargo clippy -- -D warnings"
    ]
  }
}

Run lint manually

gg lint

Run only up to a specific entry:

gg lint --until 2

Run lint during sync

gg sync --lint

Skip lint for one sync (even if enabled by default):

gg sync --no-lint

Reconciling Out-of-Sync Stacks

Use reconcile when stack metadata and remote state diverge.

Common causes:

  • Someone pushed with git push instead of gg sync
  • A stack was edited across machines and mappings got stale
  • Commits exist without GG-ID trailers

Preview changes safely

gg reconcile --dry-run

Apply reconciliation

gg reconcile

Reconcile can:

  1. Add missing GG-IDs to stack commits (via rebase)
  2. Map existing PRs/MRs to the right GG-IDs in config

After reconciling, run:

gg ls --refresh
gg sync

Command Reference

This section is a command-by-command reference based on gg --help and gg <command> --help.

Unlike the guides, this section is organized by command surface and flags. Each page still includes practical examples.

Command groups

  • Stack lifecycle: co, ls, sync, land, clean
  • Editing: mv, first, last, prev, next, sc, absorb, reorder, rebase
  • Utilities: lint, setup, reconcile, continue, abort, completions

gg co

Create a new stack, switch to an existing local stack, or check out a remote stack by name.

gg co [OPTIONS] [STACK_NAME]

Options

  • -b, --base <BASE>: Base branch to use (default auto-detected: main/master/trunk)
  • -w, --worktree: Create or reuse a managed worktree for this stack

Examples

# Create/switch stack
gg co user-auth

# Create stack based on a specific branch
gg co user-auth --base develop

# Create stack in worktree
gg co user-auth --worktree

gg ls

List the current stack, all local stacks, or remote-only stacks.

gg ls [OPTIONS]

Options

  • -a, --all: Show all local stacks
  • -r, --refresh: Refresh PR/MR status from remote
  • --remote: List remote stacks not checked out locally

Examples

# Current stack status
gg ls

# All local stacks
gg ls --all

# Remote stacks you can check out
gg ls --remote

# Refresh status badges from provider
gg ls --refresh

gg sync

Push entry branches and create/update PRs/MRs for the current stack.

gg sync [OPTIONS]

Options

  • -d, --draft: Create new PRs/MRs as draft
  • -f, --force: Force push even if remote is ahead
  • --update-descriptions: Update PR/MR title/body from commit messages
  • -l, --lint: Run lint before sync
  • --no-lint: Disable lint before sync (overrides config default)
  • -u, --until <UNTIL>: Sync up to target commit (position, GG-ID, or SHA)

Examples

# First publish as drafts
gg sync --draft

# Sync only first two entries
gg sync --until 2

# Refresh PR/MR descriptions after commit message edits
gg sync --update-descriptions

# Run lint as part of sync
gg sync --lint

Navigation (mv, first, last, prev, next)

These commands move HEAD within your stack without manual rebase gymnastics.

gg mv <TARGET>

Move to a specific entry by:

  • Position (1-indexed)
  • GG-ID (c-...)
  • Commit SHA
gg mv 1
gg mv c-abc1234
gg mv a1b2c3d

Relative navigation

gg first   # first entry
gg last    # stack head
gg prev    # previous entry
gg next    # next entry

gg sc

Squash local changes into the current stack commit.

gg sc [OPTIONS]

Options

  • -a, --all: Include staged and unstaged changes

Examples

# Standard amend-like flow
git add .
gg sc

# Include unstaged changes too
gg sc --all

gg absorb

Automatically distribute staged changes to the most appropriate commits in your stack.

gg absorb [OPTIONS]

Options

  • --dry-run: Preview actions without changing commits
  • -a, --and-rebase: Rebase automatically after creating fixups
  • -w, --whole-file: Match and absorb by whole file rather than hunks
  • --one-fixup-per-commit: At most one fixup per commit
  • -n, --no-limit: Search all commits in the stack (not just last 10)
  • -s, --squash: Squash directly instead of creating fixup! commits

Examples

# Preview before applying
gg absorb --dry-run

# Absorb and finish with rebase
gg absorb --and-rebase

# Heavy refactor across many files
gg absorb --whole-file --no-limit

gg reorder

Reorder commits in your stack.

gg reorder [OPTIONS]

Options

  • -o, --order <ORDER>: New order as positions/SHAs ("3,1,2" or "3 1 2")

Examples

# Interactive reorder
gg reorder

# Explicit reorder
gg reorder --order "3,1,2"

gg rebase

Rebase the current stack onto an updated branch.

gg rebase [TARGET]
  • If TARGET is omitted, git-gud uses the stack base branch.

Examples

# Rebase onto configured base
gg rebase

# Rebase onto specific branch
gg rebase main

gg land

Merge approved PRs/MRs from the bottom of your stack upward.

gg land [OPTIONS]

Options

  • -a, --all: Land all approved entries in sequence
  • --auto-merge: (GitLab only) Request auto-merge instead of immediate merge
  • --no-squash: Disable squash merge (squash is default)
  • -w, --wait: Wait for CI and approvals before merging
  • -u, --until <UNTIL>: Land up to a target entry (position, GG-ID, SHA)
  • -c, --clean: Clean stack automatically after landing all
  • --no-clean: Disable auto-clean for this run

Examples

# Land one approved entry
gg land

# Land complete stack, waiting for readiness
gg land --all --wait

# Land part of stack
gg land --until 2

# GitLab auto-merge queue
gg land --all --auto-merge

gg clean

Delete merged stacks (and associated managed worktrees).

gg clean [OPTIONS]

Options

  • -a, --all: Clean all merged stacks without prompting

Examples

gg clean
gg clean --all

gg lint

Run configured lint commands on stack commits.

gg lint [OPTIONS]

Options

  • -u, --until <UNTIL>: Stop at target entry (position, GG-ID, SHA)

Examples

# Lint from bottom to current
gg lint

# Lint only a subset
gg lint --until 2

gg setup

Interactive setup for .git/gg/config.json.

gg setup

Use this when:

  • Starting git-gud in a new repository
  • Working with self-hosted GitHub/GitLab
  • Updating defaults (base branch, username, lint config)

gg continue / gg abort

Control paused operations (typically rebases with conflicts).

gg continue
gg abort

Use gg continue after resolving conflicts and staging files. Use gg abort when you want to stop and roll back the in-progress operation.

gg reconcile

Repair stack metadata when branches/PRs were manipulated outside gg sync.

gg reconcile [OPTIONS]

Options

  • -n, --dry-run: Preview only; make no changes

What it does

  • Adds missing GG-ID trailers to stack commits
  • Maps existing remote PRs/MRs back to local stack entries

Examples

# Safe preview
gg reconcile --dry-run

# Apply reconciliation
gg reconcile

Configuration

git-gud stores config per repository in .git/gg/config.json.

Initialize or update it with:

gg setup

Example config

{
  "defaults": {
    "provider": "gitlab",
    "base": "main",
    "branch_username": "your-username",
    "lint": [
      "cargo fmt --check",
      "cargo clippy -- -D warnings"
    ],
    "auto_add_gg_ids": true,
    "land_wait_timeout_minutes": 30,
    "land_auto_clean": false,
    "worktree_base_path": "/tmp/gg-worktrees",
    "gitlab": {
      "auto_merge_on_land": false
    }
  }
}

defaults options

OptionTypeWhat it controlsDefault
providerstringProvider (github/gitlab) for self-hosted or explicit overrideAuto-detected
basestringDefault base branch for new stacksAuto-detected
branch_usernamestringUsername prefix in stack/entry branch namesAuto-detected
lintstring[]Commands used by gg lint / gg sync --lint[]
auto_add_gg_idsbooleanAuto-add GG-ID trailers when missingtrue
land_wait_timeout_minutesnumberTimeout for gg land --wait polling30
land_auto_cleanbooleanAuto-run cleanup after full landingfalse
worktree_base_pathstringBase directory for managed worktreesParent of repo
gitlab.auto_merge_on_landbooleanDefault GitLab auto-merge behavior for gg landfalse

Stack state

git-gud also stores stack-specific state in this file (for example PR/MR mappings by GG-ID). This is how it remembers which commit corresponds to which PR/MR over time.

PR/MR templates

You can customize descriptions by creating .git/gg/pr_template.md.

Supported placeholders:

  • {{title}}
  • {{description}}
  • {{stack_name}}
  • {{commit_sha}}

Example:

## Summary

{{description}}

---

**Stack:** `{{stack_name}}`
**Commit:** `{{commit_sha}}`

Shell Completions

Generate completions with:

gg completions <shell>

Supported shells include: bash, zsh, fish, elvish, powershell.

Bash

mkdir -p ~/.local/share/bash-completion/completions
gg completions bash > ~/.local/share/bash-completion/completions/gg

Zsh

mkdir -p ~/.zfunc
gg completions zsh > ~/.zfunc/_gg

Then in ~/.zshrc:

fpath=(~/.zfunc $fpath)
autoload -Uz compinit && compinit

Fish

mkdir -p ~/.config/fish/completions
gg completions fish > ~/.config/fish/completions/gg.fish

Troubleshooting / FAQ

gh or glab is missing

Install the provider CLI:

Not authenticated with provider

gh auth login
glab auth login

“Not on a stack branch”

You’re on a branch that doesn’t match the stack naming scheme.

gg co <stack-name>

I pushed with git push and now mappings are wrong

Run reconcile:

gg reconcile --dry-run
gg reconcile

Merge commits are not supported

Stacks require linear history. Rebase your branch:

git rebase main

gg land --wait times out

Increase timeout in config:

{
  "defaults": {
    "land_wait_timeout_minutes": 60
  }
}

When should I use gg absorb vs gg sc?

  • Use gg sc when you’re on the exact commit you want to modify.
  • Use gg absorb when staged edits belong to multiple commits and you want git-gud to distribute them.