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

gg split

Split a commit in the stack into two commits. The selected 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.
  • --no-tui: Disable TUI, use sequential prompt instead (legacy git add -p style).
  • --describe: Describe the target and its textual hunks as protocol v1 JSON. Requires --json and cannot be combined with interactive selection or file arguments.
  • --plan-json <PATH>: Apply a protocol v1 plan read from PATH. Requires --json and cannot be combined with --describe, interactive selection, or file arguments.
  • --json: Emit machine-readable output for structured Describe or Apply. Requires either --describe or --plan-json; ordinary interactive and file-based Split do not have a structured response.
  • -f, --force (alias --ignore-immutable): Override the immutability guard for interactive and file-based Split. Structured Describe/Apply always uses the guard without an override. Splitting a merged or base-ancestor commit is refused by default. See Core concepts · Immutable commits.
  • FILES...: Files to include in the new commit. When provided, all hunks from those files are auto-selected (skips the interactive picker).

How It Works

When you split commit K into two:

  1. New commit (K’) — Contains only the selected hunks. Inserted before K in the stack. Gets a new GG-ID.
  2. Remainder (K’’) — Contains the remaining 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"

Interactive Hunk Selection

Running gg split without file arguments opens the interactive hunk picker:

# Split the current commit — opens hunk selector
gg split

# Split a specific commit in the stack
gg split -c 3

TUI Mode (Default)

When run with a TTY, gg split 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 --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

File-Based Splitting

When file arguments are provided, all hunks from those files are auto-selected without opening the interactive picker:

# 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 with explicit files
gg split -c 3 src/config.rs

# Non-interactive with both messages
gg split -c 2 -m "Extract helpers" --no-edit helpers.rs utils.rs

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

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

Structured Clients

Native clients that provide their own hunk-selection UI can use a versioned, two-step protocol. Ordinary terminal use should keep the default TUI.

First, describe the current target without changing refs, requiring a clean worktree, or creating an operation record:

gg split --describe --commit c-abc1234 --json > split-description.json

The response contains the exact target, an opaque plan_token, selectable textual hunks, non_textual_files, and default messages. Copy target and plan_token unchanged, choose at least one returned hunk ID while leaving some textual, non-textual, or metadata change for the remainder, and supply both non-empty messages:

jq '{
  version,
  plan_token,
  target,
  selected_hunk_ids: [.hunks[0].id],
  first_message: "Extract validation",
  remainder_message: "Finish authentication"
}' split-description.json > split-plan.json

gg split --plan-json split-plan.json --json > split-result.json

The remainder must contain at least one change. It can be an unselected textual hunk, a file listed in non_textual_files, or a regular-file mode change such as an executable-bit update. Non-textual files and metadata are not selectable in protocol v1 and always remain in the remainder commit. Treat hunk IDs, plan tokens, GG-IDs, and operation IDs as opaque values; do not parse, construct, or remap them.

Apply re-resolves the target, checks its GG-ID/SHA/tree and the current ordered hunks, and rejects stale plans before moving refs or adding an operation-log record. After any stale-plan error, run Describe again and ask the user to review a new selection. Describe and Apply both refuse immutable targets, and structured Apply has no force field or force override.

Structured failures are written to stdout as JSON:

{
  "version": 1,
  "error": "stale split plan: target identity changed"
}

Once rewriting starts, a descendant rebase conflict returns the same JSON error envelope but leaves the operation paused. Resolve and stage the conflict, then run gg continue, or run gg abort to cancel it.

A successful Apply returns an operation_id. Use that exact ID to undo this split rather than assuming it is still the most recent operation:

operation_id=$(jq -r '.operation_id' split-result.json)
gg undo "$operation_id" --json

Edge Cases

  • All textual hunks selected — Structured Apply accepts the plan only when a non-textual file or regular-file mode change remains for the remainder.
  • No changes selected — Error in every mode.
  • Dirty working directory — Interactive Split and structured Apply error. Describe remains read-only and is allowed.
  • Merge conflicts during rebase — Resolve and stage the conflict, then run gg continue; use gg abort to cancel the paused operation.