Receber por email
← Voltar para o catálogo

epic-merge

Sequential squash-merge of stacked PR chains into an epic branch. Handles dependency-ordered rebase, collision-safe backup tags, CI monitoring (delegates to /watch-ci), and post-merge verification. Use when: merging a chain of stacked PRs into an epic branch, collapsing a linear PR stack into per-PR squash commits, preparing an epic branch for final review. Triggers on: 'merge PRs into epic', 'squ

172estrelas
Atualizado há 5 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add sd0xdev/sd0x-dev-flow

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/epic-merge/svg)](https://www.skillteca.com.br/skills/epic-merge)
#ai

Skills relacionadas

Ver todas de DevOps e Infra →

Alerta por categoria

Receba novas skills de DevOps e Infra toda segunda

1 email curto, só com as skills novas de DevOps e Infra. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Epic Merge — Stacked PR Chain Squash-Merge

Sequentially squash-merge a chain of stacked PRs into an epic branch, producing one squash commit per PR for clean per-PR review on the epic. Every destructive iteration is gated by AskUserQuestion to keep the operator in control.

When NOT to Use

  • Single PR merge — use /create-pr + GitHub UI
  • Simple rebase without stacked dependencies — use /smart-rebase
  • Pre-merge conflict / impact analysis only — use /merge-prep
  • Diamond / parallel merge chains — this skill handles linear chains only
  • Repos that use merge-commit or rebase-merge — this skill assumes squash-merge only

Permissions

This skill is one of the explicit exceptions in @rules/git-workflow.md allowed to execute git rebase --onto, git push --force-with-lease, and gh pr merge --squash. Every destructive step is gated by AskUserQuestion.

| Phase | Operation | Mutates | Approval | |-------|-----------|---------|----------| | Phase 1 backup | git fetch, git tag -f | local refs only | No (no remote / non-recoverable mutation) | | Phase 2 iteration | rebase + force-push + gh pr merge | local + remote | Yes — single bundled gate per iteration (or per-step with --per-step) | | Phase 3 verify | git log | none | No (read-only) |

--dry-run skips all destructive steps and outputs the plan only.

Core Concept

After squash-merging PR N, the original commits are replaced by a single squash commit on epic. PR N+1 still contains N's original commits as its base — these must be cut via git rebase --onto before merging N+1. The cut point is the original tip of PR N's branch, captured in Phase 1 as a backup tag.

epic:    E ─── S_N (squash of PR N)
PR N+1:  E ─ A1 ─ A2 ─ ... ─ B1 ─ B2
              └── drop (in S_N) ──┘ └─ keep ─┘

After: git rebase --onto epic backup/pr-<N> PR_N+1
epic:    E ─── S_N ─── B1' ─ B2'

Workflow

sequenceDiagram
    participant U as User
    participant E as /epic-merge
    participant W as /watch-ci
    participant GH as GitHub
    E->>E: Phase 0 — analyze chain (linear?)
    E->>GH: Phase 1 — fetch + create backup tags
    Note over E,GH: Iteration 1 — direct squash (no rebase needed)
    E->>U: AskUserQuestion (bundled gate)
    U-->>E: Proceed / Dry-run / Abort
    E->>GH: gh pr merge --squash
    E->>GH: fetch updated epic
    Note over E,GH: Iteration 2..N — rebase + force-push + CI + merge
    loop For each remaining PR
        E->>U: AskUserQuestion (bundled gate)
        U-->>E: Proceed / Per-step / Dry-run / Abort
        E->>E: rebase --onto epic backup/pr-<prev>
        E->>E: verify manifest (subject + count)
        E->>GH: push --force-with-lease
        E->>GH: gh pr edit --base epic
        E->>W: /watch-ci --sha <sha> --branch <head> --timeout <ci-timeout>
        W-->>E: PASS / FAIL verdict
        E->>GH: gh pr merge --squash
        E->>GH: fetch updated epic
    end
    E->>E: Phase 3 — verify final epic log

Phase 0: Analyze PR Chain

# For each PR, get head/base branch and unique commit count
gh pr view <N> --json number,headRefName,baseRefName,title,state
git log origin/<base>..origin/<head> --oneline | wc -l

Output a chain table:

| Order | PR | Head Branch | Base Branch | Unique Commits | State | |-------|-----|-------------|-------------|----------------|-------| | 1 | #100 | feat/A | epic/xxx | N | OPEN | | 2 | #101 | feat/B | feat/A | N | OPEN | | ... | ... | ... | ... | ... | ... |

Validation gate — abort if any of:

  • A PR's base is not the previous PR's head (chain not linear)
  • Any PR is not OPEN
  • Any PR has uncommitted local changes on its head branch
  • Working tree is dirty (git status --porcelain non-empty)

Phase 1: Pre-flight Backup

Creates safety nets. Original branch tips and PR-level commit fingerprints persist as git tags + manifest files so they survive shell session loss.

git fetch origin

# Collision-safe backup tags keyed by PR number (NOT branch basename)
for pr in <PR-numbers>; do
  head_branch=$(gh pr view "$pr" --json headRefName -q .headRefName)
  git tag -f "backup/pr-${pr}" "origin/${head_branch}"
done

# Stable manifest per PR (subject-only — survives SHA rewrite during rebase)
for pr in <PR-numbers>; do
  head=$(gh pr view "$pr" --json headRefName -q .headRefName)
  base=$(gh pr view "$pr" --json baseRefName -q .baseRefName)
  git log "origin/${base}..origin/${head}" --pretty=format:'%s' > ".epic-merge-pr-${pr}.manifest"
done

Why backup/pr-<N>: branch basenames collide (feat/foo vs fix/foo both become foo). PR numbers are globally unique within the repo. Why subject-only manifest: rebase rewrites SHAs; commit subjects are stable across rebases (assuming no --squash/--fixup mid-rebase). Subject + count is the right invariant to verify. Why origin refs: local branches drift; origin/* is SSOT. Why tags: shell variables die on session interruption; tags persist in .git/refs/tags/.

Phase 2: Sequential Merge Loop (gated)

Iteration 1 (First PR) — direct squash, no rebase

# AskUserQuestion gate (see Iteration Gate Design below)
# On Proceed:
gh pr merge <first-PR> --squash
git fetch origin <epic-branch>

Iteration 2..N — gate first, then rebase + force-push + CI + merge

For each subsequent PR (PR <N> with head branch <head>, previous PR was <prev>):

# Step 1: AskUserQuestion BEFORE any destructive op (see Gate Design)
#   On Proceed: continue Steps 2-8 atomically
#   On Per-step: re-prompt before push (Step 5) and merge (Step 7)
#   On Dry-run: print Steps 2-8 commands, do not execute
#   On Abort: stop, leave backup tags in place

# Step 2: Checkout fresh from remote
git switch -C "<head>" "origin/<head>"

# Step 3: Rebase — cut already-squashed commits, replay unique ones onto epic
git rebase --onto "origin/<epic>" "backup/pr-<prev>" "<head>"

# Step 4: Verify manifest (subject + count, NOT SHA)
git log "origin/<epic>..<head>" --pretty=format:'%s' > ".epic-merge-actual.manifest"
diff ".epic-merge-pr-<N>.manifest" ".epic-merge-actual.manifest"
# Mismatch → STOP, restore: git switch -C "<head>" "backup/pr-<N>"

# Step 5: Force-push (--force-with-lease, NEVER --force)
git push --force-with-lease "origin" "<head>"

# Step 6: Update PR base so CI runs against correct diff
gh pr edit "<N>" --base "<epic>"

# Step 7: Wait for CI — delegate to /watch-ci with proper args
sha=$(git rev-parse "<head>")
# Skill: /watch-ci --sha "$sha" --branch "<head>" --timeout <--ci-timeout value (default 15)>
# Re-enter loop only on PASS verdict; FAIL → STOP and restore from backup

# Step 8: Squash merge
gh pr merge "<N>" --squash

# Step 9: Refresh epic
git fetch "origin" "<epic>"

Iteration Gate Design

Default: one bundled gate per iteration. Operator can opt into finer control with --per-step.

| Mode | Gate count per iteration | Gate moments | When to use | |------|--------------------------|--------------|-------------| | Bundled (default) | 1 | Before Step 2 (covers Steps 2-8) | Trusted chain, fast iteration | | --per-step | 3 | Before Step 3 (rebase), Step 5 (push), Step 7 (merge) | First-time use, untrusted diff, recovery from prior failure |

AskUserQuestion fields (bundled mode):

| Field | Value | |-------|-------| | question | "Proceed with PR #<N> ('<title>')? Will rebase onto epic, force-push, wait for CI, then squash-merge." | | options | Proceed, Per-step approval, Dry-run only, Abort and rollback | | description per option | Show diff stats (+X -Y across F files), backup tag SHA, expected unique-commit count from manifest |

AskUserQuestion fields (per-step mode):

| Step | question (short) | |------|------------------| | Before rebase | "Run: git rebase --onto origin/<epic> backup/pr-<prev> <head> ?" | | Before push | "Manifest verified (N commits). Force-push <head>?" | | Before merge | "CI passed for #<N>. Squash-merge into <epic>?" |

Compartilhar X LinkedIn