split
Split a large branch into N stacked logical branches with hunk-level granularity
Works with
---
name: split
description: Split a large branch into N stacked logical branches with hunk-level granularity
license: MIT
---
# Split Branch into Stacked Logical Branches
You are executing the `/split` skill. Your job is to take the current branch's diff against a base branch, intelligently group changes into N logical units (with hunk-level granularity), and create stacked branches — each building on the previous — ready for separate PRs.
**CRITICAL: Follow these phases exactly. Do NOT skip Phase 4 (user approval).**
---
## Phase 0: Parse Arguments
The skill runner populates `$ARGUMENTS` with everything after `/split`.
Extract from `$ARGUMENTS`:
- **N** (optional integer, >= 2): Number of groups to split into. If omitted, you decide based on analysis. If N < 2, abort — splitting into fewer than 2 groups is a no-op.
- **--base <branch>** (optional): Base branch to diff against. Default: auto-detect (`main` or `master`).
- **--no-draft** (optional flag): If present, create PRs as ready for review. Default: PRs are created as drafts.
Examples:
- `/split 3` → N=3, base=auto, draft PRs
- `/split --base develop` → N=auto, base=develop, draft PRs
- `/split 4 --base main` → N=4, base=main, draft PRs
- `/split 2 --no-draft` → N=2, base=auto, ready-for-review PRs
---
## Phase 1: Pre-flight Checks
Run these checks **in order**. Abort with a clear message if any fail.
```bash
# 1. Verify git repo
git rev-parse --git-dir
# 2. Check for clean worktree (no uncommitted changes, no untracked files)
git diff --quiet && git diff --cached --quiet
# Also check for untracked files that could be accidentally staged
git ls-files --others --exclude-standard
# 3. Get current branch name (fails in detached HEAD)
CURRENT_BRANCH=$(git symbolic-ref --short HEAD)
# If this fails: "You are in detached HEAD state. Please checkout a named branch first."
# 4. Detect base branch (if not provided)
# Try: main, master — whichever exists on the remote
BASE_BRANCH=${provided_base:-$(git branch -r | grep -oE 'origin/(main|master)' | head -1 | sed 's|origin/||')}
# If BASE_BRANCH is empty: "Could not detect base branch. Use --base <branch> to specify."
# 5. Guard: current branch must not be the base branch
# If CURRENT_BRANCH == BASE_BRANCH: "You are on the base branch. Switch to a feature branch first."
# 6. Verify split_diff.py exists
SPLIT_SCRIPT="${CLAUDE_PLUGIN_ROOT}/skills/split/scripts/split_diff.py"
# If not found: "split_diff.py not found at $SPLIT_SCRIPT. Is the skill installed?"
# 7. Verify gh CLI is available (needed for Phase 6)
command -v gh
# If missing: warn "gh CLI not found — branches will be created but PRs must be created manually."
# 8. Find merge base
MERGE_BASE=$(git merge-base HEAD "origin/$BASE_BRANCH")
# 9. Verify there are changes to split
git diff --name-only "$MERGE_BASE"..HEAD
# If empty: "No changes found between $CURRENT_BRANCH and $BASE_BRANCH."
```
If untracked files are present, tell the user: "Untracked files detected. These could be accidentally included. Please commit, stash, or .gitignore them first." Then list the files.
If the worktree is dirty, tell the user: "Working tree has uncommitted changes. Please commit or stash them first."
Print a summary:
```
Branch: $CURRENT_BRANCH
Base: $BASE_BRANCH
Merge base: ${MERGE_BASE:0:8}
Script: $SPLIT_SCRIPT
gh CLI: available / not found (PRs will be skipped)
```
---
## Phase 2: Analyze the Diff
Run these commands to gather the full picture:
```bash
# File-level summary
git diff --name-status $MERGE_BASE..HEAD
# Stats
git diff --stat $MERGE_BASE..HEAD
# Full diff (for your analysis)
git diff $MERGE_BASE..HEAD
```
Also run the analyze helper to get structured hunk data:
```bash
git diff "$MERGE_BASE"..HEAD | python3 "$SPLIT_SCRIPT" analyze
```
Display a summary table for the user:
```
## Changes to split
| File | Status | Hunks | +/- |
|------|--------|-------|-----|
| src/models/User.ts | Added | 1 | +45 |
| src/api/routes.ts | Modified | 3 | +22/-5 |
| ... | ... | ... | ... |
Total: X files changed, Y insertions, Z deletions
```
---
## Phase 3: Propose a Split Plan
Analyze ALL the changes and group them into N logical units. Consider:
1. **Dependency order** — Models/types before logic before API routes before tests
2. **Layer separation** — Data layer, business logic, presentation, tests
3. **Feature cohesion** — Related changes stay together
4. **Reviewability** — Each group should make sense as a standalone review
5. **Hunk granularity** — If a file has changes for multiple groups, split by hunks
**Branch naming:** Derive from the current branch name by appending `-{number}-{short-name}`.
- `Iron-Ham/big-feature` → `Iron-Ham/big-feature-1-models`, `Iron-Ham/big-feature-2-api`
- `feature/auth` → `feature/auth-1-models`, `feature/auth-2-routes`
**Build the spec mentally, then present the plan:**
```
## Proposed Split Plan
### Branch 1: Iron-Ham/big-feature-1-models
> feat: add user and auth data models
| File | Change | Hunks |
|------|--------|-------|
| src/models/User.ts | A (whole file) | all |
| src/models/Auth.ts | A (whole file) | all |
| src/index.ts | M (partial) | #0, #1 |
### Branch 2: Iron-Ham/big-feature-2-api
> feat: add authentication API routes
> (includes all changes from branch 1)
| File | Change | Hunks |
|------|--------|-------|
| src/api/auth.ts | A (whole file) | all |
| src/api/routes.ts | M (all hunks) | all |
| src/index.ts | M (partial) | #2 |
...
```
Make clear that branches are **stacked** — each includes all changes from previous branches plus its own.
---
## Phase 4: User Approval
**STOP HERE. You MUST wait for user input.**
Ask the user:
> Does this split plan look good? You can:
> - **Approve** — I'll create the branches
> - **Adjust** — Tell me what to move between groups
> - **Cancel** — Abort without changes
Use AskUserQuestion with options: "Approve", "Adjust", "Cancel"
If the user adjusts, revise the plan and ask again. If the user cancels, stop immediately.
---
## Phase 5: Execute the Split
**Important rules:**
- NEVER modify the original branch
- Track all created branches for rollback messaging
- Each branch is based on the merge base, with cumulative changes applied
### Setup:
Create a unique temp directory for this run:
```bash
SPLIT_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/split-XXXXXXXX")
```
### Execution algorithm:
```
For each group K (1 to N):
1. Start from merge base:
git checkout -b "$BRANCH_NAME" "$MERGE_BASE"
# If this fails (e.g., branch already exists), abort with rollback guidance.
2. For each file in groups 1..K (cumulative):
Apply changes based on type (see table below)
# Check exit code after EVERY git checkout / git rm / python3 command.
# On any failure, abort and print rollback guidance.
3. Stage specific files and commit:
git add <file1> <file2> ... # explicit files only, NOT git add -A
git commit -m "$COMMIT_MESSAGE"
# Use conventional commit format (e.g., "feat: ...", "refactor: ...")
```
**Error checking is mandatory.** After every git or python3 command, verify it succeeded. If any step fails, immediately jump to the On Failure section below.
### Change application methods:
For each file, determine the method based on change type and hunk selection:
| Scenario | Method |
|----------|--------|
| Added file (A), hunks = "all" | `git checkout $ORIGINAL_BRANCH -- $FILE` |
| Modified file (M), hunks = "all" | `git checkout $ORIGINAL_BRANCH -- $FILE` |
| Modified file (M), partial hunks | Use reconstruct (see below) |
| Deleted file (D) | `git rm $FILE` |
| Renamed file (R) | `git checkout $ORIGINAL_BRANCH -- $NEW_PATH` then `git rm $OLD_PATH` |
| Binary file | `git checkout $ORIGINAL_BRANCH -- $FILE` |
### For partial hunk application:
For each file needing partial hunks in group K, reconstruct it using the temp directory:
```bash
# Get base file content
git show "$MERGE_BASE:$FILE_PATH" > "$SPLIT_TMPDIR/base_file" || { echo "Error: ..."; exit 1; }
# Get the diff for this file
git diff "$MERGE_BASE".."$ORIGINAL_BRANCH" -- "$FILE_PATH" > "$SPLIT_TMPDIR/file_diff" || { echo "Error: ..."; exit 1; }
# Reconstruct with hunks from groups 1..K
python3 "$SPLIT_SCRIPT" reconstruct \
--base-file "$SPLIT_TMPDIR/base_file" \
--diff-file "$SPLIT_TMPDIR/file_diff" \
--hunks 0,1,3,5 \
--output "$FILE_PATH" || { echo "Error: reconstruction failed for $FILE_PATH"; exit 1; }
```
The `--hunks` flag takes a comma-separated list of 0-based hunk indices to apply.
### After creating all branches:
Verify the final stacked branch matches the original:
```bash
git diff "$LAST_BRANCH" "$ORIGINAL_BRANCH" --stat
```
If this shows differences, something went wrong during reconstruction. Warn the user and print rollback guidance.
Return to the original branch:
```bash
git checkout "$ORIGINAL_BRANCH"
```
Clean up the temp directory:
```bash
rm -rf "$SPLIT_TMPDIR"
```
### On failure:
If any step fails:
1. Print which branches were created
2. Print cleanup commands: `git branch -D $BRANCH1 $BRANCH2 ...`
3. Return to the original branch
4. **Never auto-delete branches**
---
## Phase 6: Push, Create PRs, and Summarize
**Skip PR creation if `gh` CLI was not found in Phase 1.** In that case, push branches and print manual PR creation commands instead.
### 6a. Push all branches
Push each branch, checking for errors:
```bash
git push -u origin "$BRANCH_1" || echo "Warning: failed to push $BRANCH_1"
git push -u origin "$BRANCH_2" || echo "Warning: failed to push $BRANCH_2"
# ... for each branch
```
If any push fails, report which branches were pushed successfully and which failed, then continue with the branches that did push.
### 6b. Create PRs
Create a PR for each branch. **PRs are drafts by default** unless `--no-draft` was passed.
- The first PR targets the base branch (e.g., `main`).
- Each subsequent PR targets the previous stacked branch.
- PR titles should be human-readable descriptions (no conventional commit prefixes).
- PR bodies must include `## Summary`, `## Stack`, and `## Test plan` sections.
```bash
# Determine draft flag
DRAFT_FLAG="--draft" # omit if --no-draft was passed
# PR 1 targets base
gh pr create $DRAFT_FLAG --base main --head "$BRANCH_1" \
--title "Add user and auth data models" \
--body "$(cat <<'EOF'
## Summary
- First in a stack of N PRs from `$ORIGINAL_BRANCH`
- <describe changes>
## Stack
1. **This PR** ← `$BRANCH_1`
2. TBD ← `$BRANCH_2`
## Test plan
- [ ] <describe how to verify these changes>
EOF
)"
# PR 2 targets PR 1's branch
gh pr create $DRAFT_FLAG --base "$BRANCH_1" --head "$BRANCH_2" \
--title "Add authentication API routes" \
--body "$(cat <<'EOF'
## Summary
- Second in a stack of N PRs from `$ORIGINAL_BRANCH`
- <describe changes>
## Stack
1. #PR1_NUMBER ← `$BRANCH_1`
2. **This PR** ← `$BRANCH_2`
## Test plan
- [ ] <describe how to verify these changes>
EOF
)"
```
After creating all PRs:
1. **Update earlier PR bodies** with actual PR numbers/URLs for the stack list.
2. If any `gh pr create` fails, report the error and print the equivalent manual command.
### 6c. Print summary
```
## Split Complete!
Created N stacked branches from `$ORIGINAL_BRANCH`:
| # | Branch | PR | Status |
|---|--------|----|--------|
| 1 | $BRANCH_1 | #1 (draft) | ✓ |
| 2 | $BRANCH_2 | #2 (draft) | ✓ |
Original branch `$ORIGINAL_BRANCH` is unchanged.
```
---
## Reminders
- **NEVER modify the original branch.** All work happens on new branches created from the merge base.
- **ALWAYS wait for user approval in Phase 4.** Never skip ahead.
- **ALWAYS check exit codes** after every git and python3 command. Abort on failure.
- **Cumulative stacking:** Branch K contains changes from groups 1 through K.
- **Hunk indices are 0-based** in split_diff.py.
- **Use explicit file staging** (`git add <files>`) — never `git add -A`.
- **Use conventional commit format** for commit messages (e.g., `feat:`, `fix:`, `refactor:`).
- If N is not specified, choose a reasonable number (typically 2-5) based on the logical structure of the changes.More Git Workflows skills
git-commit
github/awesome-copilot
Execute git commit with conventional commit message analysis, intelligent staging, and message generation. Use when user asks to commit changes, create a git commit, or mentions "/commit". Supports: (1) Auto-detecting type and scope from changes, (2) Generating conventional commit messages from diff, (3) Interactive commit with optional type/scope/description overrides, (4) Intelligent file staging for logical grouping
git-workflow-and-versioning
addyosmani/agent-skills
Structures git workflow practices. Use when making any code change. Use when committing, branching, resolving conflicts, or when you need to organize work across multiple parallel streams. Use when cutting a release, choosing a semantic version bump, tagging, or writing a changelog.
resolve-merge-conflicts
warpdotdev/common-skills
Resolve Git merge conflicts by extracting only unresolved paths, conflict hunks, and compact diffs instead of loading whole files into context. Use when a merge, rebase, cherry-pick, or stash pop stops on conflicts, when `git status` shows unmerged paths, or when files contain conflict markers.

