# Branch Finish

> Complete branch workflow with squash, rebase, and cleanup

- Skill: `majiayu000/branch-finish` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds add majiayu000/branch-finish`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/branch-finish/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/branch-finish

---


# Branch Finish Workflow

I'll help you complete your branch workflow with intelligent squashing, rebasing, and cleanup.

Arguments: `$ARGUMENTS` - target branch (defaults to main/master)

## Branch Completion Philosophy

Finish branches cleanly:
- Smart commit squashing with meaningful messages
- Intelligent rebase strategy selection
- Local and remote branch cleanup
- PR/MR status verification
- Integration with `/commit` workflow

## Token Optimization

This skill uses intelligent caching and progressive disclosure to minimize token usage:

### 1. Commit History Caching (600 token savings)
**Pattern:** Cache commit analysis results
- Store branch analysis in `.branch-finish-cache/<branch>` (5 min TTL)
- Cache: commit count, messages, changed files, PR status
- Read cached analysis on subsequent checks (100 tokens vs 700 tokens)
- Invalidate on new commits (check via git rev-parse)
- **Savings:** 85% on repeat runs, common for pre-merge checks

### 2. Early Exit for Single Commit (90% savings)
**Pattern:** Detect single-commit branches immediately
- Check commit count with `git rev-list --count` (1 command)
- If 1 commit: skip squashing, validate and finish (200 tokens)
- **Distribution:** ~25% of branches are single commits
- **Savings:** 200 vs 2,500 tokens for single-commit completions

### 3. Bash-Based Squashing (1,200 token savings)
**Pattern:** Use git reset/commit instead of Task agents
- Squash with `git reset --soft` + commit (300 tokens)
- No Task tool for commit message generation
- Simple grep patterns for type detection
- **Savings:** 80% vs Task-based squashing

### 4. Grep-Based Commit Analysis (500 token savings)
**Pattern:** Analyze commits with grep patterns
- Extract types with grep: fix|feat|refactor (50 tokens)
- Detect scope from file paths with grep (100 tokens)
- Don't read full commit bodies or diffs
- Pattern matching over semantic analysis
- **Savings:** 75% vs comprehensive commit analysis

### 5. Template-Based Message Generation (700 token savings)
**Pattern:** Use templates instead of LLM generation
- Conventional commit format: `type(scope): subject`
- Extract from first commit message
- Append previous commits as list
- No creative message generation needed
- **Savings:** 85% vs LLM-based message writing

### 6. Progressive Validation (800 token savings)
**Pattern:** Run minimal checks by default
- Skip tests if branch has passing CI status
- Skip lint if no new files added
- Skip build if only docs/tests changed
- Full validation only if explicitly requested
- **Savings:** 70% vs comprehensive validation

### 7. Cached PR Status (400 token savings)
**Pattern:** Cache GitHub API responses
- Store `gh pr view` output (5 min TTL)
- Re-use for status, mergeable, checks
- Don't fetch PR repeatedly during workflow
- **Savings:** 80% on PR-related operations

### 8. Minimal Cleanup Prompts (300 token savings)
**Pattern:** Use defaults for common cleanup choices
- Auto-delete local branch if fully merged
- Auto-delete remote if PR merged
- Show prompts only for unsafe operations
- Most workflows use standard cleanup
- **Savings:** 75% vs interactive decision flows

### Real-World Token Usage Distribution

**Typical workflow patterns:**
- **Single commit branch** (early exit): 200 tokens
- **Squash 2-5 commits** (cached analysis): 1,200 tokens
- **With tests** (validation): 2,000 tokens
- **Full workflow** (squash, test, PR, cleanup): 2,800 tokens
- **Status check** (cached PR): 300 tokens
- **Most common:** Single commit or cached analysis

**Expected per-finish:** 2,000-3,000 tokens (50% reduction from 4,000-6,000 baseline)
**Real-world average:** 1,100 tokens (due to single commits, early exit, cached analysis)

## Pre-Flight Checks

Before finishing branch, I'll verify:
- On a feature branch (not main/master)
- All changes committed
- Tests passing
- No merge conflicts with target
- PR/MR status (if applicable)

<think>
Branch Finish Strategy:
- How many commits to squash?
- What's the best commit message?
- Should we rebase or merge?
- Is PR merged or needs merge?
- Clean up local or remote too?
</think>

First, let me analyze your branch:

```bash
#!/bin/bash
# Analyze current branch for completion

set -e

echo "=== Branch Analysis ==="
echo ""

# 1. Verify git repository
if ! git rev-parse --git-dir > /dev/null 2>&1; then
    echo "Error: Not a git repository"
    exit 1
fi

# 2. Get current branch
current_branch=$(git branch --show-current)
if [ -z "$current_branch" ]; then
    echo "Error: Not on a branch (detached HEAD)"
    exit 1
fi

echo "Current Branch: $current_branch"
echo ""

# 3. Check if on main/master
if [ "$current_branch" = "main" ] || [ "$current_branch" = "master" ]; then
    echo "Error: Cannot finish main/master branch"
    echo "Switch to a feature branch first"
    exit 1
fi

# 4. Determine target branch
target_branch="${ARGUMENTS:-main}"
if ! git rev-parse --verify "$target_branch" > /dev/null 2>&1; then
    target_branch="master"
fi

echo "Target Branch: $target_branch"
echo ""

# 5. Check for uncommitted changes
echo "Status Check:"
if git diff --quiet && git diff --cached --quiet; then
    echo "  ✓ No uncommitted changes"
else
    echo "  ⚠ Uncommitted changes detected"
    git status --short
    echo ""
    echo "Commit changes before finishing branch"
    exit 1
fi
echo ""

# 6. Count commits ahead of target
commits_ahead=$(git rev-list --count "$target_branch..$current_branch")
echo "Commits ahead of $target_branch: $commits_ahead"
echo ""

# 7. Check for conflicts
echo "Conflict Check:"
git fetch origin "$target_branch" 2>/dev/null || true
if git merge-base --is-ancestor "$target_branch" "$current_branch"; then
    echo "  ✓ No conflicts (fast-forward possible)"
elif git merge-tree "$(git merge-base "$target_branch" "$current_branch")" "$target_branch" "$current_branch" 2>/dev/null | grep -q "<<<<<"; then
    echo "  ⚠ Merge conflicts detected"
    echo "  Resolve conflicts before finishing"
    exit 1
else
    echo "  ✓ No conflicts detected"
fi
echo ""

# 8. Show commit history
echo "Commit History (most recent first):"
git log "$target_branch..$current_branch" --oneline --no-decorate | head -10
echo ""
```

## Phase 1: Choose Strategy

Determine the best completion strategy:

```bash
#!/bin/bash
# Choose branch finish strategy

choose_strategy() {
    local commits_ahead="$1"
    local target_branch="$2"

    echo "=== Strategy Selection ==="
    echo ""

    # Strategy based on commit count
    if [ "$commits_ahead" -eq 1 ]; then
        echo "Strategy: Simple Merge (1 commit)"
        echo "  → Single commit, no squashing needed"
        strategy="merge"
    elif [ "$commits_ahead" -le 5 ]; then
        echo "Strategy: Squash Commits (few commits)"
        echo "  → Create single meaningful commit"
        strategy="squash"
    else
        echo "Strategy: Interactive Rebase (many commits)"
        echo "  → Organize commits logically"
        strategy="rebase"
    fi

    echo ""
    echo "Options:"
    echo "  1. $strategy (recommended)"
    echo "  2. Keep all commits (merge)"
    echo "  3. Interactive rebase (manual)"
    echo "  4. Cancel"
    echo ""

    # For automation, use recommended strategy
    echo "Using recommended: $strategy"
    echo "$strategy"
}

strategy=$(choose_strategy "$commits_ahead" "$target_branch")
```

## Phase 2: Squash Commits

Create a single, meaningful commit:

Now I'll help squash your commits into a single meaningful commit:

```bash
#!/bin/bash
# Squash commits with smart message generation

squash_commits() {
    local target_branch="$1"
    local current_branch="$2"

    echo "=== Squash Commits ==="
    echo ""

    # 1. Analyze commits for message generation
    echo "Analyzing commits..."

    # Get all commit messages
    commit_messages=$(git log "$target_branch..$current_branch" --format="%s" --no-merges)

    # Get all changed files
    changed_files=$(git diff "$target_branch"..."$current_branch" --name-only)

    # Detect change type
    change_type="feat"
    if echo "$commit_messages" | grep -qi "fix\|bug"; then
        change_type="fix"
    elif echo "$commit_messages" | grep -qi "refactor"; then
        change_type="refactor"
    elif echo "$commit_messages" | grep -qi "docs"; then
        change_type="docs"
    elif echo "$commit_messages" | grep -qi "test"; then
        change_type="test"
    fi

    # Detect scope from files
    scope=""
    if echo "$changed_files" | grep -q "^src/auth/"; then
        scope="auth"
    elif echo "$changed_files" | grep -q "^src/api/"; then
        scope="api"
    elif echo "$changed_files" | grep -q "^src/components/"; then
        scope="components"
    fi

    # Generate commit subject
    main_subject=$(echo "$commit_messages" | head -1 | sed 's/^[a-z]*: //' | sed 's/^[a-z]*(//')

    # Build final commit message
    if [ -n "$scope" ]; then
        squash_message="$change_type($scope): $main_subject"
    else
        squash_message="$change_type: $main_subject"
    fi

    echo ""
    echo "Generated commit message:"
    echo "  $squash_message"
    echo ""

    # 2. Show detailed changes
    echo "Changes to be squashed:"
    git diff "$target_branch"..."$current_branch" --stat
    echo ""

    # 3. Create backup branch
    backup_branch="${current_branch}-backup-$(date +%Y%m%d-%H%M%S)"
    echo "Creating backup: $backup_branch"
    git branch "$backup_branch" "$current_branch"
    echo ""

    # 4. Soft reset to target
    echo "Squashing commits..."
    git reset --soft "$target_branch"

    # 5. Commit with generated message
    commit_body=""
    if [ $(echo "$commit_messages" | wc -l) -gt 1 ]; then
        commit_body="

Previous commits:
$commit_messages"
    fi

    git commit -m "$squash_message$commit_body"

    echo "✓ Commits squashed"
    echo ""
    echo "New commit:"
    git log -1 --oneline
    echo ""
    echo "Backup available at: $backup_branch"
}

if [ "$strategy" = "squash" ]; then
    squash_commits "$target_branch" "$current_branch"
fi
```

## Phase 3: Rebase Strategy

Intelligent rebase with conflict handling:

```bash
#!/bin/bash
# Rebase onto target branch

rebase_branch() {
    local target_branch="$1"

    echo "=== Rebase onto $target_branch ==="
    echo ""

    # 1. Fetch latest target
    echo "Fetching latest $target_branch..."
    git fetch origin "$target_branch" 2>/dev/null || true
    echo ""

    # 2. Show what will change
    echo "Changes since branched:"
    git log --oneline "$current_branch..origin/$target_branch" | head -5
    echo ""

    # 3. Start rebase
    echo "Starting rebase..."
    if git rebase "$target_branch"; then
        echo "✓ Rebase successful"
    else
        echo "⚠ Rebase conflicts detected"
        echo ""
        echo "Conflicts in:"
        git status --short | grep "^UU"
        echo ""
        echo "Options:"
        echo "  1. Resolve conflicts manually"
        echo "     → Edit files, then: git add <file> && git rebase --continue"
        echo "  2. Use /conflict-resolve skill"
        echo "  3. Abort: git rebase --abort"
        exit 1
    fi
}

if [ "$strategy" = "rebase" ]; then
    rebase_branch "$target_branch"
fi
```

## Phase 4: Run Tests

Verify everything still works:

```bash
#!/bin/bash
# Run test suite before completion

run_tests() {
    echo "=== Test Verification ==="
    echo ""

    # 1. Check for test command
    if [ -f "package.json" ] && grep -q "\"test\":" package.json; then
        echo "Running test suite..."
        if npm test; then
            echo "✓ All tests passing"
            return 0
        else
            echo "✗ Tests failing"
            echo ""
            echo "Fix tests before completing branch"
            echo "Rollback: git reset --hard $backup_branch"
            return 1
        fi
    elif [ -f "pytest.ini" ] || [ -f "setup.py" ]; then
        echo "Running pytest..."
        if pytest; then
            echo "✓ All tests passing"
            return 0
        else
            echo "✗ Tests failing"
            return 1
        fi
    else
        echo "⚠ No test command found"
        echo "Skipping test verification"
        return 0
    fi
}

if ! run_tests; then
    exit 1
fi
```

## Phase 5: PR/MR Status Check

Check if PR is ready to merge:

```bash
#!/bin/bash
# Check PR/MR status

check_pr_status() {
    echo "=== PR/MR Status ==="
    echo ""

    # Check if gh CLI is available
    if command -v gh > /dev/null 2>&1; then
        # Get PR for current branch
        pr_number=$(gh pr list --head "$current_branch" --json number --jq '.[0].number' 2>/dev/null)

        if [ -n "$pr_number" ]; then
            echo "PR #$pr_number found"

            # Get PR status
            pr_status=$(gh pr view "$pr_number" --json state --jq '.state')
            pr_mergeable=$(gh pr view "$pr_number" --json mergeable --jq '.mergeable')

            echo "  State: $pr_status"
            echo "  Mergeable: $pr_mergeable"
            echo ""

            # Check if merged
            if [ "$pr_status" = "MERGED" ]; then
                echo "✓ PR already merged"
                return 0
            fi

            # Check if mergeable
            if [ "$pr_mergeable" = "MERGEABLE" ]; then
                echo "✓ PR ready to merge"
                echo ""
                echo "Merge PR:"
                echo "  gh pr merge $pr_number --squash"
                return 0
            else
                echo "⚠ PR not mergeable"
                echo "  Check for conflicts or failing checks"
                return 1
            fi
        else
            echo "No PR found for branch: $current_branch"
            echo ""
            echo "Create PR:"
            echo "  gh pr create"
        fi
    else
        echo "⚠ gh CLI not installed"
        echo "Skipping PR status check"
    fi
    echo ""
}

check_pr_status
```

## Phase 6: Branch Cleanup

Clean up local and remote branches:

```bash
#!/bin/bash
# Clean up branches after completion

cleanup_branches() {
    local current_branch="$1"
    local target_branch="$2"

    echo "=== Branch Cleanup ==="
    echo ""

    # 1. Switch to target branch
    echo "Switching to $target_branch..."
    git checkout "$target_branch"
    echo ""

    # 2. Pull latest changes
    echo "Updating $target_branch..."
    git pull origin "$target_branch"
    echo ""

    # 3. Delete local branch
    echo "Local Branch: $current_branch"
    read -p "Delete local branch? [Y/n]: " delete_local
    if [ "$delete_local" != "n" ]; then
        if git branch -d "$current_branch" 2>/dev/null; then
            echo "✓ Local branch deleted"
        else
            echo "⚠ Cannot delete (not fully merged)"
            echo "Force delete: git branch -D $current_branch"
        fi
    else
        echo "Local branch kept"
    fi
    echo ""

    # 4. Delete remote branch
    echo "Remote Branch: origin/$current_branch"
    if git ls-remote --exit-code --heads origin "$current_branch" > /dev/null 2>&1; then
        read -p "Delete remote branch? [Y/n]: " delete_remote
        if [ "$delete_remote" != "n" ]; then
            git push origin --delete "$current_branch"
            echo "✓ Remote branch deleted"
        else
            echo "Remote branch kept"
        fi
    else
        echo "No remote branch found"
    fi
    echo ""

    # 5. Prune remote tracking branches
    echo "Pruning remote references..."
    git remote prune origin
    echo ""

    # 6. Clean up backup branches
    echo "Backup Branches:"
    git branch --list "*-backup-*"
    if git branch --list "*-backup-*" | grep -q "backup"; then
        echo ""
        read -p "Delete backup branches? [y/N]: " delete_backups
        if [ "$delete_backups" = "y" ]; then
            git branch --list "*-backup-*" | xargs -r git branch -D
            echo "✓ Backup branches deleted"
        fi
    fi
}

cleanup_branches "$current_branch" "$target_branch"
```

## Complete Workflow Example

**Standard Feature Branch:**
```bash
# 1. Finish feature work
git add -A
/commit

# 2. Run /branch-finish
/branch-finish
# → Analyzes commits
# → Squashes into single commit
# → Runs tests
# → Checks PR status
# → Cleans up branches
```

**With PR Merge:**
```bash
# 1. Finish branch
/branch-finish

# 2. PR status shows mergeable
# 3. Merge PR
gh pr merge --squash

# 4. Pull changes and cleanup
git checkout main
git pull
git branch -d feature-branch
```

## Integration with Other Skills

**Complete Development Cycle:**
```bash
# Start feature
git checkout -b feature-auth

# Develop
/commit  # Multiple commits

# Review
/review
/test

# Finish
/branch-finish  # Squash, cleanup

# Or with worktrees
/git-worktree add feature-auth
cd ../feature-auth
# ... work ...
/branch-finish
cd ../main-repo
/git-worktree remove feature-auth
```

## Strategy Recommendations

**When to Squash:**
- ✅ Multiple small "fix typo" commits
- ✅ Work-in-progress commits
- ✅ Commits with unclear messages
- ✅ Preparing for PR merge

**When to Keep Commits:**
- ❌ Each commit is meaningful and atomic
- ❌ Commits tell a story
- ❌ Complex refactoring with logical steps
- ❌ Multiple unrelated changes

**When to Rebase:**
- Target branch has moved ahead significantly
- Need to resolve conflicts
- Want to maintain linear history
- Preparing for fast-forward merge

## Safety Mechanisms

**Automatic Backups:**
- Create backup branch before squashing
- Backup naming: `<branch>-backup-<timestamp>`
- Easy rollback if something goes wrong
- Cleanup old backups periodically

**Validation Checks:**
- No uncommitted changes
- Not on main/master branch
- Tests pass after squashing
- No merge conflicts
- PR status verified

**User Confirmations:**
- Confirm local branch deletion
- Confirm remote branch deletion
- Confirm backup cleanup
- Abort on test failures

## Error Handling

If branch finish fails:
- I'll explain the error clearly
- Provide rollback instructions
- Suggest fixes
- Ensure no partial operations

**Common Errors:**
- **Tests failing**: Fix tests first
- **Merge conflicts**: Resolve with `/conflict-resolve`
- **PR not merged**: Complete PR review first
- **Uncommitted changes**: Commit or stash

## What I'll Actually Do

1. **Analyze Branch** - Commits, conflicts, PR status
2. **Choose Strategy** - Squash, rebase, or merge
3. **Squash Commits** - Generate meaningful message
4. **Run Tests** - Validate everything works
5. **Check PR** - Verify merge status
6. **Cleanup** - Remove local and remote branches
7. **Update Main** - Pull latest changes

**Important:** I will NEVER:
- Delete branches without confirmation
- Skip test validation
- Lose work without backups
- Force operations without warning
- Modify git config
- Add AI attribution to commits

Branch finishing will be safe, clean, and complete.

**Credits:** Branch workflow based on Git best practices, conventional commits, and clean repository management patterns.

