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.

Note: If the auth check fails due to a network error (e.g., DNS resolution failure, connection timeout), gg will print a warning and continue. The operation may still fail later if authentication is actually required.

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 metadata trailers

Each stack commit carries stable trailers, for example:

GG-ID: c-abc1234
GG-Parent: c-1234567
  • GG-ID identifies the commit itself.
  • GG-Parent points to the previous stack entry’s GG-ID.
  • The first stack entry has no GG-Parent.

Why this matters:

  • Commit-to-PR/MR mappings stay stable across rebases
  • Stack topology is recoverable from commit-local metadata
  • gg sync and gg reconcile can auto-heal metadata drift 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

If your stack base is behind origin/<base>, gg sync warns and suggests running gg rebase first. Use --no-rebase-check to skip this check once.

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

Active stacks are shown first. Stacks whose PRs/MRs have all been merged appear in a separate “Landed” section at the bottom, so you can focus on work that still needs attention.

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

If lint fails during gg sync --lint, sync is aborted and git-gud restores repository state to the pre-sync snapshot.

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

Agent Skills Plugin

git-gud ships as a Claude Code plugin and follows the open Agent Skills standard. This means AI coding agents — Claude Code, Cursor, Gemini CLI, OpenAI Codex, VS Code integrations, and others — can use gg for stacked-diff workflows.

What’s included

The plugin provides one unified skill:

SkillDescription
ggUse gg with GitHub PRs (gh CLI) or GitLab MRs (glab CLI, merge trains)

Each skill includes:

  • SKILL.md — concise instructions with agent operating rules
  • reference.md — command reference and JSON schemas
  • examples/ — step-by-step workflow walkthroughs

Installation

claude plugin marketplace add https://github.com/mrmans0n/git-gud
claude plugin install git-gud

2) One-off plugin loading (CLI)

Use this when launching Claude Code directly:

claude --plugin-dir /path/to/git-gud

3) Project-level config (.claude/settings.json)

Use this when you want the plugin enabled by default for a repository:

{
  "plugins": [
    {
      "name": "git-gud",
      "path": "/path/to/git-gud"
    }
  ]
}

4) Other Agent Skills-compatible tools

Tools that support the Agent Skills standard can load skills from the repo’s skills/ directory. In practice, this includes Claude Code, Cursor, Gemini CLI, OpenAI Codex, and other compatible agent hosts.

How agents typically use gg

A practical AI-assisted stacked-diff workflow looks like this:

  1. Agent creates or switches to a stack (gg co ..., ideally with a worktree)
  2. Agent makes small commits and keeps each commit focused
  3. Agent syncs the stack (gg sync) so PRs/MRs are created/updated in order
  4. Agent iterates on review feedback (amend/reorder/re-sync)
  5. Agent asks for explicit user confirmation before gg land

This keeps work reviewable while preserving user control over merges.

JSON output for tool-driven agents

For machine-readable parsing, gg supports --json on key commands:

  • gg ls --json
  • gg sync --json
  • gg land --json
  • gg clean --json
  • gg lint --json

Use these outputs in agents and automation for reliable state checks and decisions. For full response schemas, see each skill’s reference.md.

Safety model (required behavior)

When using AI agents with gg, keep these rules:

  1. Never land without explicit user confirmation
  2. Never run git add -A blindly (stage only reviewed/intended files)
  3. Prefer worktrees for isolation (gg co --wt)
  4. Use structured output (--json) when automation must parse command results

Skill references

For full operational details, prompts, and examples:

File structure

.claude-plugin/
  plugin.json           # Plugin manifest
skills/
  gg/
    SKILL.md            # Unified GitHub + GitLab skill
    reference.md        # Command reference + JSON schemas
    examples/
      basic-flow.md     # Provider-agnostic feature workflow
      multi-commit.md   # Absorb, reorder, lint
      merge-train.md    # GitLab merge train workflow

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.

When the stack base is behind origin/<base>, output includes a ↓N indicator (N = commits behind).

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. Stacks whose PRs/MRs are all merged are shown in a separate “Landed” section at the bottom with a marker
  • --json: Print structured JSON output (for scripts and automation). Automatically performs a best-effort refresh of PR/MR state from the provider API, so pr_state and ci_status fields are populated without needing --refresh.

Examples

# Current stack status
gg ls

# All local stacks
gg ls --all

# Remote stacks (active first, then landed)
gg ls --remote

# Refresh status badges from provider
gg ls --refresh

# Structured JSON for automation
gg ls --json
gg ls --all --json
gg ls --remote --json

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 (does not affect existing PRs/MRs)
  • -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 (aborts sync on lint failure and restores repository state to the pre-sync snapshot)
  • --no-lint: Disable lint before sync (overrides config default)
  • --no-rebase-check: Skip checking whether your stack base is behind origin/<base>
  • -u, --until <UNTIL>: Sync up to target commit (position, GG-ID, or SHA)
  • --json: Output structured JSON for automation (suppresses human/progress output)

Before pushing, gg sync checks whether your stack base is behind origin/<base>. If it is behind by at least the configured threshold, git-gud warns and suggests rebasing first (gg rebase).

When you run gg sync --lint, lint runs before any push/PR updates. If lint fails, sync aborts immediately and git-gud restores your repository to the pre-sync snapshot.

Before pushing, gg sync also normalizes commit metadata (GG-ID and GG-Parent) for the whole stack. This normalization is always enforced during sync (including adding missing GG-ID trailers) to keep stack identity and PR/MR mappings stable.

You can control this behavior with config:

  • defaults.sync_auto_rebase (sync.auto_rebase): automatically run gg rebase before sync when behind threshold is reached
  • defaults.sync_behind_threshold (sync.behind_threshold): minimum number of commits behind before warning/rebase logic applies (0 disables the check)

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

# Skip behind-base check once
gg sync --no-rebase-check

# Machine-readable output
# (useful in scripts/agents)
gg sync --json

Example JSON (shape):

{
  "version": 1,
  "sync": {
    "stack": "my-stack",
    "base": "main",
    "rebased_before_sync": false,
    "metadata": {
      "gg_ids_added": 0,
      "gg_parents_updated": 1,
      "gg_parents_removed": 0
    },
    "entries": [
      {
        "position": 1,
        "sha": "abc1234",
        "title": "Add feature",
        "gg_id": "c-abc1234",
        "branch": "user/my-stack--c-abc1234",
        "action": "created",
        "pr_number": 42,
        "pr_url": "https://github.com/org/repo/pull/42",
        "draft": false,
        "pushed": true,
        "error": null
      }
    ]
  }
}

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

When unstaged changes are present, behavior is controlled by defaults.unstaged_action in .git/gg/config.json:

  • ask (default): prompt to stage all, stash, continue, or abort
  • add: auto-stage all changes (git add -A) and continue
  • stash: auto-stash and continue
  • continue: continue without including unstaged changes
  • abort: fail immediately

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 drop

Remove one or more commits from the stack.

Alias: gg abandon

gg drop <TARGET>... [OPTIONS]

Arguments

  • <TARGET>...: One or more commits to drop. Each target can be:
    • Position (1-indexed): 1, 3
    • Short SHA: abc1234
    • GG-ID: c-abc1234

Options

  • -f, --force: Skip the confirmation prompt
  • --json: Output result as JSON

Behavior

  1. Validates the working directory is clean
  2. Resolves each target to a commit in the stack
  3. Shows which commits will be dropped and asks for confirmation (unless --force)
  4. Performs a git rebase -i that omits the dropped commits
  5. Cleans up per-commit branches for dropped commits
  6. Prints a summary of what was dropped

At least one commit must remain in the stack after dropping.

Examples

# Drop the second commit in the stack
gg drop 2

# Drop multiple commits at once
gg drop 1 3

# Drop by GG-ID, skip confirmation
gg drop c-abc1234 --force

# Drop with JSON output
gg drop 2 --force --json

# Use the 'abandon' alias (inspired by jj)
gg abandon 2

JSON Output

{
  "version": 1,
  "drop": {
    "dropped": [
      {"position": 2, "sha": "abc1234", "title": "Fix typo"}
    ],
    "remaining": 3
  }
}

Edge Cases

  • Dropping all commits produces an error — at least one commit must remain
  • Invalid position shows the valid range
  • Rebase conflicts are handled the same as gg reorder — resolve with gg continue or gg abort

gg reorder / gg arrange

Reorder and/or drop commits in your stack.

gg arrange is an alias for gg reorder — they share the same implementation.

gg reorder [OPTIONS]
gg arrange [OPTIONS]

Options

  • -o, --order <ORDER>: New order as positions/SHAs ("3,1,2" or "3 1 2")
  • --no-tui: Disable the interactive TUI and use a text editor instead

Interactive TUI

When run without --order, opens an interactive TUI where you can visually rearrange and drop commits:

┌─ Arrange Stack ──────────────────────────────────┐
│  1  abc1234  feat: add login page                │
│▸ 2  def5678  fix: handle empty input     ↕       │
│  [DROP] 3  ghi9012  refactor: extract validator   │
│  4  jkl3456  test: add integration tests          │
└──────────────────────────────────────────────────┘
 j/k:navigate  J/K:move commit  d:drop/undrop  Enter/s:confirm  q/Esc:cancel

Keyboard shortcuts

KeyAction
j / Move cursor down
k / Move cursor up
J / Shift+↓Move commit down
K / Shift+↑Move commit up
d / DeleteToggle drop mark on commit
Enter / sConfirm new order
q / EscCancel (no changes)

Position 1 is the bottom of the stack (closest to the base branch).

Dropped commits appear in red with strikethrough and a [DROP] prefix. You can still move dropped commits (e.g., to undrop them later). At least one commit must remain — you cannot drop all commits.

The TUI requires a TTY. In non-interactive environments (pipes, CI), gg reorder falls back to the text editor automatically. Use --no-tui to force the editor fallback.

Editor Fallback

When using the editor fallback (--no-tui or non-TTY), you can:

  • Reorder commits by rearranging lines
  • Drop commits by deleting their lines

At least one commit must remain.

Examples

# Interactive reorder/drop with TUI
gg reorder
gg arrange

# Explicit reorder by position (no dropping)
gg reorder --order "3,1,2"

# Use text editor instead of TUI
gg arrange --no-tui

gg split

Split a commit in the stack into two commits. The selected files/hunks become a new commit inserted before the original in the stack, while the remaining changes stay in the original commit.

gg split [OPTIONS] [FILES...]

Options

  • -c, --commit <TARGET>: Target commit — position (1-indexed), short SHA, or GG-ID. Defaults to the current commit (HEAD).
  • -m, --message <MESSAGE>: Commit message for the new (first) commit. Skips the editor prompt.
  • --no-edit: Keep the original message for the remainder commit without prompting.
  • -i, --interactive: Select individual hunks interactively. Auto-enabled for single-file commits.
  • --no-tui: Disable TUI, use sequential prompt instead (legacy git add -p style).
  • FILES...: Files to include in the new commit. If omitted, opens an interactive file selector (or hunk selector in interactive mode).

How It Works

When you split commit K into two:

  1. New commit (K’) — Contains only the selected files/hunks. Inserted before K in the stack. Gets a new GG-ID.
  2. Remainder (K’’) — Contains the remaining files/hunks. Stays in K’s original position. Keeps the original GG-ID (preserving PR association).

All descendant commits are automatically rebased onto the remainder.

BEFORE                    AFTER
  4: "Fix tests"            5: "Fix tests"       (rebased)
  3: "Add auth+logging"     4: "Add logging"      ← remainder (keeps GG-ID)
  2: "Setup DB"             3: "Add auth"         ← NEW commit (selected changes)
  1: "Init project"         2: "Setup DB"
                             1: "Init project"

File-Level Splitting

Interactive file selection

# Split the current commit — opens a checkbox selector
gg split

Split with explicit files

# Move auth files to a new commit before the current one
gg split -m "Add authentication" src/auth.rs src/auth_test.rs

Split a specific commit in the stack

# Split commit at position 3
gg split -c 3 src/config.rs

# Split by GG-ID
gg split -c c-abc1234 src/config.rs

Non-interactive with both messages

gg split -c 2 -m "Extract helpers" --no-edit helpers.rs utils.rs

Hunk-Level Splitting (-i)

When you need finer control than whole files, use interactive hunk mode:

# Force hunk mode for any commit
gg split -i

# Hunk mode on specific files
gg split -i src/auth.rs

Note: Single-file commits automatically enter hunk mode since file-level splitting wouldn’t make sense.

TUI Mode (Default)

When run interactively with a TTY, gg split -i opens a two-panel TUI for hunk selection:

┌── Files (1/3 width) ──┬── Diff (2/3 width) ──────────────┐
│ [✓] src/auth.rs (3)   │ @@ -10,6 +10,12 @@               │
│ [ ] src/logging.rs (1)│ +  // Validate token              │
│ [~] src/tests.rs (2)  │ +  if token.is_empty() {          │
│                        │ +      return false;               │
│                        │ +  }                               │
├────────────────────────┴────────────────────────────────────┤
│ 5/12 hunks selected │ [Space] toggle · [Tab] switch panel │
└─────────────────────────────────────────────────────────────┘

TUI Keyboard Shortcuts

KeyIn File PanelIn Diff Panel
↑/↓ or j/kNavigate filesNavigate hunks
SpaceToggle all hunks for fileToggle current hunk
aSelect all hunks (all files)Select all hunks (this file)
nDeselect all hunks (all files)Deselect all hunks (this file)
sSplit current hunk into sub-hunks
Tab / ← / →Switch to diff panelSwitch to file panel
EnterEnter commit messageEnter commit message
q / EscAbort (cancel split)Abort (cancel split)

Inline Commit Message

After pressing Enter to confirm your hunk selection, an inline text input appears at the bottom of the TUI for the commit message. It’s pre-filled with Split from: <original commit title>.

KeyAction
EnterConfirm message and create the split
EscGo back to hunk selection
← / →Move cursor
Home / EndJump to beginning/end
Backspace / DeleteDelete characters
Ctrl+A / Ctrl+EJump to beginning/end (emacs-style)
Ctrl+UClear from cursor to beginning
Ctrl+KClear from cursor to end

After confirming the new commit message, a second inline input appears for the remainder commit message (pre-filled with the original commit’s message). This replaces the external editor for both messages, keeping the entire split workflow inside the TUI.

KeyAction
EnterConfirm remainder message and complete the split
EscGo back to the new commit message input

The -m flag still works and bypasses the TUI input for the new commit. The --no-edit flag skips the remainder message input entirely, keeping the original message as-is.

File Panel Indicators

  • [✓] — All hunks selected (green)
  • [~] — Some hunks selected (yellow)
  • [ ] — No hunks selected

Sequential Prompt Mode (--no-tui)

Use --no-tui to fall back to the legacy git add -p style sequential prompt:

gg split -i --no-tui

This mode is automatically used when no TTY is available (e.g., in CI pipelines or when piping).

For each hunk, you’ll see the diff with colored output and a prompt:

--- a/src/auth.rs
+++ b/src/auth.rs
@@ -10,6 +10,12 @@ fn authenticate(user: &str) -> bool {
+    // Validate token
+    if token.is_empty() {
+        return false;
+    }

Include this hunk? [y]es/[n]o/[a]ll file/[d]one file/[s]plit/[q]uit/?help:

Sequential Mode Actions

KeyActionDescription
yYesInclude this hunk in the new commit
nNoSkip this hunk (stays in remainder)
aAll fileInclude all remaining hunks from this file
dDone fileSkip all remaining hunks from this file
sSplitSplit this hunk into smaller hunks
qQuitStop; all remaining hunks stay in remainder
?HelpShow this help

Hunk Splitting

If a hunk contains multiple logical changes separated by unchanged lines, pressing s will break it into smaller hunks. You can then select each sub-hunk individually. If the hunk is already atomic (contiguous changes), you’ll see “This hunk cannot be split further.”

Edge Cases

  • All changes selected — Warning: the original commit will be empty.
  • No changes selected — Error.
  • Dirty working directory — Error. Commit or stash changes first.
  • Merge conflicts during rebase — Split is aborted, original state restored.

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
  • --json: Emit machine-readable JSON output (no human logs)

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

# JSON output for automation
gg land --all --json

Merge Trains (GitLab)

When merge trains are enabled on the target branch, gg land automatically adds MRs to the merge train instead of merging directly.

Approval is always required before an MR can enter the merge train queue — even with --all. If using --wait, the command will show “Waiting for approval…” until a reviewer approves the MR.

CI Failure Details

When using --wait, if CI fails on an MR the command stops and shows which jobs failed:

OK Landed 1 MR(s)

⚠ Landed 1 MR(s), but encountered an error:

Error: MR !7621 CI failed
  Failed jobs: lint (stage: test), build-android (stage: build)

This helps diagnose CI issues without having to open the GitLab UI. Failed job names and stages are fetched from the MR’s head pipeline.

JSON Output

Example JSON response:

{
  "version": 1,
  "land": {
    "stack": "my-stack",
    "base": "main",
    "landed": [
      {
        "position": 1,
        "sha": "abc1234",
        "title": "feat: add parser",
        "gg_id": "c-abc1234",
        "pr_number": 42,
        "action": "merged",
        "error": null
      }
    ],
    "remaining": 0,
    "cleaned": false,
    "warnings": [],
    "error": null
  }
}

gg clean

Delete merged stacks (and associated managed worktrees).

gg clean [OPTIONS]

Options

  • -a, --all: Clean all merged stacks without prompting
  • --json: Emit machine-readable JSON output

Examples

gg clean
gg clean --all
gg clean --json

--json prints:

  • version: output schema version
  • clean.cleaned: stacks that were cleaned
  • clean.skipped: stacks skipped (unmerged or declined in interactive mode)

gg lint

Run configured lint commands on stack commits.

gg lint [OPTIONS]

Options

  • -u, --until <UNTIL>: Stop at target entry (position, GG-ID, SHA)
  • --json: Emit structured JSON output

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        # Quick mode: essential settings only
gg setup --all  # Full mode: all settings organized by category

Use this when:

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

Quick Mode (default)

Quick mode prompts for only the essential settings:

  • Provider: GitHub or GitLab
  • Base branch: Default base branch (main/master/trunk)
  • Username: Username for branch naming

After completing quick setup, you’ll see:

Tip: Run 'gg setup --all' to configure advanced options (sync, land, lint, etc.)

Full Mode (--all)

Full mode organizes all settings into logical groups:

General

FieldTypeDefaultDescription
providerselectauto-detectGitHub or GitLab
basestringauto-detectDefault base branch (main/master/trunk)
branch_usernamestringfrom CLI authUsername for branch naming
unstaged_actionselectaskAction for gg amend with unstaged changes

Sync

FieldTypeDefaultDescription
sync_auto_rebaseboolfalseAuto-rebase when base is behind origin
sync_behind_thresholdnumber1Commits behind origin before warning/rebase
sync_draftboolfalseCreate new PRs/MRs as drafts by default
sync_update_descriptionsbooltrueUpdate PR/MR descriptions on re-sync

Land

FieldTypeDefaultDescription
land_auto_cleanboolfalseAuto-clean stack after landing all
land_wait_timeout_minutesnumber30Timeout for gg land --wait

Lint

FieldTypeDefaultDescription
lintlistemptyLint commands to run per commit
sync_auto_lintboolfalseRun lint automatically before sync

Worktrees

FieldTypeDefaultDescription
worktree_base_pathstringemptyTemplate for stack worktrees ({repo}, {stack})

GitLab (only shown if provider is GitLab)

FieldTypeDefaultDescription
gitlab.auto_merge_on_landboolfalseUse “merge when pipeline succeeds”

Global Config

git-gud supports global configuration at ~/.config/gg/config.json. When running gg setup:

  • If no local config exists, global defaults are shown in prompts
  • Local config always takes precedence over global config

This allows you to set organization-wide defaults while allowing per-repo overrides.

All fields are written to config.json after setup, making it easy to review and edit configuration manually.

Note: auto_add_gg_ids is deprecated. Existing configs that include it are still read, but setup no longer prompts for it and runtime behavior always treats it as enabled.

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

  • Normalizes GG metadata trailers on stack commits (GG-ID + GG-Parent)
  • Maps existing remote PRs/MRs back to local stack entries

Examples

# Safe preview
gg reconcile --dry-run

# Apply reconciliation
gg reconcile

MCP Server

git-gud includes an MCP (Model Context Protocol) server that allows AI assistants like Claude Desktop, Cursor, and other MCP-compatible tools to interact with your stacked-diffs workflows programmatically.

Installation

The gg-mcp binary is distributed alongside gg. If you installed via Homebrew or cargo-dist, it should already be available.

Configuration

Claude Desktop

Add this to your Claude Desktop MCP configuration (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "git-gud": {
      "command": "gg-mcp",
      "env": {
        "GG_REPO_PATH": "/path/to/your/repo"
      }
    }
  }
}

Cursor / Other MCP Clients

Configure gg-mcp as a stdio-based MCP server. Set GG_REPO_PATH to point to your repository.

Environment Variables

VariableDescriptionDefault
GG_REPO_PATHPath to the git repositoryCurrent working directory

Available Tools

stack_list

List the current stack with commit entries and PR/MR status.

Parameters:

  • refresh (boolean, optional): Refresh PR/MR status from remote before listing. Default: false.

Returns: Stack name, base branch, commit entries with positions, SHAs, titles, GG-IDs, PR numbers, states, CI status, and approval status.

stack_list_all

List all stacks in the repository with summary information.

Parameters: None.

Returns: Current stack name and a list of all stacks with name, base branch, commit count, and whether each is the current stack.

stack_status

Get a quick status summary of the current stack.

Parameters: None.

Returns: Stack name, base branch, total commits, synced commits, current position, and how many commits behind the base branch.

pr_info

Get detailed information about a specific PR/MR by number.

Parameters:

  • number (integer, required): The PR/MR number to look up.

Returns: PR number, title, state (open/merged/closed/draft), URL, draft status, approval status, mergeability, and CI status.

config_show

Show the current git-gud configuration for this repository.

Parameters: None.

Returns: Provider, base branch, branch username, lint commands, and boolean settings (including compatibility field auto_add_gg_ids, which is always returned as true).

Write Tools

stack_checkout

Create a new stack or switch to an existing one.

Parameters:

  • name (string, optional): Stack name.
  • base (string, optional): Base branch (default: main/master).
  • worktree (boolean, optional): Use a git worktree for isolation.

stack_sync

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

Parameters:

  • draft (boolean, optional): Create PRs as draft.
  • force (boolean, optional): Force-push branches.
  • update_descriptions (boolean, optional): Update PR descriptions from commit messages.
  • no_rebase_check (boolean, optional): Skip rebase-needed check.
  • lint (boolean, optional): Run lint before syncing.
  • until (string, optional): Only sync up to this position/GG-ID/SHA.

stack_land

Merge approved PRs/MRs from the current stack.

Parameters:

  • all (boolean, optional): Land all approved PRs.
  • squash (boolean, optional): Use squash merge.
  • auto_clean (boolean, optional): Auto-clean the stack after landing.
  • until (string, optional): Only land up to this position/GG-ID/SHA.

stack_clean

Clean up stacks whose PRs have been merged.

Parameters:

  • all (boolean, optional): Clean all merged stacks.

stack_rebase

Rebase the current stack onto the latest base branch.

Parameters:

  • target (string, optional): Target branch to rebase onto.

stack_squash

Squash (amend) staged changes into the current commit.

Parameters:

  • all (boolean, optional): Stage all changes first.

stack_absorb

Auto-absorb staged changes into the correct commits.

Parameters:

  • dry_run (boolean, optional): Show what would be absorbed.
  • and_rebase (boolean, optional): Rebase after absorbing.
  • whole_file (boolean, optional): Absorb whole files.
  • one_fixup_per_commit (boolean, optional): One fixup per target commit.
  • squash (boolean, optional): Squash fixups immediately.

stack_reconcile

Reconcile out-of-sync branches pushed outside of gg.

Parameters:

  • dry_run (boolean, optional): Show what would change.

stack_move

Move to a specific commit in the stack.

Parameters:

  • target (string, required): Position number, GG-ID, or SHA prefix.

stack_navigate

Navigate within the stack.

Parameters:

  • direction (string, required): "first", "last", "prev", or "next".

stack_lint

Run configured lint commands on each commit.

Parameters:

  • until (integer, optional): Only lint up to this position.

stack_drop

Remove commits from the stack.

Parameters:

  • targets (array of strings, required): Commits to drop—positions (1-indexed), short SHAs, or GG-IDs.

Notes: Always uses --force (the agent is expected to confirm with the user before calling). Returns JSON with dropped commits.

stack_split

Split a commit into two by moving specified files to a new commit.

Parameters:

  • commit (string, optional): Target commit—position (1-indexed), short SHA, or GG-ID. Defaults to the current commit.
  • files (array of strings, required): Files to include in the new commit.
  • message (string, optional): Message for the new (first) commit.
  • no_edit (boolean, optional): Don’t prompt for the remainder commit message.

Notes: File-level only (no hunk selection via MCP). The new commit is inserted before the original.

stack_reorder

Reorder commits in the stack with an explicit order.

Parameters:

  • order (string, required): New order as positions (1-indexed), e.g., "3,1,2" or "3 1 2".

Notes: No TUI via MCP. The order specifies the new bottom-to-top arrangement of commits.

Transport

The MCP server uses stdio transport (JSON-RPC over stdin/stdout), which is the standard for local MCP tools. No network configuration is needed.

Configuration

git-gud uses a layered configuration system:

  1. Global config: ~/.config/gg/config.json — shared defaults across all repos
  2. Local config: .git/gg/config.json — per-repository settings

Local config always takes precedence over global config.

Setup

Initialize or update local config with:

gg setup        # Quick mode: essential settings only
gg setup --all  # Full mode: all settings organized by category

For global config, manually create ~/.config/gg/config.json with your preferred defaults.

Example config

{
  "defaults": {
    "provider": "gitlab",
    "base": "main",
    "branch_username": "your-username",
    "lint": [
      "cargo fmt --check",
      "cargo clippy -- -D warnings"
    ],
    "auto_add_gg_ids": true,
    "unstaged_action": "ask",
    "land_wait_timeout_minutes": 30,
    "land_auto_clean": false,
    "sync_auto_lint": false,
    "sync_auto_rebase": false,
    "sync_behind_threshold": 1,
    "sync_draft": false,
    "sync_update_descriptions": true,
    "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_idsbooleanDeprecated compatibility field. gg always enforces GG metadata normalization, regardless of this value.true
unstaged_actionstringDefault behavior for gg sc/gg amend when unstaged changes exist: ask, add, stash, continue, or abortask
land_wait_timeout_minutesnumberTimeout for gg land --wait polling30
land_auto_cleanbooleanAuto-run cleanup after full landingfalse
sync_auto_lintbooleanAutomatically run gg lint before gg syncfalse
sync_auto_rebasebooleanAutomatically run gg rebase before gg sync when behind threshold is reachedfalse
sync_behind_thresholdnumberWarn/rebase in gg sync when base is at least this many commits behind origin/<base> (0 disables check)1
sync_draftbooleanCreate new PRs/MRs as drafts by defaultfalse
sync_update_descriptionsbooleanUpdate PR/MR descriptions on re-synctrue
worktree_base_pathstringBase directory for managed worktreesParent of repo
gitlab.auto_merge_on_landbooleanDefault GitLab auto-merge behavior for gg landfalse

Global Config

Store shared defaults in ~/.config/gg/config.json. This is useful for:

  • Organization-wide settings (e.g., always use drafts)
  • Personal preferences that apply to all your repos
  • Reducing repetitive setup across multiple repositories

Example global config:

{
  "defaults": {
    "sync_draft": true,
    "land_auto_clean": true,
    "sync_behind_threshold": 5
  }
}

When gg setup runs in a new repo, these global defaults will be shown in prompts. You can accept them or override per-repo.

Stack state

git-gud also stores stack-specific state in the local config 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.