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
-
Introducing git-gud — the story behind this tool, by its author
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:
- Git 2.x+
- GitHub CLI (
gh) for GitHub repositories - GitLab CLI (
glab) for GitLab repositories
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 setupis 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-IDidentifies the commit itself.GG-Parentpoints to the previous stack entry’sGG-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 syncandgg reconcilecan 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-authnacho/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…”.
Navigate to target commit
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:
- Prefer
gg syncover manualgit pushto keep mappings healthy - If mappings drift, use Reconciling Out-of-Sync Stacks
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 pushinstead ofgg 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:
- Add missing GG-IDs to stack commits (via rebase)
- 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:
| Skill | Description |
|---|---|
gg | Use 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
1) Claude Code marketplace (recommended)
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:
- Agent creates or switches to a stack (
gg co ..., ideally with a worktree) - Agent makes small commits and keeps each commit focused
- Agent syncs the stack (
gg sync) so PRs/MRs are created/updated in order - Agent iterates on review feedback (amend/reorder/re-sync)
- 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 --jsongg sync --jsongg land --jsongg clean --jsongg 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:
- Never land without explicit user confirmation
- Never run
git add -Ablindly (stage only reviewed/intended files) - Prefer worktrees for isolation (
gg co --wt) - Use structured output (
--json) when automation must parse command results
Skill references
For full operational details, prompts, and examples:
- Unified skill:
skills/gg/SKILL.md
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, sopr_stateandci_statusfields 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 behindorigin/<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 rungg rebasebefore sync when behind threshold is reacheddefaults.sync_behind_threshold(sync.behind_threshold): minimum number of commits behind before warning/rebase logic applies (0disables 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 abortadd: auto-stage all changes (git add -A) and continuestash: auto-stash and continuecontinue: continue without including unstaged changesabort: 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 creatingfixup!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
- Position (1-indexed):
Options
-f, --force: Skip the confirmation prompt--json: Output result as JSON
Behavior
- Validates the working directory is clean
- Resolves each target to a commit in the stack
- Shows which commits will be dropped and asks for confirmation (unless
--force) - Performs a
git rebase -ithat omits the dropped commits - Cleans up per-commit branches for dropped commits
- 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 withgg continueorgg 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
| Key | Action |
|---|---|
j / ↓ | Move cursor down |
k / ↑ | Move cursor up |
J / Shift+↓ | Move commit down |
K / Shift+↑ | Move commit up |
d / Delete | Toggle drop mark on commit |
Enter / s | Confirm new order |
q / Esc | Cancel (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 (legacygit add -pstyle).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:
- New commit (K’) — Contains only the selected files/hunks. Inserted before K in the stack. Gets a new GG-ID.
- 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
| Key | In File Panel | In Diff Panel |
|---|---|---|
| ↑/↓ or j/k | Navigate files | Navigate hunks |
| Space | Toggle all hunks for file | Toggle current hunk |
| a | Select all hunks (all files) | Select all hunks (this file) |
| n | Deselect all hunks (all files) | Deselect all hunks (this file) |
| s | — | Split current hunk into sub-hunks |
| Tab / ← / → | Switch to diff panel | Switch to file panel |
| Enter | Enter commit message | Enter commit message |
| q / Esc | Abort (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>.
| Key | Action |
|---|---|
| Enter | Confirm message and create the split |
| Esc | Go back to hunk selection |
| ← / → | Move cursor |
| Home / End | Jump to beginning/end |
| Backspace / Delete | Delete characters |
| Ctrl+A / Ctrl+E | Jump to beginning/end (emacs-style) |
| Ctrl+U | Clear from cursor to beginning |
| Ctrl+K | Clear 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.
| Key | Action |
|---|---|
| Enter | Confirm remainder message and complete the split |
| Esc | Go 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
| Key | Action | Description |
|---|---|---|
y | Yes | Include this hunk in the new commit |
n | No | Skip this hunk (stays in remainder) |
a | All file | Include all remaining hunks from this file |
d | Done file | Skip all remaining hunks from this file |
s | Split | Split this hunk into smaller hunks |
q | Quit | Stop; all remaining hunks stay in remainder |
? | Help | Show 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
TARGETis 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 versionclean.cleaned: stacks that were cleanedclean.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
| Field | Type | Default | Description |
|---|---|---|---|
provider | select | auto-detect | GitHub or GitLab |
base | string | auto-detect | Default base branch (main/master/trunk) |
branch_username | string | from CLI auth | Username for branch naming |
unstaged_action | select | ask | Action for gg amend with unstaged changes |
Sync
| Field | Type | Default | Description |
|---|---|---|---|
sync_auto_rebase | bool | false | Auto-rebase when base is behind origin |
sync_behind_threshold | number | 1 | Commits behind origin before warning/rebase |
sync_draft | bool | false | Create new PRs/MRs as drafts by default |
sync_update_descriptions | bool | true | Update PR/MR descriptions on re-sync |
Land
| Field | Type | Default | Description |
|---|---|---|---|
land_auto_clean | bool | false | Auto-clean stack after landing all |
land_wait_timeout_minutes | number | 30 | Timeout for gg land --wait |
Lint
| Field | Type | Default | Description |
|---|---|---|---|
lint | list | empty | Lint commands to run per commit |
sync_auto_lint | bool | false | Run lint automatically before sync |
Worktrees
| Field | Type | Default | Description |
|---|---|---|---|
worktree_base_path | string | empty | Template for stack worktrees ({repo}, {stack}) |
GitLab (only shown if provider is GitLab)
| Field | Type | Default | Description |
|---|---|---|---|
gitlab.auto_merge_on_land | bool | false | Use “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_idsis 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
| Variable | Description | Default |
|---|---|---|
GG_REPO_PATH | Path to the git repository | Current 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:
- Global config:
~/.config/gg/config.json— shared defaults across all repos - 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
| Option | Type | What it controls | Default |
|---|---|---|---|
provider | string | Provider (github/gitlab) for self-hosted or explicit override | Auto-detected |
base | string | Default base branch for new stacks | Auto-detected |
branch_username | string | Username prefix in stack/entry branch names | Auto-detected |
lint | string[] | Commands used by gg lint / gg sync --lint | [] |
auto_add_gg_ids | boolean | Deprecated compatibility field. gg always enforces GG metadata normalization, regardless of this value. | true |
unstaged_action | string | Default behavior for gg sc/gg amend when unstaged changes exist: ask, add, stash, continue, or abort | ask |
land_wait_timeout_minutes | number | Timeout for gg land --wait polling | 30 |
land_auto_clean | boolean | Auto-run cleanup after full landing | false |
sync_auto_lint | boolean | Automatically run gg lint before gg sync | false |
sync_auto_rebase | boolean | Automatically run gg rebase before gg sync when behind threshold is reached | false |
sync_behind_threshold | number | Warn/rebase in gg sync when base is at least this many commits behind origin/<base> (0 disables check) | 1 |
sync_draft | boolean | Create new PRs/MRs as drafts by default | false |
sync_update_descriptions | boolean | Update PR/MR descriptions on re-sync | true |
worktree_base_path | string | Base directory for managed worktrees | Parent of repo |
gitlab.auto_merge_on_land | boolean | Default GitLab auto-merge behavior for gg land | false |
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:
- GitHub: https://cli.github.com/
- GitLab: https://gitlab.com/gitlab-org/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 scwhen you’re on the exact commit you want to modify. - Use
gg absorbwhen staged edits belong to multiple commits and you want git-gud to distribute them.