---
name: step-commit
description: Commit with EF Core migration validation
next_step: null
---

# Step: COMMIT - Migration-Aware Commits

## YOUR TASK:

Validate changes, check EF Core migrations, and create commit with proper message.

**ULTRA THINK about migration safety.**

---

## INPUT PARSING:

```
{commit_message} = optional message from args
{validate_only} = --validate flag (check without committing)
{dry_run} = --dry-run flag (simulate)
```

---

## EXECUTION SEQUENCE:

### 0. Pre-commit Guards

**0.0 Load Config:**
```bash
# Load GitFlow config (sets GF_* variables)
read_gitflow_config || { echo "❌ Run /gitflow init first."; exit 1; }
```

**0.1 Branch Protection:**
```bash
CURRENT=$(git rev-parse --abbrev-ref HEAD)

# BLOCK: main
[[ "$CURRENT" == "$GF_MAIN_BRANCH" ]] && {
  echo "⛔ COMMIT BLOCKED - $GF_MAIN_BRANCH is protected"
  echo "→ Use: /gitflow start hotfix <name>"
  STOP
}

# WARNING: develop
[[ "$CURRENT" == "$GF_DEVELOP_BRANCH" ]] && {
  echo "⚠️ Direct commits to $GF_DEVELOP_BRANCH are discouraged"
  # Offer RESCUE TO FEATURE
}
```

**0.2 Rescue to Feature (if on develop):**
```bash
git fetch origin --quiet
STAGED=$(git diff --cached --name-only | wc -l)
MODIFIED=$(git diff --name-only | wc -l)
UNTRACKED=$(git ls-files --others --exclude-standard | wc -l)
LOCAL_COMMITS=$(git rev-list --count origin/$GF_DEVELOP_BRANCH..HEAD 2>/dev/null || echo "0")

# If there are changes or commits, offer rescue
[ "$STAGED" -gt 0 ] || [ "$MODIFIED" -gt 0 ] || [ "$LOCAL_COMMITS" -gt 0 ] && {
  # Ask to rescue to feature branch
  RESCUE_BRANCH="feature/rescue-$(date +%Y%m%d-%H%M%S)"
  # Execute rescue flow...
}
```

**0.3 Check Sync Status:**
```bash
BEHIND=$(git rev-list --count HEAD..origin/$CURRENT 2>/dev/null || echo "0")
[ "$BEHIND" -gt 0 ] && echo "⚠️ Branch is $BEHIND commits behind → /gitflow sync"
```

### 1. Analyze Changes

```bash
STAGED=$(git diff --cached --name-only)
MODIFIED=$(git diff --name-only)
UNTRACKED=$(git ls-files --others --exclude-standard)

# Nothing to commit?
[ -z "$STAGED" ] && [ -z "$MODIFIED" ] && [ -z "$UNTRACKED" ] && {
  echo "✓ Nothing to commit"
  exit 0
}
```

### 2. Detect EF Core Migrations

```bash
# Find migration files in changes
MIGRATIONS=$(echo "$STAGED $MODIFIED $UNTRACKED" | tr ' ' '\n' | grep -E "Migrations/.*\.cs$")
HAS_MIGRATIONS=$([ -n "$MIGRATIONS" ] && echo "true" || echo "false")

if [ "$HAS_MIGRATIONS" = "true" ]; then
  # Check for the 3 required files
  MIGRATION_NAME=$(echo "$MIGRATIONS" | grep -v "Designer\|Snapshot" | head -1 | sed 's/.*\///' | sed 's/\.cs$//')

  HAS_MAIN=$(echo "$MIGRATIONS" | grep -c "${MIGRATION_NAME}.cs")
  HAS_DESIGNER=$(echo "$MIGRATIONS" | grep -c "${MIGRATION_NAME}.Designer.cs")
  HAS_SNAPSHOT=$(echo "$MIGRATIONS" | grep -c "ModelSnapshot.cs")

  [ "$HAS_MAIN" -eq 0 ] || [ "$HAS_DESIGNER" -eq 0 ] || [ "$HAS_SNAPSHOT" -eq 0 ] && {
    echo "❌ INCOMPLETE MIGRATION"
    echo "Required: Migration.cs + Designer.cs + ModelSnapshot.cs"
    echo "Found: Main=$HAS_MAIN, Designer=$HAS_DESIGNER, Snapshot=$HAS_SNAPSHOT"
    STOP
  }
fi
```

### 3. Check Destructive Operations (BLOCKING)

```bash
if [ "$HAS_MIGRATIONS" = "true" ]; then
  DESTRUCTIVE=$(grep -l "DropTable\|DropColumn\|DeleteData" $MIGRATIONS 2>/dev/null)

  [ -n "$DESTRUCTIVE" ] && {
    echo "⚠️ DESTRUCTIVE OPERATIONS DETECTED"
    grep -n "DropTable\|DropColumn\|DeleteData" $DESTRUCTIVE

    # Require explicit confirmation
    AskUserQuestion:
      header: "Confirm"
      question: "Destructive migration detected. Continue?"
      options:
        - label: "Yes, I understand the risks"
        - label: "No, let me review"

    # Log to audit trail
    echo "$(date) - DESTRUCTIVE: $DESTRUCTIVE - User: $USER" >> .claude/gitflow/logs/audit.log
  }
fi
```

### 4. Stage All Changes

```bash
# Stage all changes
git add -A

# Show what will be committed
echo "Files to commit:"
git diff --cached --stat
```

### 5. Generate Commit Message

**If no message provided:**

```bash
# Determine commit type from branch
BRANCH_TYPE=$(echo $CURRENT | cut -d'/' -f1)
case "$BRANCH_TYPE" in
  feature) PREFIX="feat" ;;
  hotfix)  PREFIX="fix" ;;
  release) PREFIX="chore" ;;
  *)       PREFIX="chore" ;;
esac

# Determine scope from branch name
SCOPE=$(echo $CURRENT | cut -d'/' -f2 | cut -d'-' -f1)

# If migrations present
[ "$HAS_MIGRATIONS" = "true" ] && PREFIX="db(migrations)"

# Generate message from diff analysis
SUMMARY=$(git diff --cached --stat | tail -1 | sed 's/^ *//')
```

**Message format:**
```
{type}({scope}): {summary - 50 chars max}

**CHANGE**
PROBLEM: {what was wrong}
SOLUTION: {what was done}
IMPACT: {benefit}

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
```

### 6. Create Commit

```bash
git commit -m "$(cat <<'EOF'
{generated_message}
EOF
)"

COMMIT_HASH=$(git rev-parse --short HEAD)
```

### 7. Push to Remote (ALWAYS ASK USER)

**MANDATORY: Always ask user before pushing commits:**

```yaml
AskUserQuestion:
  header: "Push"
  question: "Push commit to origin/$CURRENT?"
  options:
    - label: "Yes, push now"
      description: "git push origin $CURRENT"
    - label: "No, later"
      description: "Keep commit local for now"
```

**If user chooses "Yes":**
```bash
git push origin $CURRENT

# CRITICAL: Verify push and update tracking refs
git fetch origin $CURRENT:refs/remotes/origin/$CURRENT --force --quiet
LOCAL_SHA=$(git rev-parse HEAD)
REMOTE_SHA=$(git rev-parse origin/$CURRENT)
if [ "$LOCAL_SHA" = "$REMOTE_SHA" ]; then
  PUSHED="yes"
  echo "✅ Pushed and verified - origin/$CURRENT in sync"
else
  PUSHED="partial"
  echo "⚠️ Push may have failed - refs mismatch"
  echo "  Local:  $LOCAL_SHA"
  echo "  Remote: $REMOTE_SHA"
fi
```

**If user chooses "No":**
```bash
PUSHED="no"
echo "⏸️ Commit kept local. Push later with: git push origin $CURRENT"
```

### 8. Summary

```
╔══════════════════════════════════════════════════════════════════╗
║                    COMMIT CREATED                                 ║
╠══════════════════════════════════════════════════════════════════╣
║  Branch:  {CURRENT}                                               ║
║  Commit:  {COMMIT_HASH}                                           ║
║  Message: {first line of message}                                 ║
╠══════════════════════════════════════════════════════════════════╣
║  Files:   {N} changed                                             ║
║  EF Core: {✅ Valid | ⚠️ Destructive confirmed | N/A}             ║
║  Pushed:  {yes | no | pending}                                    ║
╠══════════════════════════════════════════════════════════════════╣
║  NEXT: /gitflow pr                                                ║
╚══════════════════════════════════════════════════════════════════╝
```

---

## ERROR HANDLING:

| Error | Solution |
|-------|----------|
| Incomplete migration | Add missing files |
| Build fails | `dotnet ef migrations remove` + fix |
| Pre-commit hook fails | Fix issues, stage again, NEW commit |
| Destructive operation | Confirm or modify migration |

---

## SUCCESS CRITERIA:

- All files staged
- Migrations validated (if present)
- Destructive operations confirmed
- Commit created with proper message
- **Push: ALWAYS ask user (never automatic)**

## NEXT STEP:

User continues coding or runs `/gitflow pr`.
